"""Verify recorded ordinary transaction bytes and metadata exports without Core/network. This verifies internal records/arithmetic, not signatures, actual process history or an independent witness. python verify.py.txt . """ from decimal import Decimal from pathlib import Path import csv,hashlib,json,sys folder=Path(sys.argv[1]if len(sys.argv)>1 else".") data=json.loads((folder/"results.json").read_text(encoding="utf-8")) trace=json.loads((folder/"rpc-trace.json").read_text(encoding="utf-8"))["calls"] rows=list(csv.DictReader((folder/"cases.csv").open(encoding="utf-8"))) checks=0 def check(value,why): global checks if not value:raise AssertionError(why) checks+=1 def sha(raw):return hashlib.sha256(hashlib.sha256(raw).digest()).digest()[::-1].hex() def parse(raw): pos=4;witness=raw[pos:pos+2]==b"\0\1";pos+=2 if witness else 0 def take(n): nonlocal pos check(0<=n<=len(raw)-pos,"transaction byte bounds") out=raw[pos:pos+n];pos+=n;return out def varint(): n=take(1)[0] if n<253:return n size={253:2,254:4,255:8}[n];return int.from_bytes(take(size),"little") input_start=pos;n_in=varint();check(1<=n_in<=100,"bounded ordinary input count") for _ in range(n_in):take(36);take(varint());take(4) inputs=raw[input_start:pos];output_start=pos;n_out=varint();check(1<=n_out<=100,"bounded ordinary output count");outputs=[] for n in range(n_out): amount=int.from_bytes(take(8),"little");script=take(varint());outputs.append((n,amount,script.hex())) encoded_outputs=raw[output_start:pos] if witness: for _ in range(n_in): items=varint();check(items<=100,"bounded witness stack") for _ in range(items):take(varint()) locktime=take(4);check(pos==len(raw),"exact full transaction consumption") stripped=raw[:4]+inputs+encoded_outputs+locktime;weight=len(stripped)*3+len(raw) return {"txid":sha(stripped),"wtxid":sha(raw),"outputs":outputs,"size":len(raw),"vsize":(weight+3)//4,"weight":weight} check(data["network"]=="regtest","explicit test network") check(len(data["cases"])==len(rows)==4,"four case/CSV records") check(data["assertions_passed"]==len(data["assertions"])==92,"experiment assertion count") check(all(c["passed"]for c in data["assertions"]),"recorded assertions passed") check("-listen=0"in data["node_options"]and"-connect=0"in data["node_options"],"explicit noP2P options") check("-dbcache=32"in data["node_options"]and"-par=1"in data["node_options"],"bounded resource options") for wallet in ["litecoind","litecoin-cli"]:check("0.21.5.8"in data["core_versions"][wallet],"pinned tested version") stops=[c for c in data["peer_lifecycle"]if c["event"]=="stopped"];check(len(stops)==2 and all(c["exit_code"]==0 for c in stops),"both recorded processes exited cleanly") check(not any(c["method"]in["dumpprivkey","dumpwallet","walletpassphrase","importprivkey","importwallet"]for c in trace),"no key export/import/auth RPC in transcript") check(all(c.get("result")==0 for c in trace if c["method"]=="getconnectioncount"),"all recorded peer counts zero") total=0;nonempty=0;raw_metrics=[] for case,row in zip(data["cases"],rows): raw=bytes.fromhex(case["raw_hex"]);parsed=parse(raw) check(parsed["txid"]==case["txid"]==case["decoded"]["txid"],"independent stripped TXID") check(parsed["wtxid"]==case["decoded"]["hash"],"independent witness TXID") check(parsed["size"]==case["decoded"]["size"],"byte size") check(parsed["vsize"]==case["decoded"]["vsize"],"independent weight/vsize") n,value,script=parsed["outputs"][case["recipient_vout"]] expected=Decimal(case["amount"])*100000000;check(expected==expected.to_integral_exact()and value==int(expected),"raw output exact integer litoshis") check(script==case["recipient_script"]and script.startswith("0014")and len(script)==44,"native ordinary SegWit script") check(all(not script.startswith("6a")for _,_,script in parsed["outputs"]),"no data-carrier output in these cases") total+=value for before,after,wallet in [(case["sender_metadata_before_restart"],case["sender_metadata_after_restart"],"sender"),(case["recipient_metadata_before_restart"],case["recipient_metadata_after_restart"],"recipient")]: check(before==after,"same per-wallet metadata after restart") calls=[c["result"]for c in trace if c["method"]=="gettransaction"and c["wallet"]==wallet and c["params"]==[case["txid"]]] check(len(calls)>=2 and calls[0]["hex"]==calls[-1]["hex"]==case["raw_hex"],"trace preserves exact bytes across restart") for key in ["comment","to"]: check(before[key+"_present"]==(key in calls[0])and before[key]==calls[0].get(key),"metadata matches actual RPC record") check(not case["recipient_metadata_after_restart"]["comment_present"]and not case["recipient_metadata_after_restart"]["to_present"],"recipient did not acquire sender fields") for field,column in [("comment","comment_in_raw"),("comment_to","comment_to_in_raw")]: encoded=case[field].encode("utf-8") check(int(row[field+"_utf8_bytes"])==len(encoded)and int(row[field+"_chars"])==len(case[field]),"CSV Unicode length and UTF8byte count") if encoded: nonempty+=1;check(encoded not in raw and encoded[:24]not in raw,"full/prefix marker absent from raw bytes") check(row[column]=="False"and case["searches"][field]["full_string_found"]is False,"CSV absence matches raw verification") else:check(row[column]==""and case["searches"][field]["full_string_found"]is None,"empty control not misreported as substring absence") expected_conf=105-case["observed_height"]+1 check(case["sender_confirmations_after_restart"]==case["recipient_confirmations_after_restart"]==int(row["confirmations"])==expected_conf,"confirmation-depth arithmetic") raw_metrics.append({"case":case["id"],"bytes":parsed["size"],"vsize":parsed["vsize"]}) check(total==11500000 and Decimal(data["recipient_total_ltc"])==Decimal(total)/100000000,"independent summed11500000litoshis") check(nonempty==6,"six nonempty strings searched") check(data["labels"]["hex_before"]==data["labels"]["hex_after"],"address relabeling left raw bytes unchanged") check(data["labels"]["sender_label"]!=data["labels"]["recipient_label"],"different local labels recorded") print(json.dumps({"ok":True,"checks":checks,"recipient_litoshis":total,"raw_metrics":raw_metrics,"nonempty_strings_checked":nonempty,"scope":"Internal export verification and independent byte/TXID/integer calculations; not signature verification, a new Core run or an external witness."}))