"""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."}))
