"""Independently decode the published transparent regtest transactions. Usage: python verify-evidence.py results.json Uses no node and no network. Checks byte counts, weight, vsize, IDs and conservation. It does not independently verify ECDSA signatures or prove the node-state transcript. """ import hashlib,json,math,pathlib,sys def digest(raw):return hashlib.sha256(hashlib.sha256(raw).digest()).digest()[::-1].hex() def decode(raw): p=0 def take(n): nonlocal p if p+n>len(raw):raise ValueError('Truncated transaction') result=raw[p:p+n];p+=n;return result def compact(): first=take(1)[0] return first if first<253 else int.from_bytes(take({253:2,254:4,255:8}[first]),'little') version=take(4);witness=raw[p:p+2]==b'\x00\x01' if witness:take(2) body_start=p;count=compact();inputs=[] for _ in range(count): txid=take(32)[::-1].hex();vout=int.from_bytes(take(4),'little');take(compact());take(4);inputs.append((txid,vout)) outputs=[] for _ in range(compact()):outputs.append((int.from_bytes(take(8),'little'),take(compact()).hex())) body=raw[body_start:p] if witness: for _ in range(count): for _ in range(compact()):take(compact()) lock=take(4) if p!=len(raw):raise ValueError('Unsupported trailing data') stripped=version+body+lock;weight=len(stripped)*3+len(raw) return {'bytes':len(raw),'weight':weight,'vsize':math.ceil(weight/4),'txid':digest(stripped),'wtxid':digest(raw),'inputs':inputs,'outputs':outputs} def main(): data=json.loads(pathlib.Path(sys.argv[1]).read_text());total=0 assert data['network']=='regtest' and len(data['cases'])==12 and len(data['flow'])==3 for row in data['cases']+data['flow']: actual=decode(bytes.fromhex(row['hex'])) for key in ['bytes','weight','vsize','txid','wtxid']:assert actual[key]==row[key],(row['id'],key) assert actual['inputs']==[(x['txid'],x['vout']) for x in row['prevouts']] assert actual['outputs']==[(x['valueLitoshi'],x['scriptHex']) for x in row['decodedOutputs']] assert sum(x['valueLitoshi'] for x in row['prevouts'])==row['inputTotalLitoshi'] assert row['inputTotalLitoshi']-sum(x[0] for x in actual['outputs'])==row['feeLitoshi'] assert row['recipientLitoshi']+row['changeLitoshi']+row['feeLitoshi']==row['inputTotalLitoshi'] assert row['mempoolAllowed'] is True;total+=1 steps={x['key']:x for x in data['timeline']} assert steps['lagging-observer']['A']['confirmations']==2 and steps['lagging-observer']['B']['status']=='not-seen' assert steps['reorganized']['A']['confirmations']==0 and steps['reorganized']['A']['tip']==steps['reorganized']['B']['tip'] assert int(steps['reorganized']['A']['chainwork'],16)>int(steps['lagging-observer']['A']['chainwork'],16) assert steps['synchronized']['A']['confirmations']==steps['synchronized']['B']['confirmations']==1 assert all(c['passed'] for c in data['checks']) print(json.dumps({'decodedTransactions':total,'checked':'bytes, weight, vsize, txid, wtxid, prevout identity, output values/scripts, fee conservation; transcript consistency','nodeChecks':len(data['checks'])})) if __name__=='__main__':main()