Files
HaloKeymind/test/test_fleet_channel_runtime.py
T

1610 lines
90 KiB
Python

#!/usr/bin/env python3
"""Execute production fleet receiver/storage/crypto boundaries with host mocks.
FleetChannel.cpp and FleetCommand.cpp are compiled unchanged. Only hardware,
packet admission, clock and filesystem I/O are mocked. Ed25519 signatures and
SHA-256 use real implementations; AES encryption/MAC are supplied by OpenSSL.
The real RP2040 ContactFileTransaction and nRF52 AtomicFileWriter validate and
publish persisted state using their actual platform-specific implementations.
"""
from pathlib import Path
import os
import shutil
import subprocess
import tempfile
import unittest
from cpp_source import body
ROOT = Path(__file__).resolve().parents[1]
UTILS = r'''
#pragma once
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <cstdio>
#include <cassert>
#include <SHA256.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#define PUB_KEY_SIZE 32
#define PRV_KEY_SIZE 64
#define SIGNATURE_SIZE 64
#define PATH_HASH_SIZE 1
#define CIPHER_MAC_SIZE 2
#define CIPHER_BLOCK_SIZE 16
inline unsigned hash_calls = 0, decrypt_calls = 0, verify_calls = 0, last_jitter_max = 0;
inline int jitter_pick_boundary = 0;
namespace mesh {
class RNG { public: uint32_t nextInt(uint32_t low, uint32_t high) {
assert(low == 500 && (high == 10000 || high == 1500)); last_jitter_max=high;
return jitter_pick_boundary < 0 ? low : jitter_pick_boundary > 0 ? high : 1000;
}};
class Utils { public:
static void sha256(uint8_t* out, size_t count, const uint8_t* data, int length) {
++hash_calls; SHA256 hash; hash.update(data, length); hash.finalize(out, count);
}
static void toHex(char* out, const uint8_t* data, size_t length) {
for (size_t i = 0; i < length; ++i) sprintf(out + 2*i, "%02x", data[i]);
out[2*length] = 0;
}
static bool fromHex(uint8_t* out, int length, const char* hex) {
if (strlen(hex) != size_t(2*length)) return false;
for (int i=0;i<length;++i) {
auto digit=[](char c) { return c>='0'&&c<='9'?c-'0':c>='a'&&c<='f'?c-'a'+10:
c>='A'&&c<='F'?c-'A'+10:-1; };
int a=digit(hex[2*i]), b=digit(hex[2*i+1]); if(a<0||b<0) return false;
out[i]=uint8_t(a*16+b);
} return true;
}
static int encryptThenMAC(const uint8_t* key, uint8_t* out, const uint8_t* data, int size) {
uint8_t padded[256] = {}; const int rounded = (size+15)/16*16;
assert(rounded <= int(sizeof(padded))); memcpy(padded,data,size);
EVP_CIPHER_CTX* ctx=EVP_CIPHER_CTX_new(); assert(ctx);
assert(EVP_EncryptInit_ex(ctx,EVP_aes_128_ecb(),nullptr,key,nullptr));
EVP_CIPHER_CTX_set_padding(ctx,0); int produced=0, tail=0;
assert(EVP_EncryptUpdate(ctx,out+CIPHER_MAC_SIZE,&produced,padded,rounded));
assert(EVP_EncryptFinal_ex(ctx,out+CIPHER_MAC_SIZE+produced,&tail));
EVP_CIPHER_CTX_free(ctx); assert(produced+tail==rounded);
uint8_t mac[32]; unsigned mac_len=0;
assert(HMAC(EVP_sha256(),key,32,out+CIPHER_MAC_SIZE,rounded,mac,&mac_len));
memcpy(out,mac,CIPHER_MAC_SIZE); return rounded+CIPHER_MAC_SIZE;
}
static int MACThenDecrypt(const uint8_t* key,uint8_t* out,const uint8_t* data,int size) {
++decrypt_calls; int cipher_size=size-CIPHER_MAC_SIZE;
if(cipher_size<=0 || cipher_size%16) return 0;
uint8_t mac[32]; unsigned mac_len=0;
assert(HMAC(EVP_sha256(),key,32,data+CIPHER_MAC_SIZE,cipher_size,mac,&mac_len));
if(memcmp(mac,data,CIPHER_MAC_SIZE)) return 0;
EVP_CIPHER_CTX* ctx=EVP_CIPHER_CTX_new(); assert(ctx);
assert(EVP_DecryptInit_ex(ctx,EVP_aes_128_ecb(),nullptr,key,nullptr));
EVP_CIPHER_CTX_set_padding(ctx,0); int produced=0,tail=0;
assert(EVP_DecryptUpdate(ctx,out,&produced,data+CIPHER_MAC_SIZE,cipher_size));
assert(EVP_DecryptFinal_ex(ctx,out+produced,&tail)); EVP_CIPHER_CTX_free(ctx);
return produced+tail;
}
};
}
'''
FILESYSTEM = r'''
#pragma once
#include <map>
#include <vector>
#include <string>
#include <memory>
#include <cstring>
#include <algorithm>
namespace fs { class FS; }
class File {
fs::FS* owner_=nullptr; std::string path_; size_t cursor_=0; bool live_=false;
public:
File() = default;
File(fs::FS* owner,const char* path,bool live):owner_(owner),path_(path),live_(live){}
explicit File(fs::FS& owner):owner_(&owner){}
bool open(const char* path,int flags);
explicit operator bool() const { return live_; }
bool isDirectory() const { return false; }
size_t size() const;
size_t read(uint8_t* data,size_t length);
size_t write(const uint8_t* data,size_t length);
void flush() {}
void close() {live_=false;}
};
namespace fs {
class FS { public:
std::map<std::string,std::vector<uint8_t>> files;
bool fail_open=false, fail_read=false, fail_write=false, fail_rename=false;
unsigned reads=0,writes=0,renames=0;
std::string fail_rename_from;
void _lockFS() {} void _unlockFS() {} FS* _getFS(){return this;}
bool exists(const char* path) {return files.count(path)!=0;}
bool remove(const char* path) {return files.erase(path)!=0;}
bool mkdir(const char*) {return true;}
bool rename(const char* old,const char* next) {
++renames; if(fail_rename || fail_rename_from==old || !exists(old)) return false;
files[next]=std::move(files[old]);files.erase(old);return true;
}
File open(const char* path,const char* mode="r") {
if(!path||fail_open) return {};
if(mode[0]=='w') {files[path].clear();return File(this,path,true);}
return File(this,path,exists(path));
}
};
}
inline bool File::open(const char* path,int flags){
if(!owner_)return false;*this=owner_->open(path,flags==2?"w":"r");return live_;
}
inline size_t File::size() const {return live_?owner_->files[path_].size():0;}
inline size_t File::read(uint8_t* data,size_t length) {
++owner_->reads; if(!live_||owner_->fail_read) return 0;
auto& bytes=owner_->files[path_];size_t n=std::min(length,bytes.size()-cursor_);
memcpy(data,bytes.data()+cursor_,n);cursor_+=n;return n;
}
inline size_t File::write(const uint8_t* data,size_t length) {
++owner_->writes; if(!live_||owner_->fail_write) return 0;
auto& bytes=owner_->files[path_];bytes.insert(bytes.end(),data,data+length);return length;
}
'''
MESH = r'''
#pragma once
#include <Identity.h>
#include <FS.h>
#include <vector>
#include <functional>
#define MAX_PACKET_PAYLOAD 184
#define PAYLOAD_TYPE_GRP_TXT 5
#define PAYLOAD_TYPE_GRP_DATA 6
#define FLOOD_RETRY_POLICY_DENY 1
inline uint32_t fake_ms = 1000;
inline uint32_t millis() {return fake_ms;}
namespace mesh {
struct Packet {
uint8_t type=PAYLOAD_TYPE_GRP_DATA,payload[MAX_PACKET_PAYLOAD]={},flood_retry_policy=0;
uint16_t payload_len=0,transport_codes[2]={};
uint32_t radio_generation=0;uint8_t radio_profile=0,path_hash_size=1;
bool scoped=false,radio_reply=false,radio_bound=false;
uint8_t getPayloadType() const{return type;}
uint8_t getPathHashSize() const{return path_hash_size;}
bool hasTransportCodes() const{return scoped;}
};
struct GroupChannel {uint8_t hash[PATH_HASH_SIZE]={},secret[32]={};};
struct Clock {uint32_t now=1735689600UL+1000;
uint32_t getCurrentTime() const{return now;}};
class Mesh;
class Dispatcher {public:
class ReceiveProfileScope {Mesh& mesh_;uint8_t old_;uint32_t generation_;
public:ReceiveProfileScope(Mesh&,uint8_t,uint32_t);~ReceiveProfileScope();};
virtual void onSendFail(Packet*)=0;
};
class PacketManager {public:
std::vector<Packet*> outbound;
int getOutboundTotal() const{return int(outbound.size());}
Packet* getOutboundByIdx(int i){return outbound.at(i);}
Packet* removeOutboundByIdx(int i){Packet* p=outbound.at(i);outbound.erase(outbound.begin()+i);return p;}
};
class Mesh:public Dispatcher {public:
LocalIdentity self_id;Clock clock;RNG rng;PacketManager manager;PacketManager* _mgr=&manager;
std::function<void(Packet*,const Packet*)> copying;
std::function<void(Packet*)> failure;
std::vector<Packet*> allocated;
Packet* in_flight=nullptr;bool queue_ok=true,allocation_ok=true,fanout=false;
unsigned queued=0,cancelled=0,released=0;
uint8_t scoped_profile=0,ack_path_size=0;uint32_t scoped_generation=0,ack_delay=0;
bool ack_scoped=false;uint16_t ack_codes[2]={};
~Mesh(){for(Packet* p:allocated)delete p;}
Clock* getRTCClock(){return &clock;}RNG* getRNG(){return &rng;}
Packet* createGroupDatagram(uint8_t type,const GroupChannel& channel,const uint8_t* data,size_t length){
assert(length<=168);if(!allocation_ok)return nullptr;Packet* p=new Packet;allocated.push_back(p);p->type=type;
p->payload[0]=channel.hash[0];p->payload_len=1+Utils::encryptThenMAC(channel.secret,p->payload+1,data,int(length));return p;
}
bool sendFlood(Packet* p,uint32_t delay,uint8_t hashes){
ack_delay=delay;ack_path_size=hashes;
if(!queue_ok)return false;manager.outbound.push_back(p);++queued;
if(fanout){Packet* second=new Packet(*p);allocated.push_back(second);manager.outbound.push_back(second);
if(copying)copying(second,p);}
return true;
}
bool sendFlood(Packet* p,const uint16_t* codes,uint32_t delay,uint8_t hashes){
ack_scoped=true;memcpy(ack_codes,codes,sizeof(ack_codes));return sendFlood(p,delay,hashes);
}
const Packet* getOutboundInFlight()const{return in_flight;}
void cancelOutboundRadioRetry(const Packet* p){assert(p==in_flight);++cancelled;}
void onSendFail(Packet* p)override{if(failure)failure(p);}
void releasePacket(Packet*){++released;}
};
inline Dispatcher::ReceiveProfileScope::ReceiveProfileScope(Mesh& m,uint8_t profile,uint32_t generation)
:mesh_(m),old_(m.scoped_profile),generation_(m.scoped_generation){m.scoped_profile=profile;m.scoped_generation=generation;}
inline Dispatcher::ReceiveProfileScope::~ReceiveProfileScope(){mesh_.scoped_profile=old_;mesh_.scoped_generation=generation_;}
}
'''
REGION_MAP = r'''
#pragma once
#include <cstdint>
#include <cstring>
#include <cassert>
#define MAX_REGION_ENTRIES 32
#define REGION_DENY_FLOOD 0x01
struct RegionEntry {
uint16_t id=0,parent=0;uint8_t flags=0;char name[31]={};
bool isWildcard()const{return id==0;}
};
class RegionMap {public:
RegionEntry entries[MAX_REGION_ENTRIES]{},wildcard{};
unsigned count=0;uint16_t home_id=0,default_id=0;
RegionEntry* put(const char* name,uint16_t parent=0){
assert(count<MAX_REGION_ENTRIES&&strlen(name)<sizeof(entries[0].name));
RegionEntry& row=entries[count++];row.id=uint16_t(count);row.parent=parent;
strcpy(row.name,name);return &row;
}
int getCount()const{return int(count);}
const RegionEntry* getByIdx(int index)const{return &entries[index];}
RegionEntry* findById(uint16_t id){
if(!id)return &wildcard;for(unsigned i=0;i<count;++i)if(entries[i].id==id)return &entries[i];
return nullptr;
}
RegionEntry* getHomeRegion(){return home_id?findById(home_id):nullptr;}
RegionEntry* getDefaultRegion(){return default_id?findById(default_id):nullptr;}
RegionEntry& getWildcard(){return wildcard;}
void setHomeRegion(const RegionEntry* row){home_id=row?row->id:0;}
void setDefaultRegion(const RegionEntry* row){default_id=row?row->id:0;}
};
'''
PROFILE = r'''
#pragma once
namespace mesh {
class RadioProfileCLI {public:
bool mutation=false,command=false;uint32_t generation=0;
unsigned finishes=0,commits=0,rollbacks=0;
void beginReplyCommand(){command=true;}void endReplyCommand(){command=false;}
bool hasReplyMutation()const{return mutation;}
uint32_t replyMutationGeneration()const{return generation;}
void stage(){assert(command);mutation=true;++generation;}
bool finishReplyMutation(bool delivered){assert(mutation);mutation=false;++finishes;
if(delivered)++commits;else ++rollbacks;return true;}
};
}
'''
HARNESS = r'''
#include <helpers/FleetChannel.h>
#include <helpers/PersistentStoreFormat.h>
#include <cassert>
#include <string>
#include <algorithm>
#include <cstdio>
@TRANSPORT_METHODS@
inline unsigned identity_counter=0;
namespace mesh {
Identity::Identity(){memset(pub_key,0,sizeof(pub_key));}
LocalIdentity::LocalIdentity():Identity(){
memset(prv_key,0xAA+identity_counter++,32);EVP_PKEY* key=EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519,nullptr,prv_key,32);
assert(key);size_t length=sizeof(pub_key);assert(EVP_PKEY_get_raw_public_key(key,pub_key,&length)==1);
memcpy(prv_key+32,pub_key,32);EVP_PKEY_free(key);
}
void LocalIdentity::sign(uint8_t* sig,const uint8_t* data,int length)const{
EVP_PKEY* key=EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519,nullptr,prv_key,32);
EVP_MD_CTX* ctx=EVP_MD_CTX_new();assert(key&&ctx);assert(EVP_DigestSignInit(ctx,nullptr,nullptr,nullptr,key)==1);
size_t size=64;assert(EVP_DigestSign(ctx,sig,&size,data,length)==1);assert(size==64);
EVP_MD_CTX_free(ctx);EVP_PKEY_free(key);
}
bool Identity::verify(const uint8_t* sig,const uint8_t* data,int length)const{
++verify_calls;EVP_PKEY* key=EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519,nullptr,pub_key,32);
EVP_MD_CTX* ctx=EVP_MD_CTX_new();assert(key&&ctx);assert(EVP_DigestVerifyInit(ctx,nullptr,nullptr,nullptr,key)==1);
bool valid=EVP_DigestVerify(ctx,sig,64,data,length)==1;EVP_MD_CTX_free(ctx);EVP_PKEY_free(key);return valid;
}
}
using namespace mesh;
static constexpr const char* Path="/fleet_channel";
static constexpr const char* Key="21212121212121212121212121212121";
static uint8_t channel_key[32]={0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,
0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21};
static std::string config(FleetChannel& fleet,const std::string& command){
char reply[160]={};assert(fleet.handleConfig(command.c_str(),reply,sizeof(reply)));return reply;
}
static void enroll(FleetChannel& fleet,const LocalIdentity& publisher){
assert(config(fleet,std::string("set fleet.channel ")+Key).rfind("OK",0)==0);
char hex[65];Utils::toHex(hex,publisher.pub_key,32);
assert(config(fleet,std::string("set fleet.controller ")+hex).rfind("OK",0)==0);
}
static Packet raw(const uint8_t* bytes,size_t length){
Packet packet;
assert(1+CIPHER_MAC_SIZE+(length+15)/16*16<=sizeof(packet.payload));
Utils::sha256(packet.payload,1,channel_key,16);
packet.payload_len=1+Utils::encryptThenMAC(channel_key,packet.payload+1,bytes,int(length));
packet.radio_profile=1;packet.radio_generation=17;packet.path_hash_size=2;
return packet;
}
static Packet command(const LocalIdentity& publisher,uint32_t sequence,const char* text="set radio2 off",
const char* target="all"){
assert(strlen(text)<=FleetCommand::MaxPayloadLength-FleetCommand::HeaderSize-FleetCommand::SignatureSize);
uint8_t payload[184]={},destination[16];assert(FleetCommand::parseTarget(target,destination));
size_t length=FleetCommand::encode(publisher,channel_key,sequence,sequence+300,destination,text,payload+3,sizeof(payload)-3);
assert(length);payload[0]=uint8_t(FleetCommand::DataType);payload[1]=uint8_t(FleetCommand::DataType>>8);payload[2]=uint8_t(length);
return raw(payload,3+length);
}
static Packet targetsCommand(const LocalIdentity& publisher,uint32_t sequence,const std::string& list,
const char* text="get radio2"){
FleetCommand::Targets targets;assert(FleetCommand::parseTargets(list.c_str(),list.size(),targets));
uint8_t payload[184]={};
const size_t length=FleetCommand::encode(publisher,channel_key,sequence,sequence+300,targets,text,
payload+3,sizeof(payload)-3);
assert(length);payload[0]=uint8_t(FleetCommand::DataType);payload[1]=uint8_t(FleetCommand::DataType>>8);
payload[2]=uint8_t(length);
return raw(payload,3+length);
}
static Packet shortNewAll(const LocalIdentity& publisher,uint32_t sequence){
constexpr const char* text="get radio2";
uint8_t data[184]={},message[160]={};uint8_t* payload=data+3;
memcpy(payload,"FMC2",4);storage::writeLE32(payload+4,sequence);
storage::writeLE32(payload+8,sequence+300);payload[12]=0;
const size_t text_size=strlen(text);payload[13]=uint8_t(text_size);memcpy(payload+14,text,text_size);
constexpr const char* domain="MeshCoreFleet1";const size_t domain_size=strlen(domain);
memcpy(message,domain,domain_size);memcpy(message+domain_size,channel_key,16);
memcpy(message+domain_size+16,payload,14+text_size);
publisher.sign(payload+14+text_size,message,int(domain_size+16+14+text_size));
data[0]=uint8_t(FleetCommand::DataType);data[1]=uint8_t(FleetCommand::DataType>>8);
data[2]=uint8_t(14+text_size+64);return raw(data,3+data[2]);
}
static std::string publicHex(const Identity& identity){
char text[65];Utils::toHex(text,identity.pub_key,32);return text;
}
static Packet signedUnchecked(const LocalIdentity& publisher,uint32_t sequence,uint32_t expires,
const char* text){
uint8_t data[184]={},message[160]={};size_t text_size=strlen(text);
assert(text_size<=FleetCommand::MaxPayloadLength-FleetCommand::HeaderSize-FleetCommand::SignatureSize);
uint8_t* payload=data+3;memcpy(payload,"FMC1",4);
storage::writeLE32(payload+4,sequence);storage::writeLE32(payload+8,expires);
payload[28]=uint8_t(text_size);memcpy(payload+29,text,text_size);
constexpr const char* domain="MeshCoreFleet1";const size_t domain_size=strlen(domain);
memcpy(message,domain,domain_size);memcpy(message+domain_size,channel_key,16);
memcpy(message+domain_size+16,payload,29+text_size);
publisher.sign(payload+29+text_size,message,int(domain_size+16+29+text_size));
data[0]=uint8_t(FleetCommand::DataType);data[1]=uint8_t(FleetCommand::DataType>>8);
data[2]=uint8_t(29+text_size+64);return raw(data,3+data[2]);
}
static Packet compactUnchecked(const LocalIdentity& publisher,uint32_t sequence,const char* text){
uint8_t data[184]={},message[160]={};const size_t text_size=strlen(text);
// One byte over the valid limit still fits the cipher's final padded block.
assert(text_size<=FleetCommand::SinglePacketMaxCommandLength+1);
uint8_t* payload=data+3;memcpy(payload,"FMC2",4);
storage::writeLE32(payload+4,sequence);storage::writeLE32(payload+8,sequence+300);
payload[12]=0;payload[13]=uint8_t(text_size);memcpy(payload+14,text,text_size);
constexpr const char* domain="MeshCoreFleet1";const size_t domain_size=strlen(domain);
memcpy(message,domain,domain_size);memcpy(message+domain_size,channel_key,16);
memcpy(message+domain_size+16,payload,14+text_size);
publisher.sign(payload+14+text_size,message,int(domain_size+16+14+text_size));
data[0]=uint8_t(FleetCommand::DataType);data[1]=uint8_t(FleetCommand::DataType>>8);
data[2]=uint8_t(14+text_size+64);return raw(data,3+data[2]);
}
static void bind(FleetChannel& fleet,Mesh& mesh,RadioProfileCLI& profiles){
mesh.copying=[&](Packet* second,const Packet* first){fleet.copy(second,first);};
mesh.failure=[&](Packet* p){fleet.fail(p,profiles);};
}
static unsigned apply(FleetChannel& fleet,fs::FS& fs,Mesh& mesh,RadioProfileCLI& profiles,Packet& packet,
bool mutation=false,bool fail_handler=false,const TransportKey* scope=nullptr){
unsigned calls=0;fleet.receive(&packet,mesh,scope);
fleet.service(mesh,profiles,"test:node",[&](uint32_t seq,const char* text,char* reply){
++calls;assert(storage::readLE32(fs.files.at(Path).data()+56)==seq);
assert(mesh.scoped_profile==1&&mesh.scoped_generation==17);assert(profiles.command);
assert(!strcmp(text,"set radio2 off")||!strcmp(text,"get radio2.status")||!strcmp(text,"get radio2"));
if(mutation)profiles.stage();strcpy(reply,fail_handler?"Err - rejected":"OK");
});
assert(mesh.scoped_profile==0&&mesh.scoped_generation==0);assert(!profiles.command);return calls;
}
static void reseal(std::vector<uint8_t>& image){
storage::writeLE32(image.data()+60,storage::updateCRC32(0xffffffff,image.data(),60));
}
static std::string longRule(){
const std::string prefix="set flood.rule.1 type=grp_txt channel="+std::string(64,'1')
+" prefix=AABBCC,DDEEFF,112233 hops=6+ priority=255 suspend=tempradio mode=radio hashbytes=3 in=any";
const std::string action=" action=drop";
assert(prefix.size()+action.size()<=FleetCommand::MaxCommandLength);
return prefix+std::string(FleetCommand::MaxCommandLength-prefix.size()-action.size(),' ')+action;
}
static std::vector<uint8_t> envelope(const LocalIdentity& publisher,uint32_t sequence,
const std::string& text,const std::string& target="all"){
FleetCommand::Targets targets;assert(FleetCommand::parseTargets(target.c_str(),target.size(),targets));
std::vector<uint8_t> bytes(FleetCommand::MaxEnvelopeLength);
size_t length=FleetCommand::encode(publisher,channel_key,sequence,sequence+300,targets,
text.c_str(),bytes.data(),bytes.size());
assert(length);bytes.resize(length);return bytes;
}
static Packet part(const std::vector<uint8_t>& bytes,uint8_t index){
uint8_t data[168]={};size_t length=FleetCommand::fragment(bytes.data(),bytes.size(),index,
data+3,sizeof(data)-3);
assert(length&&length<=FleetCommand::MaxPayloadLength);data[0]=uint8_t(FleetCommand::DataType);
data[1]=uint8_t(FleetCommand::DataType>>8);data[2]=uint8_t(length);
Packet result=raw(data,3+length);assert(result.payload_len<=MAX_PACKET_PAYLOAD);return result;
}
template<typename Change> static Packet changePart(const Packet& packet,Change change){
uint8_t data[184]={};int length=Utils::MACThenDecrypt(channel_key,data,packet.payload+1,packet.payload_len-1);
assert(length>0);change(data+3);return raw(data,size_t(length));
}
struct FragmentFixture {
fs::FS disk;Mesh mesh;RadioProfileCLI profiles;FleetChannel fleet{&disk};LocalIdentity publisher;
std::string expected=longRule();unsigned calls=0;bool mutation=false;
FragmentFixture(){enroll(fleet,publisher);bind(fleet,mesh,profiles);}
uint32_t reserved()const{return storage::readLE32(disk.files.at(Path).data()+56);}
std::string stats(){return config(fleet,"get fleet.stats");}
unsigned feed(Packet& packet,const TransportKey* scope=nullptr,uint32_t advance_ms=1000){
fake_ms+=advance_ms;const unsigned before=calls;
const auto image=disk.files.at(Path);const unsigned writes=disk.writes,verifies=verify_calls;
fleet.receive(&packet,mesh,scope);
assert(disk.files.at(Path)==image&&disk.writes==writes&&verify_calls==verifies);
fleet.service(mesh,profiles,"long:rule",[&](uint32_t seq,const char* text,char* reply){
++calls;assert(reserved()==seq&&profiles.command);
assert(mesh.scoped_profile==packet.radio_profile&&mesh.scoped_generation==packet.radio_generation);
assert(strlen(text)==expected.size()&&!strcmp(text,expected.c_str()));
assert(text[expected.size()]==0);if(mutation)profiles.stage();strcpy(reply,"OK");
});
assert(!profiles.command&&mesh.scoped_profile==0&&mesh.scoped_generation==0);return calls-before;
}
void tick(uint32_t advance_ms){
fake_ms+=advance_ms;fleet.service(mesh,profiles,"node",[](uint32_t,const char*,char*){assert(false);});
}
void partial(){assert(calls==0&&reserved()==0&&mesh.queued==0&&!profiles.mutation);
assert(stats().find("accepted=0")!=std::string::npos&&stats().find("parts=1")!=std::string::npos);}
};
static int fragmentScenario(const std::string& scenario){
FragmentFixture f;const uint32_t seq=f.mesh.clock.now;
auto bytes=envelope(f.publisher,seq,f.expected);assert(bytes.size()==308&&f.expected.size()==230);
Packet a=part(bytes,0),b=part(bytes,1);assert(a.payload_len==179&&b.payload_len==179);
if(scenario=="fragments_orders"){
for(unsigned reverse=0;reverse<2;++reverse){
FragmentFixture x;auto encoded=envelope(x.publisher,seq,x.expected);
Packet first=part(encoded,reverse?1:0),second=part(encoded,reverse?0:1);
const auto old=x.disk.files.at(Path);const unsigned verifies=verify_calls,writes=x.disk.writes;
assert(x.feed(first)==0);x.partial();assert(x.disk.files.at(Path)==old&&x.disk.writes==writes);
assert(verify_calls==verifies&&x.stats().find("rejected=0")!=std::string::npos);
assert(x.feed(second)==1&&verify_calls==verifies+1&&x.reserved()==seq&&x.mesh.queued==1);
assert(x.stats().find("parts=0")!=std::string::npos&&last_jitter_max==10000);
assert(x.feed(first)==0&&x.feed(second)==0&&x.calls==1);
FleetChannel rebooted(&x.disk);unsigned calls=0;
for(Packet* packet:{&first,&second}){fake_ms+=1000;rebooted.receive(packet,x.mesh);
rebooted.service(x.mesh,x.profiles,"node",[&](uint32_t,const char*,char*){++calls;});}
assert(calls==0&&x.reserved()==seq);
}
}else if(scenario=="fragments_duplicates"){
const unsigned verifies=verify_calls,writes=f.disk.writes;
assert(f.feed(b)==0&&f.feed(b)==0&&f.feed(b)==0);f.partial();
assert(verify_calls==verifies&&f.disk.writes==writes&&f.stats().find("rejected=0")!=std::string::npos);
assert(f.feed(a)==1&&f.feed(a)==0&&f.feed(b)==0&&f.calls==1&&f.mesh.queued==1);
}else if(scenario=="fragments_minimum_size"){
f.expected="set flood.rule.1 05 c="+std::string(64,'1')+" d";
assert(f.expected.size()==88);auto small=envelope(f.publisher,seq,f.expected);
assert(small.size()==166);Packet first=part(small,0),second=part(small,1);
assert(first.payload_len==179&&second.payload_len==35);
assert(f.feed(second)==0);f.partial();assert(f.feed(first)==1&&f.reserved()==seq);
FleetCommand::Targets all;assert(FleetCommand::parseTargets("all",3,all));
uint8_t output[308];memset(output,0xA5,sizeof(output));
const std::string too_long=longRule()+" ";assert(too_long.size()==231);
assert(!FleetCommand::encode(f.publisher,channel_key,seq+1,seq+301,all,too_long.c_str(),output,sizeof(output)));
for(uint8_t byte:output)assert(byte==0xA5);
}else if(scenario=="fragments_expired_during_collection"){
assert(f.feed(a)==0);f.partial();f.mesh.clock.now+=301;
assert(f.feed(b)==0&&f.reserved()==0&&f.mesh.queued==0&&f.calls==0);
assert(f.stats().find("parts=0")!=std::string::npos);
auto retry=envelope(f.publisher,f.mesh.clock.now,f.expected);
Packet first=part(retry,0),second=part(retry,1);
assert(f.feed(first)==0&&f.feed(second)==1&&f.reserved()==f.mesh.clock.now);
}else if(scenario=="fragments_conflicts"){
assert(f.feed(a)==0);f.partial();
Packet changed=changePart(a,[](uint8_t* frame){frame[11+90]^=1;});
assert(f.feed(changed)==0);f.partial();
Packet length=changePart(a,[](uint8_t* frame){storage::writeLE16(frame+8,307);});
assert(f.feed(length)==0);f.partial();
Packet index=changePart(a,[](uint8_t* frame){frame[10]=2;});
assert(f.feed(index)==0);f.partial();
assert(f.feed(b)==1&&f.calls==1&&f.reserved()==seq);
}else if(scenario=="fragments_fresh_retry"){
assert(f.feed(a)==0);f.partial();auto newer=envelope(f.publisher,seq+1,f.expected);
Packet fresh_first=part(newer,1),fresh_second=part(newer,0);
assert(f.feed(fresh_first)==0&&f.stats().find("parts=2")!=std::string::npos);
// The unsigned newer metadata does not invalidate an authentic older
// envelope. Its signed sequence still passes the durable monotonic gate.
assert(f.feed(b)==1&&f.reserved()==seq&&f.stats().find("parts=0")!=std::string::npos);
assert(f.feed(fresh_second)==0&&f.feed(fresh_first)==1&&f.reserved()==seq+1);
assert(f.feed(a)==0&&f.feed(b)==0&&f.calls==2&&f.mesh.queued==2);
}else if(scenario=="fragments_future_poison"){
for(unsigned reverse=0;reverse<2;++reverse){
FragmentFixture x;auto encoded=envelope(x.publisher,seq,x.expected);
Packet first=part(encoded,reverse?1:0),second=part(encoded,reverse?0:1);
Packet poison=changePart(first,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+60);});
const auto saved=x.disk.files.at(Path);const unsigned verifies=verify_calls;
assert(x.feed(poison,nullptr,0)==0&&x.feed(first,nullptr,0)==0);
assert(x.stats().find("parts=2")!=std::string::npos&&x.disk.files.at(Path)==saved);
assert(x.feed(poison,nullptr,0)==0&&verify_calls==verifies);
assert(x.feed(second,nullptr,0)==1&&x.reserved()==seq&&verify_calls==verifies+1);
assert(x.stats().find("parts=0")!=std::string::npos&&x.feed(poison)==0);
assert(x.feed(first)==0&&x.feed(second)==0&&x.calls==1);
FleetChannel rebooted(&x.disk);unsigned calls=0;
for(Packet* packet:{&first,&second}){rebooted.receive(packet,x.mesh);
rebooted.service(x.mesh,x.profiles,"node",[&](uint32_t,const char*,char*){++calls;});}
assert(calls==0&&x.reserved()==seq);
}
}else if(scenario=="fragments_oldest_eviction"){
Packet poison_a=changePart(a,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+60);});
Packet poison_b=changePart(a,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+59);});
assert(f.feed(poison_a)==0&&f.feed(poison_b)==0);
assert(f.stats().find("parts=2")!=std::string::npos);
// A third transfer replaces the oldest slot despite its higher sequence.
assert(f.feed(a)==0&&f.feed(b)==1&&f.reserved()==seq&&f.calls==1);
assert(f.stats().find("parts=0")!=std::string::npos);
}else if(scenario=="fragments_independent_failure"){
Packet poison_a=changePart(a,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+60);});
Packet poison_b=changePart(b,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+60);});
assert(f.feed(a)==0&&f.feed(poison_a)==0&&f.feed(poison_b)==0);
f.partial();assert(f.feed(b)==1&&f.reserved()==seq&&f.calls==1);
}else if(scenario=="fragments_budget_partial"){
const unsigned verifies=verify_calls;
for(unsigned i=0;i<20;++i)assert(f.feed(a,nullptr,0)==0);
assert(verify_calls==verifies&&f.feed(b,nullptr,0)==1);
assert(verify_calls==verifies+1&&f.reserved()==seq);
}else if(scenario=="fragments_tamper"){
Packet mac=a;mac.payload[1]^=1;assert(f.feed(mac)==0&&f.stats().find("parts=0")!=std::string::npos);
assert(f.feed(a)==0);f.partial();const auto saved=f.disk.files.at(Path);
Packet signature=changePart(b,[](uint8_t* frame){frame[11+153]^=1;});
assert(f.feed(signature)==0&&f.calls==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
assert(f.stats().find("parts=0")!=std::string::npos);
assert(f.feed(b)==0&&f.feed(a)==1&&f.calls==1);
}else if(scenario=="fragments_wrong_signer"){
LocalIdentity stranger;auto forged=envelope(stranger,seq,f.expected);
Packet first=part(forged,0),second=part(forged,1);
assert(f.feed(first)==0);f.partial();assert(f.feed(second)==0&&f.reserved()==0&&f.mesh.queued==0);
assert(f.stats().find("parts=0")!=std::string::npos&&f.feed(a)==0&&f.feed(b)==1);
}else if(scenario=="fragments_fixed_timeout"){
assert(f.feed(a)==0);f.partial();assert(f.feed(a,nullptr,299999)==0);f.partial();
f.tick(1);assert(f.stats().find("parts=0")!=std::string::npos&&f.reserved()==0&&f.mesh.queued==0);
assert(f.feed(b)==0);f.partial();assert(f.feed(a)==1);
}else if(scenario=="fragments_timeout_wrap"){
fake_ms=UINT32_MAX-200000;assert(f.feed(b,nullptr,0)==0);f.partial();f.tick(299999);f.partial();
f.tick(1);assert(f.stats().find("parts=0")!=std::string::npos&&f.reserved()==0);
assert(f.feed(a)==0&&f.feed(b)==1);
}else if(scenario=="fragments_sequence_bounds"){
for(uint32_t invalid:{FleetCommand::MinEpoch-1,seq+61,seq-601}){
Packet changed=changePart(a,[&](uint8_t* frame){storage::writeLE32(frame+4,invalid);});
assert(f.feed(changed)==0&&f.stats().find("parts=0")!=std::string::npos&&f.reserved()==0);
}
Packet mismatch_a=changePart(a,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+1);});
Packet mismatch_b=changePart(b,[&](uint8_t* frame){storage::writeLE32(frame+4,seq+1);});
const unsigned verifies=verify_calls;assert(f.feed(mismatch_a)==0&&f.feed(mismatch_b)==0);
assert(f.reserved()==0&&verify_calls==verifies&&f.stats().find("parts=0")!=std::string::npos);
assert(f.feed(a)==0&&f.feed(b)==1);
}else if(scenario=="fragments_target"){
LocalIdentity stranger;f.expected="set flood.rule.1 any "+std::string(150,' ')+"drop";
auto other=envelope(f.publisher,seq,f.expected,publicHex(stranger));
Packet other_a=part(other,0),other_b=part(other,1);assert(f.feed(other_a)==0);f.partial();
assert(f.feed(other_b)==0&&f.reserved()==0&&f.mesh.queued==0);
auto mine=envelope(f.publisher,seq,f.expected,publicHex(f.mesh.self_id));
Packet mine_a=part(mine,0),mine_b=part(mine,1);
assert(f.feed(mine_b)==0&&f.feed(mine_a)==1&&last_jitter_max==1500);
}else if(scenario=="fragments_config_reboot"){
assert(f.feed(a)==0);f.partial();assert(config(f.fleet,std::string("set fleet.channel ")+Key).rfind("OK",0)==0);
assert(f.stats().find("parts=0")!=std::string::npos&&f.feed(b)==0);f.partial();
assert(config(f.fleet,"set fleet.controller "+publicHex(f.publisher)).rfind("OK",0)==0);
assert(f.stats().find("parts=0")!=std::string::npos&&f.feed(a)==0);f.partial();
FleetChannel rebooted(&f.disk);unsigned calls=0;fake_ms+=1000;rebooted.receive(&b,f.mesh);
rebooted.service(f.mesh,f.profiles,"node",[&](uint32_t,const char*,char*){++calls;});
assert(calls==0&&f.reserved()==0&&f.mesh.queued==0);
fake_ms+=1000;rebooted.receive(&a,f.mesh);
rebooted.service(f.mesh,f.profiles,"node",[&](uint32_t sequence,const char* text,char* reply){
++calls;assert(sequence==seq&&!strcmp(text,f.expected.c_str()));strcpy(reply,"OK");});
assert(calls==1&&f.reserved()==seq);
}else if(scenario=="fragments_reserve_failure"){
const auto saved=f.disk.files.at(Path);f.disk.fail_write=true;const unsigned writes=f.disk.writes;
assert(f.feed(b)==0);f.partial();assert(f.disk.writes==writes&&f.disk.files.at(Path)==saved);
assert(f.feed(a)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0&&f.calls==0);
assert(config(f.fleet,"get fleet.channel").find("store=error")!=std::string::npos);
f.disk.fail_write=false;assert(f.feed(a)==0&&f.feed(b)==0);
FleetChannel rebooted(&f.disk);unsigned calls=0;
for(Packet* packet:{&a,&b}){fake_ms+=1000;rebooted.receive(packet,f.mesh);
rebooted.service(f.mesh,f.profiles,"node",[&](uint32_t,const char*,char* reply){++calls;strcpy(reply,"OK");});}
assert(calls==1&&f.reserved()==seq);
}else if(scenario=="fragments_scoped_radio_ack"){
f.mutation=true;f.mesh.fanout=true;TransportKey scope;memset(scope.key,0x12,sizeof(scope.key));
a.radio_profile=0;a.radio_generation=9;b.radio_profile=1;b.radio_generation=27;b.path_hash_size=3;
b.scoped=true;b.transport_codes[0]=scope.calcTransportCode(&b);
assert(f.feed(a)==0);f.partial();assert(f.feed(b,&scope)==1&&f.fleet.waiting()&&f.profiles.mutation);
assert(f.mesh.manager.outbound.size()==2&&f.mesh.ack_scoped&&f.mesh.ack_path_size==3);
assert(f.mesh.ack_codes[0]==scope.calcTransportCode(f.mesh.manager.outbound[0]));
assert(f.mesh.ack_codes[0]!=b.transport_codes[0]&&last_jitter_max==10000);
f.fleet.complete(f.mesh.manager.removeOutboundByIdx(0),f.profiles);
assert(f.fleet.waiting()&&f.profiles.mutation&&f.profiles.commits==0);
f.fleet.fail(f.mesh.manager.removeOutboundByIdx(0),f.profiles);
assert(!f.fleet.waiting()&&!f.profiles.mutation&&f.profiles.commits==1);
assert(f.feed(a)==0&&f.feed(b,&scope)==0&&f.calls==1);
}else if(scenario=="fragments_unknown_scope"){
f.mutation=true;a.scoped=true;a.transport_codes[0]=123;
assert(f.feed(b)==0&&f.feed(a)==1&&f.mesh.queued==0&&f.profiles.rollbacks==1);
assert(f.reserved()==seq&&!f.fleet.waiting()&&!f.profiles.mutation);
}else if(scenario=="fragments_single_interrupt"){
assert(f.feed(a)==0);f.partial();f.expected="get radio2";
Packet single=targetsCommand(f.publisher,seq+1,"all",f.expected.c_str());
assert(f.feed(single)==1&&f.reserved()==seq+1&&f.stats().find("parts=0")!=std::string::npos);
assert(f.feed(b)==0&&f.feed(a)==0&&f.calls==1&&f.mesh.queued==1);
}else if(scenario=="fragments_malformed_frame"){
for(unsigned damage=0;damage<4;++damage){
Packet malformed=changePart(a,[&](uint8_t* frame){
if(damage==0)storage::writeLE16(frame+8,309);
else if(damage==1)storage::writeLE16(frame+8,165);
else if(damage==2)frame[10]=2;
else frame[-1]=uint8_t(FleetCommand::MaxPayloadLength-1);
});assert(f.feed(malformed)==0&&f.stats().find("parts=0")!=std::string::npos);
}
assert(f.reserved()==0&&f.mesh.queued==0&&f.feed(a)==0&&f.feed(b)==1);
}else assert(false);
printf("fleet runtime %s passed\n",scenario.c_str());return 0;
}
static Packet whole(const std::vector<uint8_t>& bytes){
assert(bytes.size()<=FleetCommand::MaxPayloadLength);uint8_t data[168]={};
data[0]=uint8_t(FleetCommand::DataType);data[1]=uint8_t(FleetCommand::DataType>>8);
data[2]=uint8_t(bytes.size());memcpy(data+3,bytes.data(),bytes.size());return raw(data,3+bytes.size());
}
static void resign(const LocalIdentity& publisher,std::vector<uint8_t>& bytes){
constexpr const char* domain="MeshCoreFleet1";const size_t domain_size=strlen(domain);
assert(bytes.size()>=FleetCommand::SignatureSize);
std::vector<uint8_t> message(domain_size+16+bytes.size()-FleetCommand::SignatureSize);
memcpy(message.data(),domain,domain_size);memcpy(message.data()+domain_size,channel_key,16);
memcpy(message.data()+domain_size+16,bytes.data(),bytes.size()-FleetCommand::SignatureSize);
publisher.sign(bytes.data()+bytes.size()-FleetCommand::SignatureSize,message.data(),int(message.size()));
}
struct LocationHooks :FleetReplyHooks {
bool valid=false;int32_t latitude=0,longitude=0;mutable unsigned reads=0;
bool getFleetLocation(int32_t& lat_e6,int32_t& lon_e6)const override {
++reads;if(!valid)return false;lat_e6=latitude;lon_e6=longitude;return true;
}
};
struct RegionFixture {
fs::FS disk;Mesh mesh;RadioProfileCLI profiles;RegionMap regions;LocationHooks location;
FleetChannel fleet{&disk,&location,&regions};LocalIdentity publisher;
std::string expected="get radio2";unsigned calls=0,issued=0;
RegionEntry* usa=nullptr;RegionEntry* sea=nullptr;RegionEntry* pdx=nullptr;RegionEntry* north=nullptr;
RegionFixture(){
usa=regions.put("#usa");sea=regions.put("#sea",usa->id);pdx=regions.put("#pdx",usa->id);
north=regions.put("#north",sea->id);regions.put("seaside");regions.put("$private",usa->id);
regions.setHomeRegion(north);regions.setDefaultRegion(pdx);
enroll(fleet,publisher);bind(fleet,mesh,profiles);
}
uint32_t reserved()const{return storage::readLE32(disk.files.at(Path).data()+56);}
unsigned feed(FleetChannel& receiver,Packet& packet,const TransportKey* scope=nullptr){
fake_ms+=1000;const unsigned before=calls,verifies=verify_calls,writes=disk.writes,reads=location.reads;
const auto saved=disk.files.at(Path);receiver.receive(&packet,mesh,scope);
assert(location.reads==reads);
assert(verify_calls==verifies&&disk.writes==writes&&disk.files.at(Path)==saved);
receiver.service(mesh,profiles,"region:node",[&](uint32_t sequence,const char* text,char* reply){
++calls;assert(reserved()==sequence&&!strcmp(text,expected.c_str()));
assert(mesh.scoped_profile==packet.radio_profile&&mesh.scoped_generation==packet.radio_generation);
assert(profiles.command);strcpy(reply,"OK");
});
assert(!profiles.command&&mesh.scoped_profile==0&&mesh.scoped_generation==0);return calls-before;
}
unsigned feed(Packet& packet,const TransportKey* scope=nullptr){return feed(fleet,packet,scope);}
unsigned send(const std::string& target){
Packet packet=targetsCommand(publisher,mesh.clock.now+issued++,target,expected.c_str());return feed(packet);
}
};
static int regionScenario(const std::string& scenario){
RegionFixture f;
if(scenario=="regions_target_delimiter"){
FleetCommand::Targets parsed;
const auto saved=f.disk.files.at(Path);
for(const char* invalid:{"sea,pdx","sea,,pdx","sea;",";sea","sea;;pdx",
"gps:47.6062:-122.3321:25","gps:47.6062,-122.3321:25,sea"}){
assert(!FleetCommand::parseTargets(invalid,strlen(invalid),parsed));
assert(parsed.count==0&&parsed.length==0);
}
assert(f.disk.files.at(Path)==saved&&f.calls==0&&f.mesh.queued==0);
assert(f.send("sea;pdx;home:sea")==1&&f.calls==1);
}else if(scenario=="regions_list_home"){
f.sea->flags=REGION_DENY_FLOOD;f.pdx->flags=REGION_DENY_FLOOD;
assert(f.send("sea")==1&&f.send("region:pdx")==1); // Configured membership ignores routing flags.
assert(f.send("home:sea")==1&&f.send("home:north")==1&&f.send("home:usa")==1);
assert(f.send("home:pdx")==0&&f.send("home:seaside")==0);
assert(f.send("SEA")==0&&f.send("region:se")==0&&f.send("home:NORTH")==0);
assert(f.send("#sea")==1&&f.send("region:#pdx")==1&&f.send("home:#sea")==1);
assert(last_jitter_max==10000); // Region requests may have multiple matching radios.
}else if(scenario=="regions_private_names"){
assert(f.send("region:$private")==1&&f.send("private")==0);
RegionEntry* private_row=f.regions.findById(6);f.regions.setHomeRegion(private_row);
assert(f.send("home:$private")==1&&f.send("home:private")==0&&f.send("home:usa")==1);
}else if(scenario=="regions_home_not_default_or_scope"){
f.regions.setHomeRegion(f.pdx);f.regions.setDefaultRegion(f.sea);
Packet packet=targetsCommand(f.publisher,f.mesh.clock.now,"home:sea");
TransportKey incoming;memset(incoming.key,0x12,sizeof(incoming.key));
packet.scoped=true;packet.transport_codes[0]=incoming.calcTransportCode(&packet);
const auto before=f.disk.files.at(Path);assert(f.feed(packet,&incoming)==0&&f.disk.files.at(Path)==before);
assert(f.send("home:pdx")==1); // Only home and ancestors qualify, not the default.
f.regions.setHomeRegion(nullptr);f.regions.setDefaultRegion(f.sea);
assert(f.send("home:sea")==0&&f.send("sea")==1);
f.regions.setHomeRegion(&f.regions.getWildcard());strcpy(f.regions.wildcard.name,"#sea");
assert(f.send("home:sea")==0);
f.regions.home_id=99;assert(f.send("home:sea")==0); // Missing home IDs fail closed.
}else if(scenario=="regions_wildcard_and_cycles"){
RegionEntry* wildcard=f.regions.put("wildcard");wildcard->id=0;
assert(f.send("region:wildcard")==0&&f.send("home:wildcard")==0);
f.north->parent=f.sea->id;f.sea->parent=f.north->id;
assert(f.send("home:pdx")==0); // Bounded parent walk cannot hang on a damaged cycle.
}else if(scenario=="regions_damaged_map_bounds"){
const unsigned count=f.regions.count;const auto saved=f.disk.files.at(Path);
f.regions.count=MAX_REGION_ENTRIES+1;
assert(f.send("sea")==0&&f.send("home:sea")==0&&f.disk.files.at(Path)==saved);
f.regions.count=UINT32_MAX;
assert(f.send("sea")==0&&f.send("home:sea")==0&&f.disk.files.at(Path)==saved);
f.regions.count=count;memset(f.sea->name,'G',sizeof(f.sea->name));
assert(f.send(std::string(30,'G'))==0); // An unterminated row cannot alias a truncated name.
f.regions.setHomeRegion(f.sea);assert(f.send("home:"+std::string(30,'G'))==0);
f.sea->parent=99;assert(f.send("home:usa")==0&&f.disk.files.at(Path)==saved);
}else if(scenario=="regions_missing_map"){
FleetChannel no_map(&f.disk);const auto before=f.disk.files.at(Path);
Packet list=targetsCommand(f.publisher,f.mesh.clock.now,"sea");
assert(f.feed(no_map,list)==0&&f.disk.files.at(Path)==before);
Packet home=targetsCommand(f.publisher,f.mesh.clock.now,"home:sea");
assert(f.feed(no_map,home)==0&&f.disk.files.at(Path)==before);
Packet individual=targetsCommand(f.publisher,f.mesh.clock.now,"sea;"+publicHex(f.mesh.self_id));
assert(f.feed(no_map,individual)==1&&f.reserved()==f.mesh.clock.now);
}else if(scenario=="regions_overlap_once"){
const std::string key=publicHex(f.mesh.self_id);
assert(f.send("sea;home:sea;"+key.substr(0,8)+";"+key)==1);
assert(f.calls==1&&f.mesh.queued==1&&last_jitter_max==10000);
assert(config(f.fleet,"get fleet.stats").find("accepted=1")!=std::string::npos);
LocalIdentity another;const auto before=f.disk.files.at(Path);
assert(f.send("home:pdx;"+publicHex(another))==0&&f.disk.files.at(Path)==before);
}else if(scenario=="regions_signature_tamper"){
auto good=envelope(f.publisher,f.mesh.clock.now,"get radio2","sea");
assert(!memcmp(good.data(),"FMC2",4)&&good[12]==1&&good[13]==0x20&&good[14]==3);
const auto saved=f.disk.files.at(Path);auto changed=good;changed[13]=0x21; // Also matches, but signature differs.
Packet bad_type=whole(changed);assert(f.feed(bad_type)==0&&f.disk.files.at(Path)==saved);
changed=good;changed[17]='x';Packet bad_name=whole(changed);
assert(f.feed(bad_name)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
Packet valid=whole(good);assert(f.feed(valid)==1&&f.calls==1);
}else if(scenario=="regions_malformed_after_match"){
auto good=envelope(f.publisher,f.mesh.clock.now,"get radio2","sea;pdx");
assert(good[12]==2&&good[13]==0x20&&good[18]==0x20&&good[19]==3);
const auto saved=f.disk.files.at(Path);
for(unsigned damage=0;damage<4;++damage){
auto broken=good;
if(damage==0)broken[18]=0x23; // Unknown type after an already-matching record.
else if(damage==1)broken[19]=0;
else if(damage==2)broken[19]=31;
else broken[20]=',';
resign(f.publisher,broken);Packet packet=whole(broken);
assert(f.feed(packet)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
}
Packet valid=whole(good);assert(f.feed(valid)==1&&f.calls==1);
}else if(scenario=="regions_durable_replay"){
Packet packet=targetsCommand(f.publisher,f.mesh.clock.now,"home:sea");
assert(f.feed(packet)==1);const auto persisted=f.disk.files.at(Path);
assert(persisted.size()==64&&!memcmp(persisted.data(),"FCS1",4));
assert(f.feed(packet)==0&&f.disk.files.at(Path)==persisted);
FleetChannel rebooted(&f.disk,nullptr,&f.regions);assert(f.feed(rebooted,packet)==0);
assert(f.disk.files.at(Path)==persisted&&f.calls==1&&f.mesh.queued==1);
}else if(scenario=="regions_fragments_membership_changes"){
f.expected="set flood.rule.1 any "+std::string(150,' ')+"drop";
auto bytes=envelope(f.publisher,f.mesh.clock.now,f.expected,"home:sea");
assert(bytes.size()>FleetCommand::MaxPayloadLength);Packet first=part(bytes,0),second=part(bytes,1);
const auto saved=f.disk.files.at(Path);unsigned verifies=verify_calls;
assert(f.feed(first)==0&&f.disk.files.at(Path)==saved&&verify_calls==verifies&&f.mesh.queued==0);
f.regions.setHomeRegion(f.pdx);
assert(f.feed(second)==0&&f.disk.files.at(Path)==saved&&f.calls==0&&f.mesh.queued==0);
f.regions.setHomeRegion(f.north); // Rejected nonmember command did not consume its sequence.
assert(f.feed(second)==0&&f.feed(first)==1&&f.reserved()==f.mesh.clock.now);
assert(f.feed(first)==0&&f.feed(second)==0&&f.calls==1);
}else if(scenario=="regions_fragments_tamper"){
f.expected="set flood.rule.1 any "+std::string(150,' ')+"drop";
auto bytes=envelope(f.publisher,f.mesh.clock.now,f.expected,"sea");
Packet first=part(bytes,0),second=part(bytes,1);
const auto saved=f.disk.files.at(Path);assert(f.feed(first)==0);
Packet tampered=changePart(second,[](uint8_t* frame){frame[11+1]^=1;});
assert(f.feed(tampered)==0&&f.calls==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
assert(f.feed(second)==0&&f.feed(first)==1&&f.calls==1);
FleetChannel rebooted(&f.disk,nullptr,&f.regions);
assert(f.feed(rebooted,first)==0&&f.feed(rebooted,second)==0&&f.calls==1);
}else assert(false);
printf("fleet runtime %s passed\n",scenario.c_str());return 0;
}
static constexpr const char* Seattle="gps:47.6062,-122.3321:25";
static int gpsScenario(const std::string& scenario){
RegionFixture f;f.location.valid=true;f.location.latitude=47606200;f.location.longitude=-122332100;
if(scenario=="gps_inside_outside"){
assert(f.send(Seattle)==1&&f.location.reads==1&&last_jitter_max==10000);
f.location.latitude=47700000;f.location.longitude=-122300000;assert(f.send(Seattle)==1);
f.location.latitude=45515200;f.location.longitude=-122678400;
const auto saved=f.disk.files.at(Path);assert(f.send(Seattle)==0&&f.disk.files.at(Path)==saved);
assert(f.send("gps:45.5152,-122.6784:1")==1);
}else if(scenario=="gps_unknown_and_bad_fixes"){
f.location.valid=false;f.location.latitude=0;f.location.longitude=0;
const auto saved=f.disk.files.at(Path);assert(f.send("gps:0,0:10")==0&&f.disk.files.at(Path)==saved);
f.location.valid=true;assert(f.send("gps:0,0:10")==0&&f.disk.files.at(Path)==saved);
f.location.valid=false;
f.location.latitude=47606200;f.location.longitude=-122332100;
assert(f.send(Seattle)==0); // A stale/invalid provider value is never substituted for a known fix.
f.location.valid=true;
for(const auto& coords:{std::pair<int32_t,int32_t>{90000001,0},{-90000001,0},
{0,180000001},{0,-180000001},{INT32_MIN,INT32_MAX}}){
f.location.latitude=coords.first;f.location.longitude=coords.second;
assert(f.send(Seattle)==0&&f.disk.files.at(Path)==saved);
}
FleetChannel no_provider(&f.disk,nullptr,&f.regions);Packet packet=targetsCommand(f.publisher,f.mesh.clock.now,Seattle);
assert(f.feed(no_provider,packet)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
}else if(scenario=="gps_boundaries_and_globe"){
assert(FleetCommand::withinRadius(0,0,1,0,8));
assert(!FleetCommand::withinRadius(0,0,1,0,10));
assert(FleetCommand::withinRadius(0,0,1000,0,8990));
assert(!FleetCommand::withinRadius(0,0,1000,0,9000));
assert(FleetCommand::withinRadius(90000000,0,1,90000000,180000000));
f.location.latitude=0;f.location.longitude=-179900000;
assert(f.send("gps:0,179.9:25")==1&&f.send("gps:0,179.9:22")==0);
f.location.latitude=89900000;f.location.longitude=180000000;
assert(f.send("gps:89.9,0:25")==1&&f.send("gps:89.9,0:22")==0);
f.location.latitude=90000000;f.location.longitude=180000000;
assert(f.send("gps:90,0:0.001")==1);
}else if(scenario=="gps_mixed_targets_once"){
const std::string key=publicHex(f.mesh.self_id);
assert(f.send(std::string(Seattle)+";sea;home:sea;"+key)==1);
assert(f.calls==1&&f.mesh.queued==1&&last_jitter_max==10000);
assert(f.send(std::string("gps:0,0:1;")+Seattle+";home:pdx")==1);
assert(f.send(key+";"+Seattle)==1); // Internal GPS comma remains distinct from target-list separators.
f.location.valid=false;assert(f.send(std::string(Seattle)+";sea")==1); // OR semantics retain valid region matches.
const auto saved=f.disk.files.at(Path);LocalIdentity stranger;
assert(f.send(std::string(Seattle)+";home:pdx;"+publicHex(stranger))==0&&f.disk.files.at(Path)==saved);
}else if(scenario=="gps_signed_metadata_tamper"){
auto good=envelope(f.publisher,f.mesh.clock.now,f.expected,Seattle);
assert(good[12]==1&&good[13]==FleetCommand::GeoRecordType);
const auto saved=f.disk.files.at(Path);
for(unsigned field:{14U,18U,22U,13U}){
auto altered=good;altered[field]^=1;Packet changed=whole(altered);
assert(f.feed(changed)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
}
Packet valid=whole(good);assert(f.feed(valid)==1&&f.calls==1);
}else if(scenario=="gps_malformed_after_matching_region"){
auto good=envelope(f.publisher,f.mesh.clock.now,f.expected,std::string("sea;")+Seattle);
assert(good[12]==2&&good[13]==0x20&&good[18]==FleetCommand::GeoRecordType);
const auto saved=f.disk.files.at(Path);
for(unsigned damage=0;damage<5;++damage){
auto broken=good;
if(damage==0)storage::writeLE32(broken.data()+19,90000001);
else if(damage==1)storage::writeLE32(broken.data()+23,180000001);
else if(damage==2)storage::writeLE32(broken.data()+27,0);
else if(damage==3)storage::writeLE32(broken.data()+27,FleetCommand::MaxRadiusMeters+1);
else broken[18]=0x23;
resign(f.publisher,broken);Packet packet=whole(broken);
assert(f.feed(packet)==0&&f.disk.files.at(Path)==saved&&f.mesh.queued==0);
}
Packet valid=whole(good);assert(f.feed(valid)==1&&f.calls==1);
}else if(scenario=="gps_durable_replay"){
Packet packet=targetsCommand(f.publisher,f.mesh.clock.now,Seattle);
assert(f.feed(packet)==1);const auto persisted=f.disk.files.at(Path);
assert(persisted.size()==64&&!memcmp(persisted.data(),"FCS1",4)&&f.reserved()==f.mesh.clock.now);
assert(f.feed(packet)==0&&f.disk.files.at(Path)==persisted);
FleetChannel rebooted(&f.disk,&f.location,&f.regions);
assert(f.feed(rebooted,packet)==0&&f.disk.files.at(Path)==persisted&&f.calls==1&&f.mesh.queued==1);
}else if(scenario=="gps_fragments_fix_changes"){
f.expected="set flood.rule.1 any "+std::string(150,' ')+"drop";
auto bytes=envelope(f.publisher,f.mesh.clock.now,f.expected,Seattle);
assert(bytes.size()>FleetCommand::MaxPayloadLength);Packet first=part(bytes,0),second=part(bytes,1);
const auto saved=f.disk.files.at(Path);const unsigned reads=f.location.reads;
assert(f.feed(first)==0&&f.disk.files.at(Path)==saved&&f.location.reads==reads&&f.mesh.queued==0);
f.location.latitude=45515200;f.location.longitude=-122678400;
assert(f.feed(second)==0&&f.disk.files.at(Path)==saved&&f.calls==0&&f.mesh.queued==0);
f.location.latitude=47606200;f.location.longitude=-122332100;
assert(f.feed(second)==0&&f.feed(first)==1&&f.reserved()==f.mesh.clock.now);
assert(f.feed(first)==0&&f.feed(second)==0&&f.calls==1);
}else if(scenario=="gps_fragments_invalid_final_fix"){
f.expected="set flood.rule.1 any "+std::string(150,' ')+"drop";
auto bytes=envelope(f.publisher,f.mesh.clock.now,f.expected,Seattle);Packet first=part(bytes,0),second=part(bytes,1);
const auto saved=f.disk.files.at(Path);assert(f.feed(second)==0);f.location.valid=false;
assert(f.feed(first)==0&&f.disk.files.at(Path)==saved&&f.calls==0&&f.mesh.queued==0);
f.location.valid=true;
Packet tampered=changePart(second,[](uint8_t* frame){frame[11+1]^=1;});
assert(f.feed(first)==0&&f.feed(tampered)==0&&f.calls==0&&f.disk.files.at(Path)==saved);
assert(f.feed(second)==0&&f.feed(first)==1&&f.calls==1);
FleetChannel rebooted(&f.disk,&f.location,&f.regions);
assert(f.feed(rebooted,first)==0&&f.feed(rebooted,second)==0&&f.calls==1);
}else assert(false);
printf("fleet runtime %s passed\n",scenario.c_str());return 0;
}
static int ackDelayScenario(){
for(int boundary:{-1,1}){
jitter_pick_boundary=boundary;
for(const std::string mode:{"all","radio","prefix","list","region","home","gps","individual"}){
RegionFixture f;f.location.valid=true;f.location.latitude=47606200;f.location.longitude=-122332100;
LocalIdentity other;std::string target="all";
if(mode=="prefix")target=publicHex(f.mesh.self_id).substr(0,8);
else if(mode=="list")target=publicHex(other)+";"+publicHex(f.mesh.self_id);
else if(mode=="region")target="sea";
else if(mode=="home")target="home:sea";
else if(mode=="gps")target=Seattle;
else if(mode=="individual")target=publicHex(f.mesh.self_id);
const bool mutation=mode=="radio";f.mesh.fanout=mutation;
const char* text=mutation?"set radio2 off":"get radio2";
Packet packet=targetsCommand(f.publisher,f.mesh.clock.now,target,text);unsigned calls=0;
fake_ms+=1000;f.fleet.receive(&packet,f.mesh);
f.fleet.service(f.mesh,f.profiles,"bounded-delay",[&](uint32_t sequence,const char* command,char* reply){
++calls;assert(sequence==f.reserved()&&!strcmp(command,text));
if(mutation)f.profiles.stage();strcpy(reply,"OK");
});
const uint32_t upper=mode=="individual"?1500:10000;
const uint32_t expected=boundary<0?500:upper;
assert(calls==1&&last_jitter_max==upper&&f.mesh.ack_delay==expected&&f.mesh.ack_delay<=10000);
assert(f.mesh.queued==1&&f.mesh.manager.outbound.size()==(mutation?2U:1U));
if(mutation){
assert(f.fleet.waiting()&&f.profiles.mutation&&f.profiles.commits==0);
f.fleet.complete(f.mesh.manager.removeOutboundByIdx(0),f.profiles);
assert(f.fleet.waiting()&&f.profiles.mutation&&f.profiles.commits==0);
f.fleet.complete(f.mesh.manager.removeOutboundByIdx(0),f.profiles);
assert(!f.fleet.waiting()&&!f.profiles.mutation&&f.profiles.commits==1);
}
}
}
jitter_pick_boundary=0;puts("fleet runtime ack_delay_ceiling passed");return 0;
}
int main(int argc,char** argv){
assert(argc==2);const std::string scenario=argv[1];
if(scenario=="ack_delay_ceiling")return ackDelayScenario();
if(scenario.rfind("gps_",0)==0)return gpsScenario(scenario);
if(scenario.rfind("regions_",0)==0)return regionScenario(scenario);
if(scenario.rfind("fragments_",0)==0)return fragmentScenario(scenario);
fs::FS fs;Mesh mesh;RadioProfileCLI profiles;
FleetChannel fleet(&fs);LocalIdentity publisher;bind(fleet,mesh,profiles);
if(scenario=="enrollment"){
Packet packet=command(publisher,mesh.clock.now);assert(apply(fleet,fs,mesh,profiles,packet)==0);
assert(config(fleet,"get fleet.channel").find("off,controller=unset,store=ok")!=std::string::npos);
assert(config(fleet,"set fleet.channel 00000000000000000000000000000000").rfind("Err",0)==0);
assert(config(fleet,"set fleet.channel 8b3387e9c5cdea6ac9e5edbaa115cd72").rfind("Err",0)==0);
assert(config(fleet,"set fleet.controller 0000000000000000000000000000000000000000000000000000000000000000").rfind("Err",0)==0);
assert(config(fleet,std::string("set fleet.channel ")+Key).rfind("OK",0)==0);
assert(apply(fleet,fs,mesh,profiles,packet)==0);enroll(fleet,publisher);
const auto& image=fs.files.at(Path);assert(image.size()==64);
assert(!memcmp(image.data()+24,publisher.pub_key,32));
const std::vector<uint8_t> private_seed(32,0xAB);
assert(std::search(image.begin(),image.end(),private_seed.begin(),private_seed.end())==image.end());
assert(apply(fleet,fs,mesh,profiles,packet)==1);
assert(config(fleet,"set fleet.controller off").rfind("OK",0)==0);
++mesh.clock.now;packet=command(publisher,mesh.clock.now);assert(apply(fleet,fs,mesh,profiles,packet)==0);
}else if(scenario=="deferred_single_mailbox"){
enroll(fleet,publisher);Packet a=command(publisher,mesh.clock.now),b=command(publisher,mesh.clock.now+1);
unsigned decrypt_before=decrypt_calls,verify_before=verify_calls,writes_before=fs.writes;
fleet.receive(&a,mesh);fleet.receive(&b,mesh);
assert(decrypt_calls==decrypt_before&&verify_calls==verify_before&&fs.writes==writes_before);
assert(config(fleet,"get fleet.stats").find("busy=1 pending=1")!=std::string::npos);
assert(config(fleet,"set fleet.channel off").rfind("Err",0)==0);
unsigned calls=0;fleet.service(mesh,profiles,"node",[&](uint32_t seq,const char*,char* reply){
++calls;assert(seq==mesh.clock.now);strcpy(reply,"OK");});assert(calls==1);
assert(config(fleet,"get fleet.stats").find("accepted=1")!=std::string::npos);
}else if(scenario=="durable_replay"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,packet,false,true)==1);
assert(apply(fleet,fs,mesh,profiles,packet)==0);FleetChannel rebooted(&fs);
assert(apply(rebooted,fs,mesh,profiles,packet)==0);
++mesh.clock.now;packet=command(publisher,mesh.clock.now);assert(apply(rebooted,fs,mesh,profiles,packet)==1);
}else if(scenario=="reserve_fault"){
enroll(fleet,publisher);const auto previous=fs.files.at(Path);fs.fail_write=true;
Packet packet=command(publisher,mesh.clock.now);assert(apply(fleet,fs,mesh,profiles,packet)==0);
assert(fs.files.at(Path)==previous);fs.fail_write=false;
assert(config(fleet,"get fleet.channel").find("store=error")!=std::string::npos);
assert(apply(fleet,fs,mesh,profiles,packet)==0);FleetChannel rebooted(&fs);
assert(apply(rebooted,fs,mesh,profiles,packet)==1);
}else if(scenario=="config_fault"){
enroll(fleet,publisher);const auto previous=fs.files.at(Path);fs.fail_write=true;
assert(config(fleet,"set fleet.channel off").rfind("Err",0)==0);
assert(fs.files.at(Path)==previous);fs.fail_write=false;
assert(config(fleet,"get fleet.channel").find("on,controller=set")!=std::string::npos);
FleetChannel rebooted(&fs);Packet packet=command(publisher,mesh.clock.now);
assert(apply(rebooted,fs,mesh,profiles,packet)==1);
}else if(scenario=="images"){
enroll(fleet,publisher);const auto original=fs.files.at(Path);
for(unsigned damage=0;damage<7;++damage){
fs.files[Path]=original;auto& image=fs.files[Path];
if(damage==0)image.resize(63);else if(damage==1)image.push_back(0);
else if(damage==2)image[0]='X';else if(damage==3)image[4]=2;
else if(damage==4)image[5]=1;else if(damage==5)image[60]^=1;
else memset(image.data()+8,0,16);
if(damage>=3&&damage!=5)reseal(image);
FleetChannel bad(&fs);assert(config(bad,"get fleet.channel").find("store=error")!=std::string::npos);
Packet packet=command(publisher,mesh.clock.now);assert(apply(bad,fs,mesh,profiles,packet)==0);
assert(config(bad,std::string("set fleet.channel ")+Key).rfind("Err",0)==0);
}
fs.files[Path]=original;fs.fail_read=true;FleetChannel unreadable(&fs);fs.fail_read=false;
assert(config(unreadable,"get fleet.channel").find("store=error")!=std::string::npos);
#if defined(RP2040_PLATFORM)
fs.files.erase(Path);fs.files[std::string(Path)+".bak"]=original;
FleetChannel recovered(&fs);assert(config(recovered,"get fleet.channel").find("store=ok")!=std::string::npos);
assert(fs.files[Path]==original);Packet packet=command(publisher,mesh.clock.now);
assert(apply(recovered,fs,mesh,profiles,packet)==1);
#endif
}else if(scenario=="ack_both_copies"){
enroll(fleet,publisher);mesh.fanout=true;Packet packet=command(publisher,mesh.clock.now);
TransportKey scope;memset(scope.key,0x12,sizeof(scope.key));
packet.scoped=true;packet.transport_codes[0]=scope.calcTransportCode(&packet);packet.transport_codes[1]=456;
assert(apply(fleet,fs,mesh,profiles,packet,true,false,&scope)==1);assert(fleet.waiting());
assert(mesh.manager.outbound.size()==2);assert(mesh.ack_scoped&&mesh.ack_codes[1]==0);
assert(mesh.ack_codes[0]==scope.calcTransportCode(mesh.manager.outbound[0]));
assert(mesh.ack_codes[0]!=packet.transport_codes[0]);
assert(mesh.ack_delay==1000&&mesh.ack_path_size==2&&last_jitter_max==10000);
assert(mesh.manager.outbound[0]->radio_reply&&!mesh.manager.outbound[0]->radio_bound);
assert(mesh.manager.outbound[0]->flood_retry_policy==FLOOD_RETRY_POLICY_DENY);
fleet.receive(&packet,mesh);assert(config(fleet,"get fleet.stats").find("busy=1")!=std::string::npos);
fleet.complete(mesh.manager.removeOutboundByIdx(0),profiles);assert(fleet.waiting());assert(profiles.commits==0);
fleet.fail(mesh.manager.removeOutboundByIdx(0),profiles);assert(!fleet.waiting());assert(profiles.commits==1&&profiles.rollbacks==0);
}else if(scenario=="ack_all_fail"){
enroll(fleet,publisher);mesh.fanout=true;Packet packet=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,packet,true)==1);
fleet.fail(mesh.manager.removeOutboundByIdx(0),profiles);assert(fleet.waiting()&&profiles.rollbacks==0);
fleet.fail(mesh.manager.removeOutboundByIdx(0),profiles);assert(!fleet.waiting());assert(profiles.rollbacks==1);
}else if(scenario=="ack_queue_or_allocation"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);mesh.queue_ok=false;
assert(apply(fleet,fs,mesh,profiles,packet,true)==1);assert(!fleet.waiting()&&profiles.rollbacks==1);
assert(apply(fleet,fs,mesh,profiles,packet,true)==0);mesh.queue_ok=true;mesh.allocation_ok=false;
++mesh.clock.now;packet=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,packet,true)==1);assert(profiles.rollbacks==2);
}else if(scenario=="ack_timeout"){
enroll(fleet,publisher);mesh.fanout=true;Packet packet=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,packet,true)==1);fake_ms+=300000;
fleet.service(mesh,profiles,"node",[](uint32_t,const char*,char*){assert(false);});
assert(mesh.manager.outbound.empty()&&mesh.released==2);assert(profiles.rollbacks==1&&!fleet.waiting());
++mesh.clock.now;packet=command(publisher,mesh.clock.now);assert(apply(fleet,fs,mesh,profiles,packet,true)==1);
mesh.in_flight=mesh.manager.removeOutboundByIdx(0);fake_ms+=300000;
fleet.service(mesh,profiles,"node",[](uint32_t,const char*,char*){assert(false);});
assert(mesh.cancelled==1&&fleet.waiting());fleet.fail(mesh.in_flight,profiles);mesh.in_flight=nullptr;
assert(!fleet.waiting()&&profiles.rollbacks==2);
}else if(scenario=="tamper_padding_target"){
enroll(fleet,publisher);Packet good=command(publisher,mesh.clock.now);
uint8_t data[184];int size=Utils::MACThenDecrypt(channel_key,data,good.payload+1,good.payload_len-1);
data[3+29]^=1;Packet forged=raw(data,size);assert(apply(fleet,fs,mesh,profiles,forged)==0);
size=Utils::MACThenDecrypt(channel_key,data,good.payload+1,good.payload_len-1);
data[size-1]=1;Packet padded=raw(data,size);assert(apply(fleet,fs,mesh,profiles,padded)==0);
Packet mac=good;mac.payload[1]^=1;assert(apply(fleet,fs,mesh,profiles,mac)==0);
char target[65];uint8_t other[32];memset(other,0x23,sizeof(other));Utils::toHex(target,other,sizeof(other));
Packet wrong=command(publisher,mesh.clock.now,"set radio2 off",target);assert(apply(fleet,fs,mesh,profiles,wrong)==0);
fake_ms+=1000;Utils::toHex(target,mesh.self_id.pub_key,32);
Packet mine=command(publisher,mesh.clock.now,"set radio2 off",target);assert(apply(fleet,fs,mesh,profiles,mine)==1);
assert(last_jitter_max==1500);
}else if(scenario=="verify_budget"){
enroll(fleet,publisher);LocalIdentity stranger;
Packet forged=command(stranger,mesh.clock.now);
const unsigned before=verify_calls;
for(unsigned i=0;i<20;++i)assert(apply(fleet,fs,mesh,profiles,forged)==0);
assert(verify_calls-before==4);fake_ms+=1000;
Packet valid=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,valid)==1);
}else if(scenario=="invalid_mac_cannot_starve"){
enroll(fleet,publisher);Packet valid=command(publisher,mesh.clock.now);
Packet garbage=valid;garbage.payload[1]^=1;
const unsigned before=verify_calls;
for(unsigned i=0;i<20;++i)assert(apply(fleet,fs,mesh,profiles,garbage)==0);
assert(verify_calls==before);
assert(apply(fleet,fs,mesh,profiles,valid)==1);
assert(verify_calls==before+1&&mesh.queued==1);
}else if(scenario=="replay_cannot_starve"){
enroll(fleet,publisher);const uint32_t seq=mesh.clock.now;
Packet previous=command(publisher,seq);
assert(apply(fleet,fs,mesh,profiles,previous)==1);
fake_ms+=1000;const unsigned before=verify_calls;
for(unsigned i=0;i<20;++i)assert(apply(fleet,fs,mesh,profiles,previous)==0);
assert(verify_calls==before);
Packet next=command(publisher,seq+1);
assert(apply(fleet,fs,mesh,profiles,next)==1&&verify_calls==before+1);
}else if(scenario=="wrong_target_cannot_starve"){
enroll(fleet,publisher);LocalIdentity other;
Packet not_mine=command(publisher,mesh.clock.now,"get radio2",publicHex(other).c_str());
const unsigned before=verify_calls;
for(unsigned i=0;i<20;++i)assert(apply(fleet,fs,mesh,profiles,not_mine)==0);
assert(verify_calls==before);
Packet valid=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,valid)==1&&verify_calls==before+1);
}else if(scenario=="primary_denied"){
assert(!FleetCommand::commandAllowed("set radio 915,500,5,5"));
assert(!FleetCommand::commandAllowed("set tempradio 915,500,5,5,10"));
assert(!FleetCommand::commandAllowed("set password pass"));
assert(!FleetCommand::commandAllowed("reboot"));
assert(!FleetCommand::commandAllowed("set fleet.controller off"));
}else if(scenario=="signed_forbidden"){
enroll(fleet,publisher);
for(const char* text:{"set radio 915,500,5,5","set password pass","reboot",
"set fleet.channel off","get prv.key","set flood.max 5;reboot"}){
fake_ms+=1000;Packet packet=signedUnchecked(publisher,mesh.clock.now,mesh.clock.now+300,text);
assert(apply(fleet,fs,mesh,profiles,packet)==0);
}
assert(storage::readLE32(fs.files[Path].data()+56)==0);assert(mesh.queued==0);
}else if(scenario=="clock_limits"){
enroll(fleet,publisher);const uint32_t now=mesh.clock.now;
const std::vector<std::pair<uint32_t,uint32_t>> invalid={{now-301,now-1},{now+61,now+100},
{FleetCommand::MinEpoch-1,now+100},{now,now+FleetCommand::MaxLifetime+1},{now,now-1}};
for(const auto& times:invalid){fake_ms+=1000;
Packet packet=signedUnchecked(publisher,times.first,times.second,"set radio2 off");
assert(apply(fleet,fs,mesh,profiles,packet)==0);
}
mesh.clock.now=0;fake_ms+=1000;Packet packet=command(publisher,now);
assert(apply(fleet,fs,mesh,profiles,packet)==0);mesh.clock.now=now;
fake_ms+=1000;packet=signedUnchecked(publisher,now+60,now+100,"set radio2 off");
assert(apply(fleet,fs,mesh,profiles,packet)==1);
}else if(scenario=="transaction_faults"){
for(unsigned fault=0;fault<4;++fault){
fs::FS disk;Mesh transport;RadioProfileCLI radio;FleetChannel receiver(&disk);enroll(receiver,publisher);
auto previous=disk.files.at(Path);Packet packet=command(publisher,transport.clock.now);
if(fault==0)disk.fail_open=true;
else if(fault==1)disk.fail_read=true;
else if(fault==2)disk.fail_rename=true;
else disk.fail_rename_from=std::string(Path)+".tmp";
assert(apply(receiver,disk,transport,radio,packet)==0);assert(disk.files.at(Path)==previous);
assert(config(receiver,"get fleet.channel").find("store=error")!=std::string::npos);
}
}else if(scenario=="backup_replay"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);
assert(apply(fleet,fs,mesh,profiles,packet)==1);const auto persisted=fs.files.at(Path);
#if defined(RP2040_PLATFORM)
fs.files.erase(Path);fs.files[std::string(Path)+".bak"]=persisted;
#else
// Atomic LittleFS replacement never has a live-name gap. Abandoned .tmp
// bytes are ignored; the committed live sequence remains authoritative.
fs.files[std::string(Path)+".tmp"]=persisted;
#endif
FleetChannel rebooted(&fs);assert(apply(rebooted,fs,mesh,profiles,packet)==0);
assert(storage::readLE32(fs.files.at(Path).data()+56)==mesh.clock.now);
}else if(scenario=="wrong_publisher"){
enroll(fleet,publisher);LocalIdentity stranger;
assert(memcmp(stranger.pub_key,publisher.pub_key,32));
Packet packet=command(stranger,mesh.clock.now);assert(apply(fleet,fs,mesh,profiles,packet)==0);
assert(verify_calls==1&&mesh.queued==0&&storage::readLE32(fs.files[Path].data()+56)==0);
}else if(scenario=="bounded_ack"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);
fleet.receive(&packet,mesh);fleet.service(mesh,profiles,"sixteen:character:very long name",
[&](uint32_t,const char*,char* reply){memset(reply,'x',159);reply[159]=0;});
assert(mesh.queued==1);uint8_t data[184]={};Packet* ack=mesh.manager.outbound[0];
assert(ack->payload_len<=MAX_PACKET_PAYLOAD);
assert(Utils::MACThenDecrypt(channel_key,data,ack->payload+1,ack->payload_len-1)==176);
assert(!strncmp((char*)data+5,"sixteen;characte",15));
}else if(scenario=="unknown_scope"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);packet.scoped=true;
packet.transport_codes[0]=123;assert(apply(fleet,fs,mesh,profiles,packet,true)==1);
assert(mesh.queued==0&&mesh.released==1&&profiles.rollbacks==1&&!fleet.waiting());
}else if(scenario=="rotated_scope"){
struct Region {bool wildcard=false;bool isWildcard()const{return wildcard;}};
struct Regions {TransportKey keys[2];int getTransportKeysFor(const Region&,TransportKey* out,int max){
assert(max==MAX_TKS_ENTRIES);memcpy(out,keys,sizeof(keys));return 2;}} regions;
memset(regions.keys[0].key,0x11,16);memset(regions.keys[1].key,0x22,16);
Region region;TransportKey captured{};Packet packet=command(publisher,mesh.clock.now);
packet.scoped=true;packet.transport_codes[0]=regions.keys[1].calcTransportCode(&packet);
assert(packet.transport_codes[0]!=regions.keys[0].calcTransportCode(&packet));
assert(captureFleetReplyScope(regions,&region,&packet,captured));
assert(!memcmp(captured.key,regions.keys[1].key,16));
enroll(fleet,publisher);assert(apply(fleet,fs,mesh,profiles,packet,true,false,&captured)==1);
assert(mesh.ack_codes[0]==regions.keys[1].calcTransportCode(mesh.manager.outbound[0]));
assert(mesh.ack_codes[0]!=packet.transport_codes[0]);
region.wildcard=true;assert(!captureFleetReplyScope(regions,&region,&packet,captured));
assert(!captureFleetReplyScope(regions,static_cast<Region*>(nullptr),&packet,captured));
region.wildcard=false;packet.transport_codes[0]^=1;
assert(!captureFleetReplyScope(regions,&region,&packet,captured));
}else if(scenario=="fmc2_short_all"){
enroll(fleet,publisher);Packet packet=shortNewAll(publisher,mesh.clock.now);
uint8_t plaintext[184]={};assert(Utils::MACThenDecrypt(channel_key,plaintext,packet.payload+1,packet.payload_len-1)>0);
assert(plaintext[2]<FleetCommand::HeaderSize+FleetCommand::SignatureSize);
assert(apply(fleet,fs,mesh,profiles,packet)==1);assert(last_jitter_max==10000);
assert(apply(fleet,fs,mesh,profiles,packet)==0);
}else if(scenario=="fmc2_encoded_all"){
enroll(fleet,publisher);Packet packet=targetsCommand(publisher,mesh.clock.now,"all");
uint8_t plaintext[184]={};
assert(Utils::MACThenDecrypt(channel_key,plaintext,packet.payload+1,packet.payload_len-1)>0);
assert(!memcmp(plaintext+3,"FMC2",4)&&plaintext[3+12]==0);
assert(plaintext[2]==FleetCommand::MinHeaderSize+strlen("get radio2")+FleetCommand::SignatureSize);
assert(plaintext[2]<FleetCommand::HeaderSize+FleetCommand::SignatureSize);
Packet legacy=command(publisher,mesh.clock.now,"get radio2");
assert(packet.payload_len+16==legacy.payload_len);
assert(apply(fleet,fs,mesh,profiles,packet)==1&&mesh.queued==1&&last_jitter_max==10000);
assert(apply(fleet,fs,mesh,profiles,packet)==0);
mesh.releasePacket(mesh.manager.removeOutboundByIdx(0));
++mesh.clock.now;fake_ms+=1000;mesh.fanout=true;
Packet mutation=targetsCommand(publisher,mesh.clock.now,"all","set radio2 off");
assert(apply(fleet,fs,mesh,profiles,mutation,true)==1);
assert(fleet.waiting()&&profiles.mutation&&last_jitter_max==10000);
fleet.complete(mesh.manager.removeOutboundByIdx(0),profiles);
assert(fleet.waiting()&&profiles.mutation&&profiles.commits==0);
fleet.complete(mesh.manager.removeOutboundByIdx(0),profiles);
assert(!fleet.waiting()&&!profiles.mutation&&profiles.commits==1);
assert(apply(fleet,fs,mesh,profiles,mutation)==0);
FleetChannel rebooted(&fs);
assert(apply(rebooted,fs,mesh,profiles,packet)==0&&apply(rebooted,fs,mesh,profiles,mutation)==0);
}else if(scenario=="fmc2_maximum_command"){
enroll(fleet,publisher);
const std::string secret(64,'1');
const std::string text="set flood.rule.1 5 c="+secret+" d";
const std::string oversized="set flood.rule.1 05 c="+secret+" d";
assert(text.size()==87&&text.size()==FleetCommand::SinglePacketMaxCommandLength);
assert(oversized.size()==88&&FleetCommand::commandAllowed(oversized.c_str()));
FleetCommand::Targets all;assert(FleetCommand::parseTargets("all",3,all));
uint8_t output[184];memset(output,0xA5,sizeof(output));
assert(FleetCommand::encode(publisher,channel_key,mesh.clock.now,mesh.clock.now+300,all,
oversized.c_str(),output,sizeof(output))==166);
assert(output[166]==0xA5);
Packet packet=targetsCommand(publisher,mesh.clock.now,"all",text.c_str());
uint8_t plaintext[184]={};
const int size=Utils::MACThenDecrypt(channel_key,plaintext,packet.payload+1,packet.payload_len-1);
assert(size==176&&packet.payload_len==179&&packet.payload_len<=MAX_PACKET_PAYLOAD);
assert(plaintext[2]==FleetCommand::MaxPayloadLength&&plaintext[3+13]==87);
assert(!memcmp(plaintext+3,"FMC2",4)&&plaintext[3+12]==0);
assert(!memcmp(plaintext+3+14,text.data(),text.size()));
for(unsigned i=3+plaintext[2];i<unsigned(size);++i)assert(plaintext[i]==0);
const auto previous=fs.files.at(Path);unsigned calls=0;
struct Capture {char text[88]{};char guard='!';} capture;
const auto dispatch=[&](FleetChannel& receiver,Packet& request){
const unsigned before=calls;receiver.receive(&request,mesh);
receiver.service(mesh,profiles,"long-rule",[&](uint32_t seq,const char* command,char* reply){
++calls;assert(storage::readLE32(fs.files.at(Path).data()+56)==seq);
assert(strlen(command)==text.size()&&!strcmp(command,text.c_str())&&command[86]=='d'&&command[87]==0);
memcpy(capture.text,command,text.size()+1);assert(capture.guard=='!');strcpy(reply,"OK");
});return calls-before;
};
plaintext[3+14+86]='s';Packet changed=raw(plaintext,size);
assert(dispatch(fleet,changed)==0&&fs.files.at(Path)==previous&&mesh.queued==0);
Packet too_long=compactUnchecked(publisher,mesh.clock.now,oversized.c_str());
assert(too_long.payload_len==179&&dispatch(fleet,too_long)==0&&fs.files.at(Path)==previous);
assert(dispatch(fleet,packet)==1&&last_jitter_max==10000&&mesh.queued==1);
assert(!strcmp(capture.text,text.c_str())&&capture.guard=='!');
assert(dispatch(fleet,packet)==0);FleetChannel rebooted(&fs);assert(dispatch(rebooted,packet)==0);
}else if(scenario=="fmc2_short_prefix"){
enroll(fleet,publisher);Packet packet=targetsCommand(publisher,mesh.clock.now,publicHex(mesh.self_id).substr(0,8));
uint8_t plaintext[184]={};assert(Utils::MACThenDecrypt(channel_key,plaintext,packet.payload+1,packet.payload_len-1)>0);
assert(plaintext[2]==FleetCommand::HeaderSize+FleetCommand::SignatureSize);
assert(!memcmp(plaintext+3,"FMC2",4));
assert(apply(fleet,fs,mesh,profiles,packet)==1);assert(last_jitter_max==10000);
FleetChannel rebooted(&fs);assert(apply(rebooted,fs,mesh,profiles,packet)==0);
}else if(scenario=="fmc2_overlap_matches_once"){
enroll(fleet,publisher);const std::string mine=publicHex(mesh.self_id);
Packet packet=targetsCommand(publisher,mesh.clock.now,mine.substr(0,8)+";"+mine);
assert(apply(fleet,fs,mesh,profiles,packet)==1);assert(mesh.queued==1&&last_jitter_max==10000);
assert(config(fleet,"get fleet.stats").find("accepted=1")!=std::string::npos);
}else if(scenario=="fmc2_nonmatching_no_reserve"){
enroll(fleet,publisher);const auto previous=fs.files.at(Path);LocalIdentity stranger;
char different_prefix[9];uint8_t changed[4];memcpy(changed,mesh.self_id.pub_key,4);changed[0]^=1;
Utils::toHex(different_prefix,changed,4);
Packet packet=targetsCommand(publisher,mesh.clock.now,std::string(different_prefix)+";"+publicHex(stranger));
assert(apply(fleet,fs,mesh,profiles,packet)==0);assert(mesh.queued==0&&fs.files.at(Path)==previous);
packet=targetsCommand(publisher,mesh.clock.now,publicHex(stranger)+";"+publicHex(mesh.self_id));
assert(apply(fleet,fs,mesh,profiles,packet)==1);assert(last_jitter_max==10000);
}else if(scenario=="fmc2_target_tamper"){
enroll(fleet,publisher);const auto previous=fs.files.at(Path);
Packet good=targetsCommand(publisher,mesh.clock.now,publicHex(mesh.self_id).substr(0,12));
uint8_t plaintext[184]={};const int length=Utils::MACThenDecrypt(channel_key,plaintext,good.payload+1,good.payload_len-1);
assert(length>0);plaintext[3+13]^=1;Packet changed=raw(plaintext,length);
assert(apply(fleet,fs,mesh,profiles,changed)==0);assert(fs.files.at(Path)==previous);
assert(apply(fleet,fs,mesh,profiles,good)==1);
}else if(scenario=="existing_mutation"){
enroll(fleet,publisher);Packet packet=command(publisher,mesh.clock.now);
profiles.mutation=true;assert(apply(fleet,fs,mesh,profiles,packet)==0);
assert(storage::readLE32(fs.files[Path].data()+56)==0);
profiles.mutation=false;assert(apply(fleet,fs,mesh,profiles,packet)==1);
}else assert(false);
printf("fleet runtime %s passed\n",argv[1]);
}
'''
class FleetChannelRuntimeTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
compiler = shutil.which("g++") or shutil.which("clang++")
if compiler is None:
raise unittest.SkipTest("host C++ compiler required")
cls.work = tempfile.TemporaryDirectory(prefix="meshcore-fleet-runtime-")
work = Path(cls.work.name)
(work / "helpers").mkdir()
for name, content in {"Utils.h": UTILS, "FS.h": FILESYSTEM, "Mesh.h": MESH,
"helpers/RadioProfileCLI.h": PROFILE,
"helpers/RegionMap.h": REGION_MAP,
"Packet.h": '#pragma once\n#include <Mesh.h>\n',
"Adafruit_LittleFS.h": '''
#pragma once
#include <FS.h>
using Adafruit_LittleFS = fs::FS;
namespace Adafruit_LittleFS_Namespace {}
#define FILE_O_READ 1
#define FILE_O_WRITE 2
#define LFS_ERR_NOENT -2
struct lfs_info {};
inline int lfs_stat(fs::FS* fs,const char* path,lfs_info*) {return fs->exists(path)?0:LFS_ERR_NOENT;}
''',
"InternalFileSystem.h": '#pragma once\n#include <FS.h>\ninline fs::FS InternalFS;\n',
"Arduino.h": '#pragma once\n#include <Mesh.h>\n'}.items():
(work / name).write_text(content)
fixture = work / "fixture.cpp"
transport_source = (ROOT / "src/helpers/TransportKeyStore.cpp").read_text()
methods = "\n".join(body(transport_source, signature) for signature in [
"uint16_t TransportKey::calcTransportCode(", "bool TransportKey::isNull("])
fixture.write_text(HARNESS.replace("@TRANSPORT_METHODS@", methods))
sha_header = ROOT / "test/mocks/SHA256.h"
(work / "SHA256.h").write_text(f'''
#pragma once
#define SHA256 SHA256Base
#include "{sha_header}"
#undef SHA256
class SHA256 : public SHA256Base {{ public:
void finalize(void* out,size_t length) {{ SHA256Base::finalize(static_cast<uint8_t*>(out),length); }}
void finalizeHMAC(const uint8_t* key,size_t size,void* out,size_t length) {{
SHA256Base::finalizeHMAC(key,size,static_cast<uint8_t*>(out),length); }}
}};
''')
cls.binaries = {}
sanitizer_flags = (["-fsanitize=address,undefined", "-fno-omit-frame-pointer", "-no-pie"]
if os.environ.get("MESHCORE_FLEET_SANITIZERS") == "1" else [])
for backend in ["RP2040", "NRF52"]:
binary = work / f"fleet-runtime-{backend.lower()}"
sources = [str(ROOT / "src/helpers/FleetChannel.cpp"),
str(ROOT / "src/helpers/FleetCommand.cpp")]
if backend == "NRF52":
sources.append(str(ROOT / "src/helpers/AtomicFileWriter.cpp"))
result = subprocess.run([
compiler, *sanitizer_flags, "-std=c++17", f"-D{backend}_PLATFORM",
"-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter",
"-Wno-misleading-indentation", "-Wno-sign-compare", "-O2", "-g",
"-I", str(work), "-isystem", str(ROOT / "test/mocks"),
"-I", str(ROOT / "src"), str(fixture), *sources,
"-lcrypto", "-o", str(binary),
], capture_output=True, text=True, timeout=60)
if result.returncode:
cls.work.cleanup()
raise AssertionError(f"{backend}:\n" + result.stdout + result.stderr)
cls.binaries[backend] = binary
@classmethod
def tearDownClass(cls):
cls.work.cleanup()
def scenario(self, name):
for backend, binary in self.binaries.items():
with self.subTest(backend=backend):
result = subprocess.run([str(binary), name], capture_output=True, text=True, timeout=10)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_gps_center_radius_targets_inside_seattle_and_excludes_portland(self):
self.scenario("gps_inside_outside")
def test_gps_unknown_invalid_out_of_range_and_absent_location_providers_fail_closed(self):
self.scenario("gps_unknown_and_bad_fixes")
def test_gps_small_distance_boundary_antimeridian_and_poles_use_spherical_distance(self):
self.scenario("gps_boundaries_and_globe")
def test_gps_region_home_and_node_target_matches_use_or_semantics_and_execute_once(self):
self.scenario("gps_mixed_targets_once")
def test_gps_latitude_longitude_radius_and_record_type_are_signed(self):
self.scenario("gps_signed_metadata_tamper")
def test_signed_malformed_gps_record_after_matching_region_cannot_authorize_execution(self):
self.scenario("gps_malformed_after_matching_region")
def test_gps_command_replay_survives_reboot_with_the_existing_64_byte_storage_format(self):
self.scenario("gps_durable_replay")
def test_two_packet_gps_target_uses_location_at_completion_before_reserving_sequence(self):
self.scenario("gps_fragments_fix_changes")
def test_two_packet_gps_rejects_invalid_final_fix_tampering_and_reboot_replays(self):
self.scenario("gps_fragments_invalid_final_fix")
def test_every_group_ack_uses_at_most_ten_seconds_and_single_identity_retains_1500ms(self):
self.scenario("ack_delay_ceiling")
def test_semicolon_is_the_only_target_separator_with_comma_reserved_for_gps_coordinates(self):
self.scenario("regions_target_delimiter")
def test_existing_configured_regions_and_home_ancestors_are_distinct_exact_targets(self):
self.scenario("regions_list_home")
def test_private_region_dollar_identity_is_preserved_in_list_and_home_targets(self):
self.scenario("regions_private_names")
def test_default_received_scope_and_missing_or_wildcard_home_never_grant_membership(self):
self.scenario("regions_home_not_default_or_scope")
def test_wildcard_rows_and_damaged_parent_cycles_fail_closed_without_hanging(self):
self.scenario("regions_wildcard_and_cycles")
def test_invalid_map_counts_unterminated_names_and_missing_parent_ids_fail_closed(self):
self.scenario("regions_damaged_map_bounds")
def test_receiver_without_region_map_refuses_regions_but_can_still_match_node_keys(self):
self.scenario("regions_missing_map")
def test_mixed_list_home_and_node_targets_execute_once_with_nonmembers_unreserved(self):
self.scenario("regions_overlap_once")
def test_region_name_and_mode_are_bound_to_the_real_publisher_signature(self):
self.scenario("regions_signature_tamper")
def test_signed_malformed_trailing_region_record_rejects_despite_prior_matching_region(self):
self.scenario("regions_malformed_after_match")
def test_region_target_replay_survives_reboot_without_changing_existing_storage_format(self):
self.scenario("regions_durable_replay")
def test_region_membership_is_rechecked_when_second_fragment_arrives_before_reservation(self):
self.scenario("regions_fragments_membership_changes")
def test_region_fragment_tampering_and_reboot_replay_never_repeat_execution(self):
self.scenario("regions_fragments_tamper")
def test_private_channel_and_public_only_publisher_enrollment(self):
self.scenario("enrollment")
def test_receive_defers_crypto_writes_and_accepts_one_pending_command(self):
self.scenario("deferred_single_mailbox")
def test_sequence_reserved_before_handler_including_error_survives_reboot(self):
self.scenario("durable_replay")
def test_failed_sequence_save_grants_no_control_and_disables_receiver(self):
self.scenario("reserve_fault")
def test_failed_configuration_write_preserves_durable_enrollment(self):
self.scenario("config_fault")
def test_corrupt_short_long_unreadable_images_fail_closed_and_backup_recovers(self):
self.scenario("images")
def test_ack_waits_for_both_profile_copies_with_one_success_and_retains_rx_scope(self):
self.scenario("ack_both_copies")
def test_both_ack_transmissions_failing_roll_back_radio_mutation(self):
self.scenario("ack_all_fail")
def test_ack_allocation_queue_failure_rolls_back_but_keeps_reserved_sequence(self):
self.scenario("ack_queue_or_allocation")
def test_ack_timeout_cancels_queued_and_inflight_packets_before_rollback(self):
self.scenario("ack_timeout")
def test_real_signature_mac_padding_and_full_identity_target_checks(self):
self.scenario("tamper_padding_target")
def test_forged_mac_authenticated_packets_have_bounded_signature_verification_cost(self):
self.scenario("verify_budget")
def test_invalid_mac_packets_cannot_exhaust_the_signature_verification_budget(self):
self.scenario("invalid_mac_cannot_starve")
def test_recorded_used_command_replays_cannot_spend_signature_budget_before_next_command(self):
self.scenario("replay_cannot_starve")
def test_recorded_commands_for_another_target_cannot_spend_signature_budget(self):
self.scenario("wrong_target_cannot_starve")
def test_fleet_permission_never_allows_primary_radio_or_administrator_changes(self):
self.scenario("primary_denied")
def test_publisher_signed_commands_still_cannot_escape_fleet_capability(self):
self.scenario("signed_forbidden")
def test_expiry_future_clock_bounds_and_invalid_rtc_prevent_dispatch(self):
self.scenario("clock_limits")
def test_open_read_rename_and_publish_faults_preserve_state_and_fail_closed(self):
self.scenario("transaction_faults")
def test_recovered_backup_preserves_durable_anti_replay_sequence(self):
self.scenario("backup_replay")
def test_another_valid_ed25519_identity_cannot_authorize_fleet_commands(self):
self.scenario("wrong_publisher")
def test_long_cli_reply_and_sender_name_fit_one_encrypted_group_packet(self):
self.scenario("bounded_ack")
def test_unknown_scoped_route_refuses_ack_and_rolls_back_radio_mutation(self):
self.scenario("unknown_scope")
def test_scope_selects_matching_rotated_region_key_and_authenticates_new_ack_payload(self):
self.scenario("rotated_scope")
def test_new_short_broadcast_envelope_dispatches_and_preserves_replay_protection(self):
self.scenario("fmc2_short_all")
def test_actual_zero_target_sender_reaches_receiver_and_keeps_ack_and_reboot_replay_safety(self):
self.scenario("fmc2_encoded_all")
def test_single_packet_87_byte_body_is_intact_signed_and_88_bytes_require_fragmentation(self):
self.scenario("fmc2_maximum_command")
def test_new_public_key_prefix_envelope_dispatches_and_staggers_possible_multiple_acks(self):
self.scenario("fmc2_short_prefix")
def test_overlapping_prefix_and_full_key_matches_execute_once(self):
self.scenario("fmc2_overlap_matches_once")
def test_nonmatching_target_list_never_reserves_sequence_and_matching_list_can_reuse_it(self):
self.scenario("fmc2_nonmatching_no_reserve")
def test_modified_new_target_header_never_dispatches_without_valid_signature(self):
self.scenario("fmc2_target_tamper")
def test_existing_remote_radio_mutation_blocks_fleet_without_reserving_sequence(self):
self.scenario("existing_mutation")
def test_two_packets_deliver_exact_230_byte_command_in_either_order_once_across_reboot(self):
self.scenario("fragments_orders")
def test_identical_fragments_never_repeat_execution_or_reserve_partial_requests(self):
self.scenario("fragments_duplicates")
def test_smallest_fragmented_command_uses_a_short_second_packet_and_231_bytes_are_rejected(self):
self.scenario("fragments_minimum_size")
def test_command_expiring_between_parts_never_dispatches_and_fresh_retry_can_succeed(self):
self.scenario("fragments_expired_during_collection")
def test_conflicting_parts_cannot_destroy_a_valid_incomplete_transfer(self):
self.scenario("fragments_conflicts")
def test_unsigned_newer_fragment_cannot_invalidate_an_older_authenticated_envelope(self):
self.scenario("fragments_fresh_retry")
def test_forged_future_sequence_interleaved_with_both_legitimate_fragment_orders_cannot_lock_out(self):
self.scenario("fragments_future_poison")
def test_third_transfer_evicts_oldest_slot_instead_of_highest_unsigned_sequence(self):
self.scenario("fragments_oldest_eviction")
def test_failed_complete_poisoned_assembly_preserves_the_other_legitimate_slot(self):
self.scenario("fragments_independent_failure")
def test_duplicate_partial_fragments_cannot_spend_signature_verification_budget(self):
self.scenario("fragments_budget_partial")
def test_mac_and_last_signature_byte_tampering_never_authorize_partial_commands(self):
self.scenario("fragments_tamper")
def test_fragmented_request_from_another_real_signing_identity_is_rejected(self):
self.scenario("fragments_wrong_signer")
def test_fragment_deadline_is_fixed_despite_duplicates_and_missing_part(self):
self.scenario("fragments_fixed_timeout")
def test_fragment_deadline_handles_millisecond_timer_wrap(self):
self.scenario("fragments_timeout_wrap")
def test_fragment_sequence_plausibility_and_signed_sequence_match_precede_reservation(self):
self.scenario("fragments_sequence_bounds")
def test_fragmented_nonmatching_target_preserves_sequence_for_matching_retry(self):
self.scenario("fragments_target")
def test_channel_controller_changes_and_reboot_clear_partial_transfers(self):
self.scenario("fragments_config_reboot")
def test_fragment_completion_storage_failure_has_no_dispatch_or_reply(self):
self.scenario("fragments_reserve_failure")
def test_final_fragment_scope_and_radio_profile_feed_both_ack_copy_commit_barrier(self):
self.scenario("fragments_scoped_radio_ack")
def test_fragmented_radio_mutation_with_unknown_final_scope_rolls_back(self):
self.scenario("fragments_unknown_scope")
def test_new_valid_single_packet_can_interrupt_an_incomplete_two_packet_transfer(self):
self.scenario("fragments_single_interrupt")
def test_malformed_fragment_size_index_and_padding_never_allocate_partial_intent(self):
self.scenario("fragments_malformed_frame")
if __name__ == "__main__":
unittest.main()