Dati on-chain e SQL · Lezione 2 di 9
Raw Bitcoin transactions and script types
12 min di lettura
Questa lezione non è ancora disponibile in Italiano, quindi è mostrata in inglese.
Explorers show a Bitcoin transaction as tidy lists of addresses and amounts, but that view is a decoding of raw bytes, and some of what it shows is not in the transaction at all. Reading the raw structure tells an investigator what is recorded on chain and what a tool derived, why the fee cannot be computed from a transaction alone, and which fields carry the wallet fingerprints that later heuristics rely on. Reading address formats tells you which script type is in use, which narrows down the wallet software and its era, and what will become public when the coins are spent.
The serialization, field by field
A transaction is stored and relayed as a byte string. Since SegWit (BIP 141, activated in August 2017) there are two forms: the legacy form, and the extended form of BIP 144, used when at least one input carries witness data.
| Field | Size | Notes |
|---|---|---|
| version | 4 bytes | Little-endian integer, usually 1 or 2 |
| marker, flag | 2 bytes | 00 01, present only in the SegWit form |
| input count | variable | CompactSize integer |
| inputs | variable | Each: previous txid (32), output index (4), scriptSig length and scriptSig, sequence (4) |
| output count | variable | CompactSize integer |
| outputs | variable | Each: value in satoshis (8), scriptPubKey length and scriptPubKey |
| witness | variable | SegWit form only: for each input, an item count and the items |
| locktime | 4 bytes | Earliest block height or time at which the transaction is valid |
Integers are little-endian: the least significant byte comes first. A CompactSize integer takes one byte for values below 253, otherwise a prefix byte (fd, fe or ff) followed by 2, 4 or 8 bytes. Here is a one-input, two-output SegWit transaction split into its fields, with long values truncated:
02000000 version 2
00 01 SegWit marker and flag
01 input count 1
ab09…1d7c previous txid, internal byte order (32 bytes)
01000000 previous output index 1
00 scriptSig length 0 (native SegWit input)
fdffffff sequence 0xfffffffd
02 output count 2
40420f0000000000 value 1,000,000 sat
16 0014 3c1e…9d02 script, 22 bytes: P2WPKH
0c6a070000000000 value 485,900 sat
16 0014 77b4…e051 script, 22 bytes: P2WPKH
02 47 3044…01 21 02a1…5f witness: 2 items, signature (71 bytes), public key (33 bytes)
00000000 locktime 0
Read the first value by reversing its bytes: 40 42 0f 00 … becomes 0x0f4240, which is 1,000,000 satoshis (0.01 BTC). Note what is absent: no sender address, no input amount and no fee.
Inputs and outpoints
An input does not hold coins; it points to an earlier output, called an outpoint and written txid:vout. The 32-byte txid inside the input is stored in internal byte order, the reverse of what explorers display: ab09…1d7c in the bytes is 7c1d…09ab on screen. Forgetting this reversal is a classic cause of "transaction not found."
The amount and the script being spent live in the previous output, so to know who paid and how much you must look up each outpoint. The input's scriptSig (legacy scripts) or witness (SegWit scripts) supplies the data that satisfies the previous output's conditions, usually signatures and public keys. A coinbase input, which creates new coins, has an all-zero previous txid and index 0xffffffff.
Sequence, version, locktime and wallet fingerprints
The fields around the inputs and outputs look like boilerplate but carry information:
- Sequence. Values below
0xfffffffesignal replaceability under BIP 125, with0xfffffffdthe common choice. Since Bitcoin Core 28.0 (2024), nodes accept replacements by default whether or not the original signaled, so today the value matters mainly as a fingerprint. Relative timelocks (BIP 68) use the same field. - Version. Almost always 1 or 2; version 2 is needed for relative timelocks, and wallets differ in which they use.
- Locktime. Read as a block height below 500,000,000 and as a Unix timestamp otherwise; zero means no restriction. Many wallets set it to the current height to discourage miners from rewriting recent blocks to capture fees (anti-fee-sniping), so a locktime of 880,213 in a transaction confirmed at 880,214 says something about the software that built it.
These values, with the script types used, the order of outputs and the signature encoding, form a wallet fingerprint. One fingerprint proves nothing; a consistent combination across transactions is one of the signals that Lesson 4.2 uses to tell a payment from change.
Output scripts and address formats
Each output holds a value in satoshis and a scriptPubKey, the condition for spending it. Standard scripts follow fixed templates, so the type can be read from the first bytes. An address is an encoding of a standard script, not a field of the transaction: decoders derive it, and the format reveals the type.
| Type | scriptPubKey pattern | Bytes | Defined in | Encoding | Address | Bitcoin Core type |
|---|---|---|---|---|---|---|
| P2PKH | 76 a9 14 {20} 88 ac | 25 | original | Base58Check, version 0x00 | 1…, up to 34 chars | pubkeyhash |
| P2SH | a9 14 {20} 87 | 23 | BIP 16 | Base58Check, version 0x05 | 3…, 34 chars | scripthash |
| P2WPKH | 00 14 {20} | 22 | BIP 141 | bech32 | bc1q…, 42 chars | witness_v0_keyhash |
| P2WSH | 00 20 {32} | 34 | BIP 141 | bech32 | bc1q…, 62 chars | witness_v0_scripthash |
| P2TR | 51 20 {32} | 34 | BIP 341 | bech32m | bc1p…, 62 chars | witness_v1_taproot |
| OP_RETURN | 6a … | varies | — | none | none | nulldata |
In P2PKH, 76 a9 14 … 88 ac is OP_DUP OP_HASH160 {push 20 bytes} OP_EQUALVERIFY OP_CHECKSIG. SegWit outputs begin with a version opcode (00 for version 0, 51 for version 1, Taproot) followed by a push of the program. An OP_RETURN output is provably unspendable and carries data, not value. Testnets use other prefixes (m or n for P2PKH, 2 for P2SH, tb1 for SegWit), so a stray testnet address in a dataset is easy to spot. Some old scripts, such as early pay-to-public-key outputs and bare multisig, have no standard address at all; warehouses may leave the address column empty, and a filter on address IS NOT NULL silently drops them.
Base58Check
Legacy addresses take a version byte, append the 20-byte payload (a HASH160 of a public key or script), add a checksum of the first 4 bytes of SHA-256(SHA-256(version ‖ payload)), and write the 25 bytes in a 58-character alphabet without 0, O, I and l. Each leading zero byte becomes a leading 1, which is why every P2PKH address starts with it. The encoding is case-sensitive and the checksum rejects almost every typo. The version byte is the classification key: 0x00 means P2PKH, 0x05 P2SH, anything else another network or coin.
Bech32 and bech32m
Native SegWit addresses use bech32 (BIP 173): a human-readable part (bc for mainnet, tb for testnet), the separator 1, and a data part in a 32-character alphabet that excludes 1, b, i and o. The first data character is the witness version (q = 0, p = 1), followed by the program and a 6-character checksum. A 20-byte program gives 42 characters (P2WPKH), a 32-byte one 62 (P2WSH). Bech32 is all lowercase (uppercase is allowed for QR codes, mixed case must be rejected), at most 90 characters, and its checksum detects any error affecting up to four characters.
It has one flaw: when a string ends in p, inserting or deleting q characters just before it can leave the checksum valid. Version 0's strict program lengths make this mostly harmless, but later versions allow other lengths, so BIP 350 introduced bech32m, identical except for the checksum constant (0x2bc830a3 instead of 1). Version 0 keeps bech32; versions 1 to 16, including Taproot, use bech32m, and decoders must reject the wrong checksum for each. In pipelines, never classify on "starts with bc1": read the fourth character and the length. Parsers written before 2021 may reject valid bc1p addresses, and a pipeline that drops unparseable rows under-counts Taproot activity without warning.
Witness data, weight and size
SegWit moves signatures into a separate witness. What it holds depends on the type being spent:
- P2WPKH: a DER signature with a sighash byte (usually 71 or 72 bytes) and a 33-byte compressed public key.
- P2WSH: the satisfying items followed by the witness script itself, so the spending policy becomes visible only when the output is spent.
- P2TR key path: one 64-byte Schnorr signature (BIP 340), or 65 bytes with a non-default sighash type. A script-path spend reveals one script (BIP 342) and a control block.
- P2SH-wrapped SegWit: the scriptSig carries only a short redeem script such as
0014…, and the signatures sit in the witness.
Witness bytes are discounted. BIP 141 defines weight as base size × 3 + total size, where the base size excludes the marker, flag and witness; virtual size (vsize) is weight ÷ 4, rounded up, and fee rates are quoted per vbyte. For the example, the base form is 113 bytes and the witness section, counting marker and flag, adds 109, for 222 bytes in total. Weight is 113 × 3 + 222 = 561, and 561 ÷ 4 rounds up to 141 vbytes.
The decoded view and the fee
Bitcoin Core's getrawtransaction with verbosity 2 returns the transaction decoded, plus each spent output (prevout) and the fee, when the node has the block's undo data (Lesson 2.1 covers the node calls):
{
"txid": "4f3a…c21e", "hash": "9b07…e4d2",
"version": 2, "size": 222, "vsize": 141, "weight": 561, "locktime": 0,
"vin": [{
"txid": "7c1d…09ab", "vout": 1,
"scriptSig": {"asm": "", "hex": ""},
"txinwitness": ["3044…01", "02a1…5f"],
"prevout": {"value": 0.01500000, "height": 868120,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…7k3f"}},
"sequence": 4294967293
}],
"vout": [
{"value": 0.01000000, "n": 0,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…m2d8"}},
{"value": 0.00485900, "n": 1,
"scriptPubKey": {"type": "witness_v0_keyhash", "address": "bc1q…x9qa"}}
],
"fee": 0.00014100
}
The fee is never written in the transaction; it is inputs minus outputs. Here the one outpoint is worth 1,500,000 sat, the outputs total 1,000,000 + 485,900 = 1,485,900 sat, so the fee is 14,100 sat, and 14,100 ÷ 141 = 100 sat/vB. Plain decoderawtransaction returns the same structure without prevout and fee, because it cannot see previous outputs. Amounts arrive in BTC as JSON numbers; convert them to integer satoshis before any arithmetic (Lesson 2.6). A coinbase transaction has no fee: its outputs claim the subsidy plus all other fees. Lesson 2.9 shows the same calculation in SQL over inputs and outputs tables.
txid versus wtxid
The txid is the double SHA-256 of the transaction serialized without marker, flag and witness; the wtxid hashes the full SegWit form, and Bitcoin Core shows it as hash. Without witness data the two are identical. Excluding the witness fixed third-party malleability: altering a signature's encoding used to change the txid of an otherwise identical transaction. Witness changes now alter only the wtxid, so chains of unconfirmed transactions can safely reference each other. Blocks commit to wtxids through a witness commitment in the coinbase. In reports, identify transactions by txid in display byte order.
What scripts reveal about wallets and custody
Address formats track wallet history:
1…: old wallets, legacy-only services, some long-standing exchange wallets; fresh P2PKH activity today often points to older or custom software.3…: ambiguous until spent: multisig, or a single-key wallet using nested SegWit, a common default around 2017–2019. At the spend, the redeem script resolves it:0014plus 20 bytes is nested P2WPKH,0020plus 32 bytes nested P2WSH, a script ending inae(OP_CHECKMULTISIG) classic multisig.bc1q…: the default receive type of most mainstream wallets in recent years; 62-character P2WSH usually means multisig or another custom script.bc1p…: wallets that adopted Taproot after its activation at block 709,632 in November 2021, plus inscription and token tooling.
Script types also mix within transactions: if all inputs are P2WPKH and the outputs are one P2WPKH and one P2SH, the output matching the inputs is more likely to be change (a heuristic, not a fact; see Lesson 4.2).
Classic multisig
Classic multisig hides its policy while funds sit unspent (only a hash is on chain) and publishes it at the first spend. A 2-of-3 P2WSH input:
witness[0] (empty) -- dummy element required by OP_CHECKMULTISIG
witness[1] 3044…01 -- first signature (DER, sighash byte 01)
witness[2] 3045…01 -- second signature
witness[3] 5221…21…21…53ae -- OP_2 <key1> <key2> <key3> OP_3 OP_CHECKMULTISIG
52 is OP_2 (two signatures required), each 21 pushes a 33-byte key, 53 is OP_3 and ae is OP_CHECKMULTISIG. One spend reveals the threshold, all three public keys and which two signed. Custodians often run distinctive policies (2-of-3, 3-of-5), and the same public key appearing in many multisig scripts links those addresses to one key holder far more reliably than behavioral heuristics. Many wallets sort keys lexicographically (BIP 67), itself a small fingerprint.
Taproot
Taproot combines Schnorr signatures with 32-byte "x-only" keys (BIP 340), an output key Q = P + t·G where P is an internal key and the tweak t commits to P and the Merkle root of an optional script tree (BIP 341), and script rules that replace OP_CHECKMULTISIG with OP_CHECKSIGADD (BIP 342).
| Spend | Witness | What an analyst learns |
|---|---|---|
| P2WSH 2-of-3 multisig | Two signatures plus the full script | Threshold, all keys, which keys signed |
| P2TR key path | One 64-byte signature (65 with explicit sighash) | Nothing about the policy |
| P2TR script path | Leaf script, its inputs, control block of 33 + 32m bytes | The one condition used, its depth m, the internal key |
On the key path, a single-key wallet, an n-of-n group using key aggregation (MuSig2) and a threshold scheme all look the same. The script path reveals only the leaf used, though the Merkle path length leaks something about the tree's size. Taproot removes the multisig footprint whenever the key path is used; it does not hide amounts, the graph or timing, and the bc1p format itself still distinguishes its users. Script-path spends also carry inscriptions: since 2023, large witnesses with an OP_FALSE OP_IF … OP_ENDIF envelope embed images and token data, a pattern to filter out of payment analysis.
Exam focus: expect to decode or classify, not just recall: field order and sizes, little-endian values and CompactSize; outpoints and reversed txid byte order; scriptPubKey templates, BIPs, prefixes and lengths (1,3,bc1qat 42 and 62,bc1pat 62); Base58Check version bytes and bech32 versus bech32m; that addresses, input amounts and fees are derived, not stored; weight and vsize arithmetic; fee from previous outputs; txid versus wtxid and malleability; how a spent P2SH resolves, what a multisig spend reveals, and what Taproot key-path and script-path spends hide.