-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from jrakibi/add-7-topics
add new topic for transaction structure
- Loading branch information
Showing
25 changed files
with
593 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
--- | ||
title: "Input count" | ||
date: 2024-01-25T15:32:14Z | ||
lastmod: "2024-07-26" | ||
draft: false | ||
category: Transactions | ||
layout: TopicBanner | ||
order: 3.3 | ||
icon: "FaClipboardList" | ||
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-inputcount.jpg"] | ||
parent: "transaction-structure" | ||
--- | ||
|
||
<TransactionCreation | ||
enabledFields={[ | ||
"input_count", | ||
"txid", | ||
"vout", | ||
"scriptsig_size", | ||
"scriptsig", | ||
"sequence" | ||
]} | ||
/> | ||
|
||
The number of inputs is stored as a variable-length integer (varint). | ||
Let's examine this real transaction: | ||
|
||
<div className="dark:hidden w-full rounded-xl overflow-hidden"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_10_inputcount.png" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
<div className="hidden dark:block w-full rounded-xl overflow-hidden"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_10_inputcount.png" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
|
||
Breaking this down: | ||
|
||
- `01000000`: Version (4 bytes) | ||
- `01`: Input count (1 byte, highlighted in the image below) | ||
- `14e68f...`: First input begins | ||
|
||
As shown in the highlighted section of the transaction hex above, this transaction has an input count of 1 (`0x01`). | ||
This single byte tells us that we should expect exactly one input in this transaction. | ||
|
||
The encoding format depends on the value range: | ||
|
||
| Decimal Range | Hex Range | Bytes used | Format | | ||
| ------------- | --------------------------------- | ---------- | ----------------------------------------- | | ||
| 0 to 252 | 0x00 to 0xfc | 1 | uint8_t | | ||
| 253 to 2¹⁶-1 | 0xfd to 0xffff | 3 | `0xfd` followed by the number as uint16_t | | ||
| 2¹⁶ to 2³²-1 | 0x10000 to 0xffffffff | 5 | `0xfe` followed by the number as uint32_t | | ||
| 2³² to 2⁶⁴-1 | 0x100000000 to 0xffffffffffffffff | 9 | `0xff` followed by the number as uint64_t | | ||
|
||
For example: | ||
|
||
- The number 100 is encoded directly as `0x64` | ||
- The number 1000 is encoded as `0xfd 0xe8 0x03` | ||
- The number 100000 is encoded as `0xfe 0xa0 0x86 0x01 0x00` | ||
|
||
<ExpandableAlert title="Note" type="info" expandable={false}> | ||
In our example transaction, the input count is 1 (`0x01`), so it uses the | ||
simplest encoding format of a single byte. | ||
</ExpandableAlert> | ||
|
||
## Implementation | ||
|
||
Here's a Python implementation for reading and writing varints: | ||
|
||
<CodeSnippet | ||
code={`def read_varint(s): | ||
'''Reads a variable integer from a stream | ||
The first byte determines the format: | ||
- If < 0xfd: directly contains the number | ||
- If 0xfd: next 2 bytes contain number | ||
- If 0xfe: next 4 bytes contain number | ||
- If 0xff: next 8 bytes contain number | ||
''' | ||
i = s.read(1)[0] | ||
if i == 0xfd: | ||
return int.from_bytes(s.read(2), 'little') | ||
elif i == 0xfe: | ||
return int.from_bytes(s.read(4), 'little') | ||
elif i == 0xff: | ||
return int.from_bytes(s.read(8), 'little') | ||
else: | ||
return i | ||
def encode_varint(i): | ||
'''Encodes an integer as a varint | ||
- Numbers < 0xfd: 1 byte | ||
- Numbers < 0x10000: 3 bytes (0xfd + 2 bytes) | ||
- Numbers < 0x100000000: 5 bytes (0xfe + 4 bytes) | ||
- Numbers < 0x10000000000000000: 9 bytes (0xff + 8 bytes) | ||
''' | ||
if i < 0xfd: | ||
return bytes([i]) | ||
elif i < 0x10000: | ||
return b'\\xfd' + i.to_bytes(2, 'little') | ||
elif i < 0x100000000: | ||
return b'\\xfe' + i.to_bytes(4, 'little') | ||
elif i < 0x10000000000000000: | ||
return b'\\xff' + i.to_bytes(8, 'little') | ||
else: | ||
raise ValueError(f'integer too large: {i}')`} | ||
language="python" | ||
|
||
/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
--- | ||
title: "Vout: Output index" | ||
date: 2024-01-25T15:32:14Z | ||
lastmod: "2024-07-26" | ||
draft: false | ||
category: Transactions | ||
layout: TopicBanner | ||
order: 3.3 | ||
icon: "FaClipboardList" | ||
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-vout.jpg"] | ||
parent: "transaction-structure" | ||
--- | ||
|
||
The output index (vout) is a 4-byte unsigned integer that identifies which output from the previous transaction we want to spend. | ||
|
||
<TransactionCreation enabledFields={["vout"]} /> | ||
|
||
Let's examine our transaction to understand this better: | ||
|
||
<div className="dark:hidden w-full rounded-xl overflow-hidden"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_12_vout.png" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
<div className="hidden dark:block w-full rounded-xl overflow-hidden"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/fees/fees_12_vout.png" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
|
||
In this transaction, we can see the vout value highlighted in orange: `00000000`. | ||
When decoded from little-endian format, this represents index 0, meaning this input is spending the first output from the previous transaction. | ||
|
||
The vout value tells us which output we're spending from the previous transaction: | ||
|
||
- Outputs are zero-indexed (first output is 0, second is 1, etc.) | ||
- Stored as a 4-byte little-endian integer | ||
|
||
## Outpoints | ||
|
||
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: | ||
|
||
- `4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0` - The first-ever Bitcoin transaction output (genesis block) | ||
- `a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d:0` - The famous Bitcoin pizza transaction (spending the first output) | ||
|
||
<ExpandableAlert title="Special Case: Coinbase Transactions" type="info"> | ||
Coinbase transactions (which create new bitcoins) don't reference any | ||
previous outputs. In these cases, the vout is set to the maximum value of | ||
0xFFFFFFFF (4294967295). | ||
</ExpandableAlert> | ||
|
||
## Implementation Example | ||
|
||
Here's how you might parse a transaction input's vout: | ||
|
||
<CodeSnippet | ||
code={`def parse_vout(raw_tx: bytes, offset: int = 0) -> tuple[int, int]: | ||
""" | ||
Parse an output index from raw transaction bytes | ||
Args: | ||
raw_tx: Raw transaction bytes | ||
offset: Starting position in bytes | ||
Returns: | ||
(vout, new_offset) | ||
""" | ||
# Read 4 bytes for vout | ||
vout_bytes = raw_tx[offset:offset + 4] | ||
# Convert to integer (little-endian) | ||
vout = int.from_bytes(vout_bytes, 'little') | ||
return vout, offset + 4`} | ||
language="python" | ||
/> | ||
|
||
## RPC Commands | ||
|
||
- Use `gettxout <txid> <n>` to view details of an unspent transaction output: | ||
|
||
<CodeSnippet | ||
code={`$ bitcoin-cli gettxout "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d" 0 | ||
{ | ||
"bestblock": "00000000000000000003a963aad5832ac54a8a6a2cf4c4856e30e3aa279e42fd", | ||
"confirmations": 750001, | ||
"value": 0.00100000, | ||
"scriptPubKey": { | ||
"asm": "OP_DUP OP_HASH160 62e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac OP_EQUALVERIFY OP_CHECKSIG", | ||
"desc": "pkh([62e907b1]02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8tscl5xj", | ||
"hex": "76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac", | ||
"type": "pubkeyhash", | ||
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" | ||
} | ||
}`} | ||
language="bash" | ||
/> | ||
|
||
- View all unspent transaction outputs (UTXOs) in your wallet with `listunspent`: | ||
|
||
<CodeSnippet | ||
code={`$ bitcoin-cli listunspent | ||
[ | ||
{ | ||
"txid": "1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", | ||
"vout": 0, | ||
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", | ||
"label": "", | ||
"amount": 0.00100000, | ||
"confirmations": 750000, | ||
"spendable": true, | ||
"solvable": true, | ||
"desc": "pkh([62e907b1]02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)#8tscl5xj", | ||
"safe": true | ||
} | ||
]`} | ||
language="bash" | ||
/> | ||
|
||
## Usage and Verification in Bitcoin | ||
|
||
The vout is crucial for transaction validation and UTXO management. When processing transactions, nodes: | ||
|
||
1. **Locate and Validate**: Use txid to find the previous transaction and verify the vout points to a valid output | ||
2. **Check UTXO Status**: Confirm the referenced output hasn't been spent already | ||
3. **Verify Conditions**: Ensure all spending conditions for that output are met | ||
|
||
<ExpandableAlert title="Warning" type="warning"> | ||
If the vout references a non-existent output index, the transaction is | ||
invalid and will be rejected by the network. | ||
</ExpandableAlert> |
Oops, something went wrong.