-
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 #64 from jrakibi/01-02-siganture-topics
Add 11 topics on tx signature
- Loading branch information
Showing
32 changed files
with
1,765 additions
and
42 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
--- | ||
title: "Step 0: Create Base Transaction" | ||
date: 2024-01-25 | ||
lastmod: "2024-01-25" | ||
draft: false | ||
category: Transactions | ||
layout: TopicBanner | ||
order: 0 | ||
icon: "FaClipboardList" | ||
images: | ||
[ | ||
"/bitcoin-topics/static/images/topics/thumbnails/transaction-module/signature/tx-thumbnail-signature-0.jpg" | ||
] | ||
--- | ||
|
||
In this step, we define the base structure of the transaction, which includes: | ||
|
||
- Version (4 bytes) | ||
- Marker/Flag (required for SegWit transactions) | ||
- Locktime (4 bytes) | ||
|
||
Inputs, outputs, and witness data will be added in the next steps. | ||
|
||
<div className="dark:hidden w-full rounded-xl overflow-hidden full-width"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/signature/signature6.svg" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
<div className="hidden dark:block w-full rounded-xl overflow-hidden full-width"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/signature/signature6.svg" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
|
||
<ExpandableAlert | ||
title="SegWit vs Legacy Transactions" | ||
type="important" | ||
expandable={true} | ||
initialLines={3} | ||
> | ||
If a transaction has at least one SegWit input (native or wrapped), it must | ||
include: | ||
- Marker byte (0x00) | ||
- Flag byte (0x01) | ||
</ExpandableAlert> | ||
|
||
The transaction structure at this stage is: | ||
|
||
<CodeSnippet | ||
language="text" | ||
highlightLines={[4, 5, 6, 12]} | ||
showLineNumbers={true} | ||
code={`Transaction Breakdown: | ||
═══════════════════════════════════════════════════════════════════════════════════ | ||
version: 01000000 | ||
marker: 00 | ||
flag: 01 | ||
in: # We'll add inputs in the next step | ||
out: # We'll add outputs later | ||
locktime: 11000000`} | ||
/> | ||
|
||
## Code Implementation | ||
|
||
<CodeSnippet | ||
code={`def create_basic_tx( | ||
version: int, | ||
inputs: list, | ||
outputs: list, | ||
locktime: int, | ||
segwit: bool = True | ||
) -> bytes: | ||
# 4-byte version in little-endian | ||
tx_version = int_to_little_endian(version, 4) | ||
# Marker + Flag for segwit (only if segwit=True) | ||
marker_flag = b'\\x00\\x01' if segwit else b'' | ||
# Number of inputs/outputs (varint) | ||
in_count = varint(len(inputs)) | ||
out_count = varint(len(outputs)) | ||
# Serialize inputs and outputs | ||
serialized_inputs = b''.join(inputs) | ||
serialized_outputs = b''.join(outputs) | ||
# Locktime (4 bytes) | ||
tx_locktime = int_to_little_endian(locktime, 4) | ||
return ( | ||
tx_version + | ||
marker_flag + | ||
in_count + | ||
serialized_inputs + | ||
out_count + | ||
serialized_outputs + | ||
tx_locktime | ||
)`} | ||
language="python" | ||
/> | ||
|
||
Let's now add inputs and outputs in the next step! |
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,133 @@ | ||
--- | ||
title: "Step 1: Create Transaction Inputs" | ||
date: 2024-01-25 | ||
lastmod: "2024-01-25" | ||
draft: false | ||
category: Transactions | ||
layout: TopicBanner | ||
order: 1 | ||
icon: "FaClipboardList" | ||
images: | ||
[ | ||
"/bitcoin-topics/static/images/topics/thumbnails/transaction-module/signature/tx-thumbnail-signature-1.jpg" | ||
] | ||
--- | ||
|
||
Each input in a Bitcoin transaction must specify: | ||
|
||
- Transaction ID (32 bytes): Points to the UTXO being spent | ||
- Output Index (4 bytes): Which output from that transaction | ||
- ScriptSig: Placeholder for the unlocking script | ||
- Sequence (4 bytes): Usually 0xFFFFFFFF | ||
|
||
<div className="w-full rounded-xl overflow-hidden full-width"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/signature/signature7.svg" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
|
||
The following table summarizes our UTXOs (Transaction IDs shown in big-endian format): | ||
|
||
| Input | Transaction ID (big-endian) | Output Index | Sequence | | ||
| ----- | ---------------------------------------------------------------- | ------------ | -------- | | ||
| #1 | 9f96add4e4db413543df3eea1781c3be62637f1e2dd44069fa99801a88f7f7ff | 0 | eeffffff | | ||
| #2 | 8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef | 1 | ffffffff | | ||
|
||
_Note: In the transaction serialization below, these Transaction IDs are converted to little-endian format as required by the Bitcoin protocol._ | ||
|
||
<CodeSnippet | ||
language="text" | ||
highlightLines={[6, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20]} | ||
showLineNumbers={true} | ||
code={`Transaction Breakdown: | ||
═══════════════════════════════════════════════════════════════════════════════════ | ||
Version: 01000000 | ||
Input Count: 02 | ||
input[0]: | ||
Previous TXID: fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f | ||
Output Index: 00000000 | ||
ScriptSig Size: 00 | ||
ScriptSig: | ||
Sequence: eeffffff | ||
input[1]: | ||
Previous TXID: ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a | ||
Output Index: 01000000 | ||
ScriptSig Size: 00 | ||
ScriptSig: | ||
Sequence: ffffffff | ||
Locktime: 11000000`} | ||
/> | ||
|
||
<ExpandableAlert | ||
title="Why are scriptSig fields empty?" | ||
type="info" | ||
expandable={true} | ||
> | ||
Transaction signing follows a specific order: | ||
1. First, we create the | ||
transaction structure with empty scriptSig fields | ||
2. Then, we calculate the | ||
signature hash (sighash) for each input | ||
3. Finally, we add the signatures to | ||
create the complete transaction | ||
</ExpandableAlert> | ||
|
||
## Code Implementation | ||
|
||
<CodeSnippet | ||
code={`def create_input( | ||
txid: str, | ||
vout: int, | ||
script_sig: bytes = b'', | ||
sequence: bytes = b'\\xff\\xff\\xff\\xff' | ||
) -> bytes: | ||
# Convert txid from hex string and reverse (to little-endian) | ||
txid_bytes = bytes.fromhex(txid)[::-1] | ||
# Convert vout to 4 bytes, little-endian | ||
vout_bytes = int_to_little_endian(vout, 4) | ||
# Script length and script | ||
script_sig_length = varint(len(script_sig)) | ||
return ( | ||
txid_bytes + # 32 bytes | ||
vout_bytes + # 4 bytes | ||
script_sig_length + # 1 byte | ||
script_sig + # variable | ||
sequence # 4 bytes | ||
)`} | ||
language="python" | ||
/> | ||
|
||
<ExpandableAlert title="Helper Functions" type="info" expandable={true}> | ||
Two essential encoding functions: | ||
1. int_to_little_endian: Converts integers to little-endian byte format | ||
2. varint: Encodes variable-length integers used for counts and lengths | ||
|
||
<CodeSnippet | ||
code={`def int_to_little_endian(value: int, length: int) -> bytes: | ||
"""Convert an integer to little-endian bytes""" | ||
return value.to_bytes(length, 'little') | ||
def varint(n: int) -> bytes: | ||
"""Encode an integer as a variable length integer""" | ||
if n < 0xfd: | ||
return bytes([n]) | ||
elif n <= 0xffff: | ||
return b'\\xfd' + n.to_bytes(2, 'little') | ||
elif n <= 0xffffffff: | ||
return b'\\xfe' + n.to_bytes(4, 'little') | ||
else: | ||
return b'\\xff' + n.to_bytes(8, 'little')`} | ||
language="python" | ||
/> | ||
|
||
</ExpandableAlert> |
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,113 @@ | ||
--- | ||
title: "Step 2: Create Transaction Outputs" | ||
date: 2024-01-25 | ||
lastmod: "2024-01-25" | ||
draft: false | ||
category: Transactions | ||
layout: TopicBanner | ||
order: 2 | ||
icon: "FaClipboardList" | ||
images: | ||
[ | ||
"/bitcoin-topics/static/images/topics/thumbnails/transaction-module/signature/tx-thumbnail-signature-2.jpg" | ||
] | ||
--- | ||
|
||
Each output in a Bitcoin transaction requires: | ||
|
||
- Amount (8 bytes): Value in satoshis | ||
- ScriptPubKey: Locking script that defines spending conditions | ||
|
||
<div className="w-full rounded-xl overflow-hidden full-width"> | ||
<SvgDisplay | ||
src="/bitcoin-topics/static/images/topics/transactions/signature/signature8.svg" | ||
width="100%" | ||
height="auto" | ||
/> | ||
</div> | ||
|
||
--- | ||
|
||
## Code Implementation | ||
|
||
<CodeSnippet | ||
code={`def create_output( | ||
amount: int, | ||
script_pubkey: bytes | ||
) -> bytes: | ||
# Amount (8 bytes, little-endian) | ||
amount_bytes = int_to_little_endian(amount, 8) | ||
return ( | ||
amount_bytes + # value in satoshis | ||
script_pubkey # includes length prefix | ||
)`} | ||
language="python" | ||
/> | ||
|
||
The following table summarizes our outputs from test vector: | ||
|
||
| Output | Amount (BTC) | ScriptPubKey | | ||
| ------ | ------------ | ---------------------------------------------------- | | ||
| #1 | 1.2 | 1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac | | ||
| #2 | 2.2 | 1976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac | | ||
|
||
Our transaction structure at this stage is: | ||
|
||
<CodeSnippet | ||
language="text" | ||
highlightLines={[22, 24, 25, 26, 27, 29, 30, 31, 32]} | ||
showLineNumbers={true} | ||
code={`Transaction Breakdown: | ||
═══════════════════════════════════════════════════════════════════════════════════ | ||
Version: 01000000 | ||
Input Count: 02 | ||
input[0]: | ||
Previous TXID: fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f | ||
Output Index: 00000000 | ||
ScriptSig Size: 00 | ||
ScriptSig: | ||
Sequence: eeffffff | ||
input[1]: | ||
Previous TXID: ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a | ||
Output Index: 01000000 | ||
ScriptSig Size: 00 | ||
ScriptSig: | ||
Sequence: ffffffff | ||
Output Count: 02 | ||
output[0]: | ||
Amount: 202cb20600000000 | ||
ScriptPubKey Size: 19 | ||
ScriptPubKey: 76a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac | ||
output[1]: | ||
Amount: 9093510d00000000 | ||
ScriptPubKey Size: 19 | ||
ScriptPubKey: 76a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac | ||
Locktime: 11000000`} | ||
/> | ||
|
||
## Test Vector from BIP-143 | ||
|
||
Let's test our code with the test vector from the <a href="https://github.com/bitcoin/bips/blob/58ffd93812ff25e87d53d1f202fbb389fdfb85bb/bip-0143.mediawiki?plain=1#L146" target="_blank">official BIP143 proposal</a>. | ||
|
||
<div className="flex justify-center items-center w-full full-width"> | ||
<iframe | ||
src="https://trinket.io/embed/python3/e3098585e3c8" | ||
width="100%" | ||
height="100%" | ||
style={{ | ||
border: "none", | ||
margin: 0 | ||
}} | ||
allowFullScreen | ||
className="rounded-md shadow-sm h-[calc(50vh)]" | ||
></iframe> | ||
</div> |
Oops, something went wrong.