comparison src/block.py @ 4:e7a84094bf07

refactor code
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Wed, 20 Oct 2021 19:36:39 +0200
parents 3d83609e12a1
children 1a8d94b500d8
comparison
equal deleted inserted replaced
3:3d83609e12a1 4:e7a84094bf07
1 import hashlib 1 import hashlib
2 from src.helpers import read_bytes 2 from src.block_structure import *
3 from src.helpers import get_variable_int
4 3
5 4
6 class Block: 5 def read_block(f):
7 """
8 Block structure
9 """
10
11 block_hash = str()
12 magic_number = int()
13 size = int()
14 number_of_transactions = int()
15 transactions = []
16
17 class Header:
18 version = int()
19 previous_block_hash = str()
20 merkle_root = str()
21 timestamp = int() # Epoch Unix time
22 difficult_target = int() # Bits
23 nonce = int()
24
25
26 class Transaction:
27 id = str()
28 version = int()
29 number_of_inputs = int()
30 inputs = []
31 number_of_outputs = int()
32 outputs = []
33
34 class TransactionInput:
35 id = str()
36 is_coinbase = False
37 vout = int()
38 script_sig_size = int()
39 script_sig = str()
40 sequence = int()
41
42 class TransactionOutput:
43 value = float()
44 script_pub_key_size = int()
45 script_pub_key = str()
46
47
48 def read_block(file):
49 """ 6 """
50 Deserialize block 7 Deserialize block
51 More info about block structure: https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch09.asciidoc 8 More info about block structure: https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch09.asciidoc
52 :param file: <class '_io.BufferedReader'>, required 9 More info about bytes order: https://en.wikipedia.org/wiki/Endianness
10 :param f: buffer, required
53 :return: 11 :return:
54 """ 12 """
55 13
56 block = Block() 14 block = Block()
57 block.magic_number = int.from_bytes(read_bytes(file, 4), 'big') 15 _ = f.read(4) # Magic number
58 block.size = int.from_bytes(read_bytes(file, 4), 'big') 16 _ = f.read(4)[::-1] # Block size
17 header_bytes = f.read(80)
18 block.h = __get_hash(header_bytes)
19 f.seek(8)
20 block.header = __get_header(f)
21 number_of_transactions = __get_variable_int(f)
22 for transaction_number in range(number_of_transactions):
23 block.transactions.append(__get_transaction(f))
59 24
60 # Compute block hash 25 return block.__dict__
61 header_bytes = read_bytes(file, 80, 'forward')
62 block_hash = hashlib.sha256(header_bytes).digest()
63 block_hash = hashlib.sha256(block_hash).digest()
64 26
65 # Read block header
66 header = block.Header()
67 header.block_hash = block_hash[::-1].hex()
68 header.version = int.from_bytes(header_bytes[:4], 'little')
69 header.previous_block_hash = header_bytes[4:36][::-1].hex()
70 header.merkle_root = header_bytes[36:68][::-1].hex()
71 header.timestamp = int.from_bytes(header_bytes[68:72], 'little')
72 header.difficult_target = int.from_bytes(header_bytes[72:76], 'little')
73 header.nonce = int.from_bytes(header_bytes[76:80], 'little')
74 27
75 # Number of transactions (varInt) 28 def __get_header(f):
76 block.number_of_transactions = get_variable_int(file) 29 """
30 Get block header
31 :param f: buffer, required
32 :return: dict
33 """
77 34
78 for transaction_number in range(block.number_of_transactions): 35 header = Header()
79 transaction = Transaction() 36 header.version = int.from_bytes(f.read(4), 'little')
80 transaction.version = int.from_bytes(read_bytes(file, 4), 'big') 37 header.previous_block_hash = f.read(32)[::-1].hex()
81 transaction.number_of_inputs = get_variable_int(file) 38 header.merkle_root = f.read(32)[::-1].hex()
39 header.timestamp = int.from_bytes(f.read(4), 'little')
40 header.bits = int.from_bytes(f.read(4), 'little')
41 header.nonce = int.from_bytes(f.read(4), 'little')
82 42
83 for input_number in range(transaction.number_of_inputs): 43 return header.__dict__
84 transaction_input = transaction.TransactionInput()
85 transaction_input.id = read_bytes(file, 32).hex()
86 if transaction_input.id == '0000000000000000000000000000000000000000000000000000000000000000':
87 transaction_input.is_coinbase = True
88 44
89 transaction_input.vout = int.from_bytes(read_bytes(file, 4), 'little')
90 transaction_input.script_sig_size = get_variable_int(file)
91 transaction_input.script_sig = read_bytes(file, transaction_input.script_sig_size, 'forward').hex()
92 transaction_input.sequence = int.from_bytes(read_bytes(file, 4), 'little')
93 45
94 transaction.number_of_outputs = get_variable_int(file) 46 def __get_transaction(f):
47 """
48 Get transaction
49 :param f: buffer, required
50 :return: dict
51 """
95 52
96 for output_number in range(transaction.number_of_outputs): 53 transaction = Transaction()
97 transaction_output = transaction.TransactionOutput() 54 transaction.version = int.from_bytes(f.read(4)[::-1], 'big')
98 transaction_output.value = float.fromhex(read_bytes(file, 8).hex()) 55 number_of_inputs = __get_variable_int(f)
99 transaction_output.value /= 100000000 # Satoshis to BTC 56
57 for input_number in range(number_of_inputs):
58 transaction_input = TransactionInput()
59 transaction_input.id = f.read(32)[::-1].hex()
60
61 if transaction_input.id == '0000000000000000000000000000000000000000000000000000000000000000':
62 transaction_input.is_coinbase = True
63
64 transaction_input.vout = int.from_bytes(f.read(4)[::-1], 'little')
65 script_sig_size = __get_variable_int(f)
66 transaction_input.script_sig = f.read(script_sig_size).hex()
67 transaction_input.sequence = int.from_bytes(f.read(4)[::-1], 'little')
68 transaction.inputs.append(transaction_input.__dict__)
69
70 number_of_outputs = __get_variable_int(f)
71
72 for output_number in range(number_of_outputs):
73 transaction_output = TransactionOutput()
74 transaction_output.value = float.fromhex(f.read(8)[::-1].hex())
75 transaction_output.value /= 100000000 # Satoshis to BTC
76 script_pub_key_size = __get_variable_int(f)
77 transaction_output.script_pub_key = f.read(script_pub_key_size)
78 transaction.outputs.append(transaction_output.__dict__)
79
80 transaction.lock_time = int.from_bytes(f.read(4)[::-1], 'little')
81
82 print(transaction.outputs)
83 print(transaction.inputs)
84 print(transaction.__dict__)
85
86 return transaction.__dict__
87
88
89 def __get_hash(buffer, bytes_order='backward'):
90 """
91 Compute hash from bytes
92 More info about bytes order: https://en.wikipedia.org/wiki/Endianness
93 :param buffer: bytes, required
94 :param bytes_order: string, 'backward' or 'forward', optional
95 :return: string
96 """
97
98 h = hashlib.sha256(buffer).digest()
99 h = hashlib.sha256(h).digest()
100
101 if bytes_order == 'backward':
102 h = h[::-1]
103
104 return h.hex()
105
106
107 def __get_variable_int(f):
108 """
109 Get variable int from transaction data
110 More info: https://learnmeabitcoin.com/technical/varint
111 :param f: buffer, required
112 :return: int
113 """
114
115 first_byte = f.read(1)
116
117 if first_byte == b'\xfd':
118 variable_int_bytes = f.read(2)[::-1]
119 elif first_byte == b'\xfe':
120 variable_int_bytes = f.read(4)[::-1]
121 elif first_byte == b'\xff':
122 variable_int_bytes = f.read(8)[::-1]
123 else:
124 variable_int_bytes = first_byte
125
126 return int.from_bytes(variable_int_bytes, 'little')