-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path10_decode_tx.py
More file actions
64 lines (52 loc) · 2.42 KB
/
Copy path10_decode_tx.py
File metadata and controls
64 lines (52 loc) · 2.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# SPDX-License-Identifier: MIT
"""10 - Decode a transaction and draw it: inputs on the left, outputs on the right.
Fetches a transaction from TestnetScan's public API (no node needed) and draws where
the coins came from and where they went, plus the fee. This is the read-only mirror of
recipe 08 (which builds one). On Bitcoin and Litecoin every amount and address is public,
which recipe 12 contrasts with Monero, where they are hidden.
python 10_decode_tx.py <txid>
Get a txid from a recent block (recipe 11), the mempool (recipe 04), or the explorer.
"""
import sys
import requests
from config import api
if len(sys.argv) < 2:
sys.exit("usage: python 10_decode_tx.py <txid> (get one from 11_block.py or 04_mempool.py)")
txid = sys.argv[1]
tx = requests.get(api(f"/tx/{txid}"), timeout=30).json()
vsize = -(-tx["weight"] // 4) # ceil(weight / 4)
fee = tx.get("fee", 0)
rate = fee / vsize if vsize else 0
st = tx["status"]
where = f"confirmed at height {st['block_height']:,}" if st.get("confirmed") else "unconfirmed (in the mempool)"
def short(a):
a = a or ""
return a if len(a) <= 20 else f"{a[:11]}...{a[-6:]}"
def side(entry): # "address value"
addr, val = entry
amt = "" if val is None else format(val, ",")
return f"{short(addr):<21}{amt:>11}"
ins = []
for v in tx["vin"]:
if v.get("is_coinbase"):
ins.append(("coinbase (new coins)", None))
else:
p = v.get("prevout") or {}
ins.append((p.get("scriptpubkey_address") or f"[{p.get('scriptpubkey_type', '?')}]", p.get("value", 0)))
outs = [(o.get("scriptpubkey_address") or f"[{o.get('scriptpubkey_type', '?')}]", o.get("value", 0)) for o in tx["vout"]]
total_in = sum(v for _, v in ins if v is not None)
total_out = sum(v for _, v in outs)
print(f"tx {short(txid)} {where}")
print(f"size {vsize:,} vB fee {fee:,} sat ({rate:.1f} sat/vB)")
print()
print(f"{'INPUTS (' + str(len(ins)) + ')':<32} {'OUTPUTS (' + str(len(outs)) + ')'}")
for i in range(max(len(ins), len(outs))):
left = side(ins[i]) if i < len(ins) else " " * 32
arrow = " -> " if i == 0 else " "
right = side(outs[i]) if i < len(outs) else ""
print(f"{left}{arrow}{right}".rstrip())
print()
if total_in:
print(f"in {total_in:,} sat -> out {total_out:,} sat + fee {fee:,} sat")
else:
print(f"out {total_out:,} sat (coinbase: inputs are newly issued coins plus fees)")