#!/usr/bin/env python3
"""Reassemble a Bcat file from BSV. Usage: bcat_fetch.py <index_txid> [outdir]
Fetches raw txs from WhatsOnChain, parses the OP_FALSE OP_RETURN Bcat index
(15DHFxWZJT58f9nhyGnsRBqrgwK4W6h4Up), then each chunk (1ChDHzdd1H4wSjgGMHyndZm6qxEDGjqpJL)."""
import sys,hashlib,urllib.request,time
WOC='https://api.whatsonchain.com/v1/bsv/main/tx/%s/hex'
def get(txid):
    for i in range(6):
        try: return bytes.fromhex(urllib.request.urlopen(WOC%txid,timeout=120).read().decode().strip())
        except Exception as e: err=e; time.sleep(3*(i+1))
    raise err
def varint(b,i):
    n=b[i]
    if n<0xfd: return n,i+1
    k={0xfd:2,0xfe:4,0xff:8}[n]; return int.from_bytes(b[i+1:i+1+k],'little'),i+1+k
def scripts(raw):
    i=4; n,i=varint(raw,i)
    for _ in range(n): i+=36; l,i=varint(raw,i); i+=l+4
    n,i=varint(raw,i); out=[]
    for _ in range(n): i+=8; l,i=varint(raw,i); out.append(raw[i:i+l]); i+=l
    return out
def pushes(s):
    if s[:2]==b'\x00\x6a': i=2
    elif s[:1]==b'\x6a': i=1
    else: return None
    r=[]
    while i<len(s):
        o=s[i]; i+=1
        if o==0: r.append(b''); continue
        if o<76: n=o
        elif o==76: n=s[i]; i+=1
        elif o==77: n=int.from_bytes(s[i:i+2],'little'); i+=2
        elif o==78: n=int.from_bytes(s[i:i+4],'little'); i+=4
        else: continue
        r.append(s[i:i+n]); i+=n
    return r
def data(txid,proto):
    for s in scripts(get(txid)):
        p=pushes(s)
        if p and p[0]==proto: return p
    raise SystemExit('no %s output in %s'%(proto,txid))
idx=data(sys.argv[1],b'15DHFxWZJT58f9nhyGnsRBqrgwK4W6h4Up')
info,mime,enc,name=(x.decode() for x in idx[1:5])
ids=[x[::-1].hex() for x in idx[6:]]  # txids stored byte-reversed (internal order)
blob=b''.join(data(t,b'1ChDHzdd1H4wSjgGMHyndZm6qxEDGjqpJL')[1] for t in ids)
out=(sys.argv[2] if len(sys.argv)>2 else '.')+'/'+name
open(out,'wb').write(blob)
print(out,len(blob),'bytes',mime,'sha256',hashlib.sha256(blob).hexdigest(),'| index info:',info)
