Skip to content

Commit 18850ba

Browse files
committed
Merge branch 'master' of https://github.com/abhigit-saha/python-bitcoin-utils into abhigit-saha-master
2 parents 08fb3d8 + 8102c64 commit 18850ba

1 file changed

Lines changed: 338 additions & 0 deletions

File tree

examples/create_and_mine_block.py

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import time
5+
from bitcoinutils.setup import setup
6+
from bitcoinutils.transactions import Transaction, TxInput, TxOutput, TxWitnessInput
7+
from bitcoinutils.script import Script
8+
from bitcoinutils.keys import P2trAddress
9+
from bitcoinutils.utils import to_satoshis
10+
from bitcoinutils.block import Block, BlockHeader
11+
import hashlib
12+
13+
14+
def calculate_merkle_root(txid_list):
15+
"""
16+
Calculates merkel root by hashing pairwise txids till only 1 is left.
17+
18+
Args:
19+
txid_list: List of the transaction ids in the block
20+
21+
Returns:
22+
(bytes): The merkle root of the block.
23+
"""
24+
if len(txid_list) == 1:
25+
return bytes.fromhex(txid_list[0])
26+
27+
# Convert each txid from big-endian hex to little-endian bytes.
28+
current_layer = [bytes.fromhex(txid)[::-1] for txid in txid_list]
29+
30+
while len(current_layer) > 1:
31+
next_layer = []
32+
for i in range(0, len(current_layer), 2):
33+
# If there's an odd number, duplicate the last one.
34+
if i + 1 < len(current_layer):
35+
pair = current_layer[i] + current_layer[i + 1]
36+
else:
37+
pair = current_layer[i] + current_layer[i]
38+
# Perform double SHA-256 on the concatenated pair.
39+
next_layer.append(hashlib.sha256(hashlib.sha256(pair).digest()).digest())
40+
current_layer = next_layer
41+
42+
# Reverse back to big-endian and return the bytes.
43+
return current_layer[0][::-1]
44+
45+
46+
def calculate_witness_root_hash(wtxid_list):
47+
"""
48+
Calculates witness root hash by hashing pairwise wtxids till only 1 is left.
49+
50+
Args:
51+
wtxid_list: List of the witness transaction ids in the block
52+
53+
Returns:
54+
(bytes): The witness root hash of the block.
55+
"""
56+
if len(wtxid_list) == 1:
57+
# For a single txid, return its bytes (big-endian) representation.
58+
return bytes.fromhex(wtxid_list[0])
59+
60+
# Convert each txid from big-endian hex to little-endian bytes.
61+
current_layer = [bytes.fromhex(wtxid)[::-1] for wtxid in wtxid_list]
62+
63+
while len(current_layer) > 1:
64+
next_layer = []
65+
for i in range(0, len(current_layer), 2):
66+
# If there's an odd number, duplicate the last one.
67+
if i + 1 < len(current_layer):
68+
pair = current_layer[i] + current_layer[i + 1]
69+
else:
70+
pair = current_layer[i] + current_layer[i]
71+
# Perform double SHA-256 on the concatenated pair.
72+
next_layer.append(hashlib.sha256(hashlib.sha256(pair).digest()).digest())
73+
current_layer = next_layer
74+
75+
return current_layer[0]
76+
77+
78+
def calculate_witness_commitment(witness_root_hash, witness_reserved_value):
79+
"""
80+
Calculates the witness commitment by performing a double SHA-256
81+
on the concatenation of the witness_root_hash and witness_reserved_value.
82+
83+
Args:
84+
witness_root_hash(hex): witness root hash of the block
85+
witness_reserved_value(hex): the reserved value stored in the witness.
86+
87+
Returns:
88+
(hex): hex of the witness commitment
89+
90+
"""
91+
# Convert both hex strings to bytes and concatenate them
92+
combined = bytes.fromhex(witness_root_hash + witness_reserved_value)
93+
94+
# Perform double SHA256 hashing
95+
hash1 = hashlib.sha256(combined).digest()
96+
hash2 = hashlib.sha256(hash1).digest()
97+
return hash2.hex()
98+
99+
100+
def create_block(coinbase_tx, tx1):
101+
# Note: Here I have for simplicity taken just the coinbase tx and another transaction (the test_tx the mempool),
102+
# more generally we just take the list of tx.
103+
104+
txid_list = [coinbase_tx.get_txid(), tx1.get_txid()]
105+
merkle_root = calculate_merkle_root(txid_list)
106+
prev_block_hash = bytes.fromhex(
107+
"0000000000000000000000000000000000000000000000000000000000000000"
108+
)
109+
# version https://learnmeabitcoin.com/technical/block/version/
110+
version = "40000000"
111+
version = int.from_bytes(bytes.fromhex(version), byteorder="little")
112+
timestamp = int(time.time())
113+
# resource: https://learnmeabitcoin.com/technical/block/bits/
114+
bits = "1f00ffff"
115+
bits = int.from_bytes(bytes.fromhex(bits), byteorder="big")
116+
117+
nonce = 0
118+
119+
block_header = BlockHeader(
120+
version,
121+
previous_block_hash=prev_block_hash,
122+
merkle_root=merkle_root,
123+
timestamp=timestamp,
124+
target_bits=bits,
125+
nonce=nonce,
126+
)
127+
magic = bytes.fromhex("f9beb4d9")
128+
block_size = None
129+
transaction_count = len([coinbase_tx, tx1])
130+
block = Block(
131+
magic=magic,
132+
block_size=block_size,
133+
header=block_header,
134+
transactions=[coinbase_tx, tx1],
135+
transaction_count=transaction_count,
136+
)
137+
return block
138+
139+
140+
def mine_block(block_header_bytes, target_hex):
141+
"""
142+
Mine a block by iterating through nonce values.
143+
144+
Args:
145+
block_header_bytes (bytes): The 80-byte block header with a placeholder nonce.
146+
target_hex (str): The difficulty target as a hex string (256-bit number).
147+
148+
Returns:
149+
(int, bytes): The nonce value and the corresponding block header hash that meets the target.
150+
"""
151+
target_int = int(target_hex, 16)
152+
print("Target (int):", target_int)
153+
154+
for nonce in range(2**32):
155+
# Replace the last 4 bytes of the header with the current nonce in little-endian format.
156+
header_with_nonce = block_header_bytes[:-4] + nonce.to_bytes(
157+
4, byteorder="little"
158+
)
159+
160+
hash_once = hashlib.sha256(header_with_nonce).digest()
161+
hash_twice = hashlib.sha256(hash_once).digest()
162+
163+
# Bitcoin displays the block hash in big-endian order.
164+
block_hash = hash_twice[::-1]
165+
block_hash_int = int.from_bytes(block_hash, byteorder="big")
166+
167+
if block_hash_int < target_int:
168+
print("Success! Nonce found:", nonce)
169+
return nonce, block_hash
170+
171+
# Optional: print progress every so often
172+
if nonce % 1000000 == 0:
173+
print("Tried nonce:", nonce)
174+
175+
return None, None
176+
177+
178+
def main():
179+
180+
# mock transaction details, this transaction would be the first transaction
181+
# of the block after the coinbase transaction
182+
tx_details = {
183+
"txid": "00000a2d1a9e29116b539b85b6e893213b1ed95a08b7526a8d59a4b088fc6571",
184+
"version": 1,
185+
"locktime": 0,
186+
"vin": [
187+
{
188+
"txid": "2e4843d552ca9487efd9e69c0359f05375b7de5449eb49510d17a25bb5b15ec0",
189+
"vout": 1,
190+
"prevout": {
191+
"scriptpubkey": "512065fd3d423ea46a70505248db989e7302bfbbdd64ee4193dd9a59f69894f0de48",
192+
"scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 65fd3d423ea46a70505248db989e7302bfbbdd64ee4193dd9a59f69894f0de48",
193+
"scriptpubkey_type": "v1_p2tr",
194+
"scriptpubkey_address": "bc1pvh7n6s375348q5zjfrde38nnq2lmhhtyaeqe8hv6t8mf398smeyqnug47s",
195+
"value": 13413
196+
},
197+
"scriptsig": "",
198+
"scriptsig_asm": "",
199+
"witness": [
200+
"29783b151d376d5178451ce14f62b091059021680bff36aec2814e33ecacf130e8aa92d6da23f35be7a8c2245b8f910261d4e6a5169f79d6ff7a3f412981f486"
201+
],
202+
"is_coinbase": False,
203+
"sequence": 1610616404
204+
}
205+
],
206+
"vout": [
207+
{
208+
"scriptpubkey": "51204b918d31f22461021ed54e354ac9dcbbe94b98edcfd3615b76c068b08222a87f",
209+
"scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 4b918d31f22461021ed54e354ac9dcbbe94b98edcfd3615b76c068b08222a87f",
210+
"scriptpubkey_type": "v1_p2tr",
211+
"scriptpubkey_address": "bc1pfwgc6v0jy3ssy8k4fc654jwuh055hx8delfkzkmkcp5tpq3z4pls7tx8q3",
212+
"value": 2908
213+
},
214+
{
215+
"scriptpubkey": "512065fd3d423ea46a70505248db989e7302bfbbdd64ee4193dd9a59f69894f0de48",
216+
"scriptpubkey_asm": "OP_PUSHNUM_1 OP_PUSHBYTES_32 65fd3d423ea46a70505248db989e7302bfbbdd64ee4193dd9a59f69894f0de48",
217+
"scriptpubkey_type": "v1_p2tr",
218+
"scriptpubkey_address": "bc1pvh7n6s375348q5zjfrde38nnq2lmhhtyaeqe8hv6t8mf398smeyqnug47s",
219+
"value": 8503
220+
}
221+
],
222+
"size": 205,
223+
"weight": 616,
224+
"fee": 2002,
225+
"status": {
226+
"confirmed": True,
227+
"block_height": 834552,
228+
"block_hash": "00000000000000000001dd0468a70c94f619251d286585cff57aeb4bd9ede330",
229+
"block_time": 1710355598
230+
},
231+
"hex": "01000000000101c05eb1b55ba2170d5149eb4954deb77553f059039ce6d9ef8794ca52d543482e0100000000540e0060025c0b0000000000002251204b918d31f22461021ed54e354ac9dcbbe94b98edcfd3615b76c068b08222a87f372100000000000022512065fd3d423ea46a70505248db989e7302bfbbdd64ee4193dd9a59f69894f0de48014029783b151d376d5178451ce14f62b091059021680bff36aec2814e33ecacf130e8aa92d6da23f35be7a8c2245b8f910261d4e6a5169f79d6ff7a3f412981f48600000000"
232+
}
233+
234+
235+
setup("mainnet")
236+
# mock requirements
237+
from_txid = "0000000000000000000000000000000000000000000000000000000000000000"
238+
to_addr = "bc1pvh7n6s375348q5zjfrde38nnq2lmhhtyaeqe8hv6t8mf398smeyqnug47s"
239+
to_addr = P2trAddress(to_addr)
240+
241+
# First create a coinbase transaction which is the first transaction of the block
242+
# The witness reserved value is a 32-byte value that is reserved for future use.
243+
# and the coinbase transaction must have it in its input's witness
244+
witness_reserved_value = (
245+
"0000000000000000000000000000000000000000000000000000000000000000"
246+
)
247+
248+
# Constructing the coinbase transaction
249+
tx1 = Transaction()
250+
tx1 = tx1.from_raw(tx_details["hex"])
251+
txinp = TxInput(
252+
txid=from_txid,
253+
txout_index=0,
254+
script_sig=Script([witness_reserved_value]),
255+
)
256+
257+
# Witness stack contains the list of witness data for the transaction
258+
# learn more: https://learnmeabitcoin.com/technical/upgrades/segregated-witness/
259+
witness_stack = [witness_reserved_value]
260+
261+
# wtxid is different from txid
262+
wtxid = tx1.get_wtxid();
263+
print("Wtx id :", wtxid)
264+
265+
# Coinbase wtxid must be set to all zeros to avoid circular reference
266+
# Learn more: https://learnmeabitcoin.com/technical/transaction/wtxid/
267+
coinbase_wtxid = "0" * 64
268+
269+
witness_root_hash = calculate_witness_root_hash([coinbase_wtxid, wtxid])
270+
witness_root_hash = witness_root_hash.hex()
271+
print("Witness root hash ", witness_root_hash)
272+
273+
# Calculating the witness commitment hash (double SHA256 of witness_root_hash and witness_reserved_value)
274+
witness_commitment_hash = calculate_witness_commitment(
275+
witness_root_hash, witness_reserved_value
276+
)
277+
278+
# The commitment is constructed as:
279+
# commitment = "6a24aa21a9ed" + doublesha256(witness_root_hash, witness_reserved_value)
280+
# where commitment is made of (source BIP 141):
281+
# 1-byte - OP_RETURN (0x6a)
282+
# 1-byte - Push the following 36 bytes (0x24)
283+
# 4-byte - Commitment header (0xaa21a9ed)
284+
# 32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value)
285+
commitment = "6a24"+ "aa21a9ed" + witness_commitment_hash
286+
287+
print("Witness Commitment :", commitment)
288+
# note: to_addr defined at the top. Taken from the first transaction
289+
# coinbase transactions must contain at least two outputs
290+
# the first output is the block reward and the second output is the commitment
291+
# The block reward is reward for the miner for mining the block + fees
292+
# block reward = block subsidy (currently 3.125 BTC) + transaction fees
293+
# Learn more: https://learnmeabitcoin.com/technical/mining/block-reward/
294+
txout1 = TxOutput(to_satoshis(3.125+0.01), to_addr.to_script_pub_key())
295+
296+
# The commitment script is described above
297+
# and is added to the second output of the coinbase transaction
298+
witness_commitment_script = Script(["OP_RETURN", "aa21a9ed"+witness_commitment_hash]);
299+
print("witness commitment script : ", witness_commitment_script)
300+
txout2 = TxOutput(to_satoshis(0), witness_commitment_script)
301+
coinbase_tx = Transaction(
302+
[txinp],
303+
[txout1, txout2],
304+
has_segwit=True,
305+
witnesses=[TxWitnessInput(witness_stack)],
306+
)
307+
# Example difficulty target
308+
# learn more: https://learnmeabitcoin.com/technical/mining/target/
309+
# Note, this is even higher then the genesis block example
310+
# to prevent the mining process from running for a long time
311+
difficulty_target = (
312+
"0000ffff00000000000000000000000000000000000000000000000000000000"
313+
)
314+
# Creating a block that includes the coinbase transaction and tx1.
315+
block = create_block(coinbase_tx, tx1)
316+
317+
print("block header: ", block.header)
318+
# getting the block header bytes
319+
# Note: only the header is hashed and compared to the difficulty in mining
320+
serialized_header = block.header.serialize_header()
321+
print("Serialized header : ", serialized_header)
322+
323+
# mining the block using mine_block returns the nonce
324+
# nonce is the value in the header that can be changed iteratively
325+
# to get a hash that is less than the target
326+
nonce, mined_hash = mine_block(serialized_header, difficulty_target)
327+
print("NONCE: ", nonce)
328+
329+
# Replace the last 4 bytes of the header with the current nonce in little-endian format.
330+
serialized_header = serialized_header[:-4] + nonce.to_bytes(4, byteorder="little")
331+
332+
print("hex of serialized header ", serialized_header.hex())
333+
print("coinbase transaction :", coinbase_tx.to_hex())
334+
print("Block: ", block)
335+
336+
337+
if __name__ == "__main__":
338+
main()

0 commit comments

Comments
 (0)