Skip to content

Commit

Permalink
Merge pull request #56 from jrakibi/add-7-topics
Browse files Browse the repository at this point in the history
add new topic for transaction structure
  • Loading branch information
jrakibi authored Jan 15, 2025
2 parents 9353942 + a8ba3c5 commit 906abb0
Show file tree
Hide file tree
Showing 25 changed files with 593 additions and 65 deletions.
4 changes: 2 additions & 2 deletions decoding/fee-calculation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ category: Transactions
layout: TopicBanner
order: 6
icon: "FaClipboardList"
images: ["/bitcoin-topics/static/images/topics/thumbnails/musig-thumbnail.webp"]
children: ["transaction-weight", "blocksize-vs-blockweight", "fee-rate", "rbf"]
images: ["/bitcoin-topics/static/images/topics/thumbnails/transaction-module/fees-thumbnail-feecalculation.jpg"]
children: ["transaction-weight", "blocksize-blockweight", "fee-rate", "rbf"]
---

## Topics:
Expand Down
116 changes: 116 additions & 0 deletions decoding/inputs-inputcount.mdx
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"

/>
137 changes: 137 additions & 0 deletions decoding/inputs-output-index.mdx
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>
Loading

0 comments on commit 906abb0

Please sign in to comment.