"""Check published exports and ordinary transaction bytes without running Core. python verify-results.py.txt /path/to/package This checks arithmetic, hashes, trace consistency and declared boundaries. It cannot independently attest execution or validate signatures, scripts or MWEB. """ from pathlib import Path from decimal import Decimal import csv,hashlib,json,re,sys folder=Path(sys.argv[1] if len(sys.argv)>1 else '.').resolve();checks=0 def check(value,label): global checks if not value:raise AssertionError(label) checks+=1 def read(name):return json.loads((folder/name).read_text(encoding='utf-8'),parse_float=Decimal) def tx(hex_string): raw=bytes.fromhex(hex_string);check(len(raw)>=10 and len(raw)<=100000,'Bounded ordinary transaction');pos=4;witness=raw[pos]==0 if witness:check(raw[pos+1]==1,'Only the ordinary SegWit flag is supported');pos+=2 def take(n): nonlocal pos check(0<=n<=len(raw)-pos,'Transaction field stays in range');value=raw[pos:pos+n];pos+=n;return value def varint(): marker=take(1)[0] if marker<253:return marker width={253:2,254:4,255:8}[marker];value=int.from_bytes(take(width),'little');check(value>={2:253,4:65536,8:4294967296}[width],'Canonical compact size');return value inputs_start=pos;count=varint();check(1<=count<=1000,'Input count bounded');inputs=[] for _ in range(count): prev=take(32)[::-1].hex();index=int.from_bytes(take(4),'little');take(varint());take(4);inputs.append({'txid':prev,'vout':index}) input_bytes=raw[inputs_start:pos];outputs_start=pos;count_out=varint();check(1<=count_out<=1000,'Output count bounded');outputs=[] for _ in range(count_out):outputs.append({'litoshis':int.from_bytes(take(8),'little'),'script':take(varint()).hex()}) output_bytes=raw[outputs_start:pos] if witness: for _ in range(count): stack=varint();check(stack<=1000,'Witness stack count bounded') for _ in range(stack):take(varint()) locktime=take(4);check(pos==len(raw),'No trailing transaction bytes');stripped=raw[:4]+input_bytes+output_bytes+locktime;weight=len(stripped)*4+len(raw)-len(stripped) return {'txid':hashlib.sha256(hashlib.sha256(stripped).digest()).digest()[::-1].hex(),'inputs':inputs,'outputs':outputs,'weight':weight,'vsize':(weight+3)//4} result=read('results.json');trace=read('rpc-trace.json');cleanup=read('cleanup.json') check(result['network']=='regtest','Network is explicitly regtest');check('v0.21.5.8' in result['daemon_version'] and 'v0.21.5.8' in result['cli_version'],'Pinned versions') check(result['assertions_passed']==len(result['assertions']) and all(a['passed'] for a in result['assertions']),'Experiment assertions agree') check(all(cleanup.values()),'Only disposable test process and files were cleaned') check(len(result['lifecycle'])==8 and all(e.get('exit_code',0)==0 for e in result['lifecycle']),'Four starts and clean stops') check('-connect=0' in result['common_options'] and '-listen=0' in result['common_options'] and '-dnsseed=0' in result['common_options'],'No automatic peers or listening') check(not any(call['method']=='sendrawtransaction' for call in trace),'No candidate was broadcast') check(not any(call['method'] in ['dumpprivkey','dumpwallet','walletpassphrase','importprivkey'] for call in trace),'No signing secrets exported or supplied') check(all('Authorization' not in call and 'cookie' not in call for call in trace),'No RPC authentication data exported') with (folder/'blocks.csv').open(newline='',encoding='utf-8') as file:blocks=list(csv.DictReader(file)) check(len(blocks)==result['block_count']==301,'The selected block count is exact');check([int(b['height']) for b in blocks]==list(range(1,302)),'Every height1 through301 occurs exactly once');check(all(int(b['transactions'])==1 for b in blocks),'Only coinbase transactions in generated blocks') reported_blocks={call['params'][0]:call['result'] for call in trace if call['method']=='getblock' and 'result' in call} for block in blocks: observed=reported_blocks[block['hash']];check(observed['height']==int(block['height']) and len(observed['tx'])==int(block['transactions']) and observed['time']==int(block['time']),'CSV block agrees with actual RPC export') check(len(result['checkpoints'])==6,'Six recorded checkpoints') for checkpoint in result['checkpoints']: check(checkpoint['height']==checkpoint['headers'],'Active and header heights agree');check(checkpoint['peer_count']==checkpoint['connections']==checkpoint['mempool']['size']==0,'No peers or accepted mempool transactions at checkpoint') derived=not checkpoint['initialblockdownload'] and checkpoint['tip_unix']>=checkpoint['host_unix']-10800 and checkpoint['height']>=checkpoint['headers']-1 check(checkpoint['currentness_inputs_match_pinned_gate']==derived,'Declared currentness inputs recomputed') check(checkpoint['tip_age_seconds']==checkpoint['host_unix']-checkpoint['tip_unix'],'Tip age is signed arithmetic, not elapsed mining time') check(len(checkpoint['estimates'])==6,'Targets and modes fully represented') check({(e['target'],e['mode']) for e in checkpoint['estimates']}=={(target,mode) for target in [2,6,12] for mode in ['ECONOMICAL','CONSERVATIVE']},'No missing target/mode combinations') for estimate in checkpoint['estimates']:check('feerate' not in estimate['result'] and estimate['result']['blocks']==0 and estimate['result']['errors']==['Insufficient data or no feerate found'],'An absent estimate is not a zero-priced estimate') check(result['checkpoints'][0]['initialblockdownload'] is True,'Fresh-node IBD boundary retained');check(all(not c['initialblockdownload'] and c['currentness_inputs_match_pinned_gate'] for c in result['checkpoints'][1:]),'Later checkpoints satisfy observed currentness inputs') check(len(result['candidates'])==5,'Five candidate attempts') expected_rates={'fallback-two-litoshi':Decimal(2),'fallback-below-required':Decimal(1),'relay-floor-above-fallback':Decimal(10)} for candidate in result['candidates']: if not candidate['created']: check(candidate['error']['code']==-4 and 'Fallbackfee is disabled' in candidate['error']['message'],'Exact disabled-fallback failure');continue parsed=tx(candidate['signed_hex']);check(parsed['txid']==candidate['txid'],'Signed candidate TXID reconstructed');check(parsed['weight']==candidate['weight'] and parsed['vsize']==candidate['vsize'],'Weight and virtual size reconstructed') previous={} for item in candidate['previous_outputs']: prev=tx(item['previous_hex']);check(prev['txid']==item['txid'],'Previous transaction TXID reconstructed');amount=prev['outputs'][item['vout']]['litoshis'];check(Decimal(str(item['value_ltc']))*100000000==amount,'Previous output value reconstructed');previous[(item['txid'],item['vout'])]=amount fee=sum(previous[(i['txid'],i['vout'])] for i in parsed['inputs'])-sum(o['litoshis'] for o in parsed['outputs']) check(fee==candidate['fee_litoshis']==Decimal(str(candidate['fee_ltc']))*100000000,'Fee reconstructed from exact previous and recipient/change outputs') check(fee==Decimal(str(candidate['funded']['fee']))*100000000==Decimal(str(candidate['acceptance']['fees']['base']))*100000000,'Funded fee and dry-run acceptance fee agree') check(Decimal(str(candidate['actual_litoshis_per_vbyte']))==Decimal(fee)/parsed['vsize'],'Actual fee rate recomputed') intended=expected_rates[candidate['case']];check(intended*parsed['vsize']<=fee<=intended*(parsed['vsize']+2),'The declared configured rate/floor accounts for conservative signed-size estimation') check(candidate['acceptance']['allowed'] is True and candidate['broadcast'] is False,'Accepted only in a local dry run') check('0.00001000'==result['candidates'][-1]['options']['mintxfee_ltc_per_kb'],'Explicit wallet minimum is part of the experiment') if (folder/'SHA256SUMS.txt').is_file(): for line in (folder/'SHA256SUMS.txt').read_text(encoding='utf-8').splitlines(): if not line.strip():continue digest,name=line.split(' ',1);check(re.fullmatch('[a-f0-9]{64}',digest) and re.fullmatch('[a-zA-Z0-9][a-zA-Z0-9._-]{0,150}',name),'Checksum entry is a bounded local file');check(hashlib.sha256((folder/name).read_bytes()).hexdigest()==digest,'Package checksum matches '+name) print(json.dumps({'state':'passed','checks':checks,'block_rows':len(blocks),'candidate_attempts':len(result['candidates']),'scope':'Export consistency and ordinary transparent transaction bytes/arithmetic only; not independent execution attestation or signature validation.'}))