Skip to content

Commit 906abb0

Browse files
authored
Merge pull request #56 from jrakibi/add-7-topics
add new topic for transaction structure
2 parents 9353942 + a8ba3c5 commit 906abb0

25 files changed

+593
-65
lines changed

decoding/fee-calculation.mdx

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ category: Transactions
77
layout: TopicBanner
88
order: 6
99
icon: "FaClipboardList"
10-
images: ["/bitcoin-topics/static/images/topics/thumbnails/musig-thumbnail.webp"]
11-
children: ["transaction-weight", "blocksize-vs-blockweight", "fee-rate", "rbf"]
10+
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-feecalculation.jpg"]
11+
children: ["transaction-weight", "blocksize-blockweight", "fee-rate", "rbf"]
1212
---
1313

1414
## Topics:

decoding/inputs-inputcount.mdx

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: "Input count"
3+
date: 2024-01-25T15:32:14Z
4+
lastmod: "2024-07-26"
5+
draft: false
6+
category: Transactions
7+
layout: TopicBanner
8+
order: 3.3
9+
icon: "FaClipboardList"
10+
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-inputcount.jpg"]
11+
parent: "transaction-structure"
12+
---
13+
14+
<TransactionCreation
15+
enabledFields={[
16+
"input_count",
17+
"txid",
18+
"vout",
19+
"scriptsig_size",
20+
"scriptsig",
21+
"sequence"
22+
]}
23+
/>
24+
25+
The number of inputs is stored as a variable-length integer (varint).
26+
Let's examine this real transaction:
27+
28+
<div className="dark:hidden w-full rounded-xl overflow-hidden">
29+
<SvgDisplay
30+
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_10_inputcount.png"
31+
width="100%"
32+
height="auto"
33+
/>
34+
</div>
35+
<div className="hidden dark:block w-full rounded-xl overflow-hidden">
36+
<SvgDisplay
37+
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_10_inputcount.png"
38+
width="100%"
39+
height="auto"
40+
/>
41+
</div>
42+
43+
Breaking this down:
44+
45+
- `01000000`: Version (4 bytes)
46+
- `01`: Input count (1 byte, highlighted in the image below)
47+
- `14e68f...`: First input begins
48+
49+
As shown in the highlighted section of the transaction hex above, this transaction has an input count of 1 (`0x01`).
50+
This single byte tells us that we should expect exactly one input in this transaction.
51+
52+
The encoding format depends on the value range:
53+
54+
| Decimal Range | Hex Range | Bytes used | Format |
55+
| ------------- | --------------------------------- | ---------- | ----------------------------------------- |
56+
| 0 to 252 | 0x00 to 0xfc | 1 | uint8_t |
57+
| 253 to 2¹⁶-1 | 0xfd to 0xffff | 3 | `0xfd` followed by the number as uint16_t |
58+
| 2¹⁶ to 2³²-1 | 0x10000 to 0xffffffff | 5 | `0xfe` followed by the number as uint32_t |
59+
| 2³² to 2⁶⁴-1 | 0x100000000 to 0xffffffffffffffff | 9 | `0xff` followed by the number as uint64_t |
60+
61+
For example:
62+
63+
- The number 100 is encoded directly as `0x64`
64+
- The number 1000 is encoded as `0xfd 0xe8 0x03`
65+
- The number 100000 is encoded as `0xfe 0xa0 0x86 0x01 0x00`
66+
67+
<ExpandableAlert title="Note" type="info" expandable={false}>
68+
In our example transaction, the input count is 1 (`0x01`), so it uses the
69+
simplest encoding format of a single byte.
70+
</ExpandableAlert>
71+
72+
## Implementation
73+
74+
Here's a Python implementation for reading and writing varints:
75+
76+
<CodeSnippet
77+
code={`def read_varint(s):
78+
'''Reads a variable integer from a stream
79+
80+
The first byte determines the format:
81+
- If < 0xfd: directly contains the number
82+
- If 0xfd: next 2 bytes contain number
83+
- If 0xfe: next 4 bytes contain number
84+
- If 0xff: next 8 bytes contain number
85+
'''
86+
i = s.read(1)[0]
87+
if i == 0xfd:
88+
return int.from_bytes(s.read(2), 'little')
89+
elif i == 0xfe:
90+
return int.from_bytes(s.read(4), 'little')
91+
elif i == 0xff:
92+
return int.from_bytes(s.read(8), 'little')
93+
else:
94+
return i
95+
96+
def encode_varint(i):
97+
'''Encodes an integer as a varint
98+
99+
- Numbers < 0xfd: 1 byte
100+
- Numbers < 0x10000: 3 bytes (0xfd + 2 bytes)
101+
- Numbers < 0x100000000: 5 bytes (0xfe + 4 bytes)
102+
- Numbers < 0x10000000000000000: 9 bytes (0xff + 8 bytes)
103+
'''
104+
if i < 0xfd:
105+
return bytes([i])
106+
elif i < 0x10000:
107+
return b'\\xfd' + i.to_bytes(2, 'little')
108+
elif i < 0x100000000:
109+
return b'\\xfe' + i.to_bytes(4, 'little')
110+
elif i < 0x10000000000000000:
111+
return b'\\xff' + i.to_bytes(8, 'little')
112+
else:
113+
raise ValueError(f'integer too large: {i}')`}
114+
language="python"
115+
116+
/>

decoding/inputs-output-index.mdx

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
title: "Vout: Output index"
3+
date: 2024-01-25T15:32:14Z
4+
lastmod: "2024-07-26"
5+
draft: false
6+
category: Transactions
7+
layout: TopicBanner
8+
order: 3.3
9+
icon: "FaClipboardList"
10+
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-vout.jpg"]
11+
parent: "transaction-structure"
12+
---
13+
14+
The output index (vout) is a 4-byte unsigned integer that identifies which output from the previous transaction we want to spend.
15+
16+
<TransactionCreation enabledFields={["vout"]} />
17+
18+
Let's examine our transaction to understand this better:
19+
20+
<div className="dark:hidden w-full rounded-xl overflow-hidden">
21+
<SvgDisplay
22+
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_12_vout.png"
23+
width="100%"
24+
height="auto"
25+
/>
26+
</div>
27+
<div className="hidden dark:block w-full rounded-xl overflow-hidden">
28+
<SvgDisplay
29+
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_12_vout.png"
30+
width="100%"
31+
height="auto"
32+
/>
33+
</div>
34+
35+
In this transaction, we can see the vout value highlighted in orange: `00000000`.
36+
When decoded from little-endian format, this represents index 0, meaning this input is spending the first output from the previous transaction.
37+
38+
The vout value tells us which output we're spending from the previous transaction:
39+
40+
- Outputs are zero-indexed (first output is 0, second is 1, etc.)
41+
- Stored as a 4-byte little-endian integer
42+
43+
## Outpoints
44+
45+
The combination of a TXID and vout (written as TXID:vout) is called an "outpoint". Every output in the Bitcoin blockchain can be uniquely identified by its outpoint. Here are some notable examples:
46+
47+
- `4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0` - The first-ever Bitcoin transaction output (genesis block)
48+
- `a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d:0` - The famous Bitcoin pizza transaction (spending the first output)
49+
50+
<ExpandableAlert title="Special Case: Coinbase Transactions" type="info">
51+
Coinbase transactions (which create new bitcoins) don't reference any
52+
previous outputs. In these cases, the vout is set to the maximum value of
53+
0xFFFFFFFF (4294967295).
54+
</ExpandableAlert>
55+
56+
## Implementation Example
57+
58+
Here's how you might parse a transaction input's vout:
59+
60+
<CodeSnippet
61+
code={`def parse_vout(raw_tx: bytes, offset: int = 0) -> tuple[int, int]:
62+
"""
63+
Parse an output index from raw transaction bytes
64+
65+
Args:
66+
raw_tx: Raw transaction bytes
67+
offset: Starting position in bytes
68+
69+
Returns:
70+
(vout, new_offset)
71+
"""
72+
# Read 4 bytes for vout
73+
vout_bytes = raw_tx[offset:offset + 4]
74+
75+
# Convert to integer (little-endian)
76+
vout = int.from_bytes(vout_bytes, 'little')
77+
78+
return vout, offset + 4`}
79+
language="python"
80+
/>
81+
82+
## RPC Commands
83+
84+
- Use `gettxout <txid> <n>` to view details of an unspent transaction output:
85+
86+
<CodeSnippet
87+
code={`$ bitcoin-cli gettxout "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" 0
88+
89+
{
90+
"bestblock": "00000000000000000003a963aad5832ac54a8a6a2cf4c4856e30e3aa279e42fd",
91+
"confirmations": 750001,
92+
"value": 0.00100000,
93+
"scriptPubKey": {
94+
"asm": "OP_DUP OP_HASH160 62e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac OP_EQUALVERIFY OP_CHECKSIG",
95+
"desc": "pkh([62e907b1]02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8tscl5xj",
96+
"hex": "76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac",
97+
"type": "pubkeyhash",
98+
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
99+
}
100+
}`}
101+
language="bash"
102+
/>
103+
104+
- View all unspent transaction outputs (UTXOs) in your wallet with `listunspent`:
105+
106+
<CodeSnippet
107+
code={`$ bitcoin-cli listunspent
108+
109+
[
110+
{
111+
"txid": "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d",
112+
"vout": 0,
113+
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
114+
"label": "",
115+
"amount": 0.00100000,
116+
"confirmations": 750000,
117+
"spendable": true,
118+
"solvable": true,
119+
"desc": "pkh([62e907b1]02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8tscl5xj",
120+
"safe": true
121+
}
122+
]`}
123+
language="bash"
124+
/>
125+
126+
## Usage and Verification in Bitcoin
127+
128+
The vout is crucial for transaction validation and UTXO management. When processing transactions, nodes:
129+
130+
1. **Locate and Validate**: Use txid to find the previous transaction and verify the vout points to a valid output
131+
2. **Check UTXO Status**: Confirm the referenced output hasn't been spent already
132+
3. **Verify Conditions**: Ensure all spending conditions for that output are met
133+
134+
<ExpandableAlert title="Warning" type="warning">
135+
If the vout references a non-existent output index, the transaction is
136+
invalid and will be rejected by the network.
137+
</ExpandableAlert>

0 commit comments

Comments
 (0)