comparison src/block.py @ 5:1a8d94b500d8

finish basic parser
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Thu, 21 Oct 2021 18:52:54 +0200
parents e7a84094bf07
children
comparison
equal deleted inserted replaced
4:e7a84094bf07 5:1a8d94b500d8
1 import hashlib 1 from src.helpers import __get_hash
2 from src.helpers import __get_variable_int
2 from src.block_structure import * 3 from src.block_structure import *
3 4
4 5
5 def read_block(f): 6 def read_block(f):
6 """ 7 """
13 14
14 block = Block() 15 block = Block()
15 _ = f.read(4) # Magic number 16 _ = f.read(4) # Magic number
16 _ = f.read(4)[::-1] # Block size 17 _ = f.read(4)[::-1] # Block size
17 header_bytes = f.read(80) 18 header_bytes = f.read(80)
18 block.h = __get_hash(header_bytes) 19 block.block_hash = __get_hash(header_bytes)
19 f.seek(8) 20 f.seek(8)
20 block.header = __get_header(f) 21 block.header = __get_header(f)
21 number_of_transactions = __get_variable_int(f) 22 number_of_transactions = __get_variable_int(f)
23
24 transactions = []
22 for transaction_number in range(number_of_transactions): 25 for transaction_number in range(number_of_transactions):
23 block.transactions.append(__get_transaction(f)) 26 transactions.append(__get_transaction(f))
24 27
25 return block.__dict__ 28 block_dict = block.__dict__
29 block_dict['transactions'] = transactions
30
31 return block_dict
26 32
27 33
28 def __get_header(f): 34 def __get_header(f):
29 """ 35 """
30 Get block header 36 Get block header
48 Get transaction 54 Get transaction
49 :param f: buffer, required 55 :param f: buffer, required
50 :return: dict 56 :return: dict
51 """ 57 """
52 58
59 transaction_data_start = f.tell()
60
53 transaction = Transaction() 61 transaction = Transaction()
54 transaction.version = int.from_bytes(f.read(4)[::-1], 'big') 62 transaction.version = int.from_bytes(f.read(4)[::-1], 'big')
55 number_of_inputs = __get_variable_int(f) 63 number_of_inputs = __get_variable_int(f)
56 64
65 inputs = []
57 for input_number in range(number_of_inputs): 66 for input_number in range(number_of_inputs):
58 transaction_input = TransactionInput() 67 inputs.append(__get_input(f))
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 68
70 number_of_outputs = __get_variable_int(f) 69 number_of_outputs = __get_variable_int(f)
71 70
71 outputs = []
72 for output_number in range(number_of_outputs): 72 for output_number in range(number_of_outputs):
73 transaction_output = TransactionOutput() 73 outputs.append(__get_outputs(f))
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 74
80 transaction.lock_time = int.from_bytes(f.read(4)[::-1], 'little') 75 transaction.lock_time = int.from_bytes(f.read(4)[::-1], 'little')
81 76
82 print(transaction.outputs) 77 transaction_dict = transaction.__dict__
83 print(transaction.inputs) 78 transaction_dict['inputs'] = inputs
84 print(transaction.__dict__) 79 transaction_dict['outputs'] = outputs
85 80
86 return transaction.__dict__ 81 transaction_data_end = f.tell()
82
83 # Get transaction id
84 transaction_data_size = transaction_data_end - transaction_data_start
85 f.seek(transaction_data_start)
86 transaction_data = f.read(transaction_data_size)
87 transaction.id = __get_hash(transaction_data)
88
89 return transaction_dict
87 90
88 91
89 def __get_hash(buffer, bytes_order='backward'): 92 def __get_input(buffer):
90 """ 93 """
91 Compute hash from bytes 94 Get input from transaction data
92 More info about bytes order: https://en.wikipedia.org/wiki/Endianness
93 :param buffer: bytes, required 95 :param buffer: bytes, required
94 :param bytes_order: string, 'backward' or 'forward', optional 96 :return: dict
95 :return: string
96 """ 97 """
97 98
98 h = hashlib.sha256(buffer).digest() 99 transaction_input = TransactionInput()
99 h = hashlib.sha256(h).digest() 100 transaction_input.id = buffer.read(32)[::-1].hex()
100 101
101 if bytes_order == 'backward': 102 if transaction_input.id == '0000000000000000000000000000000000000000000000000000000000000000':
102 h = h[::-1] 103 transaction_input.is_coinbase = True
103 104
104 return h.hex() 105 transaction_input.vout = int.from_bytes(buffer.read(4)[::-1], 'little')
106 script_sig_size = __get_variable_int(buffer)
107 transaction_input.script_sig = buffer.read(script_sig_size).hex()
108 transaction_input.sequence = int.from_bytes(buffer.read(4)[::-1], 'little')
109
110 return transaction_input.__dict__
105 111
106 112
107 def __get_variable_int(f): 113 def __get_outputs(buffer):
108 """ 114 """
109 Get variable int from transaction data 115 Get output from transaction data
110 More info: https://learnmeabitcoin.com/technical/varint 116 :param buffer: bytes, required
111 :param f: buffer, required 117 :return: dict
112 :return: int
113 """ 118 """
114 119
115 first_byte = f.read(1) 120 transaction_output = TransactionOutput()
121 transaction_output.value = float.fromhex(buffer.read(8)[::-1].hex())
122 transaction_output.value /= 100000000 # Satoshis to BTC
123 script_pub_key_size = __get_variable_int(buffer)
124 transaction_output.script_pub_key = buffer.read(script_pub_key_size).hex()
116 125
117 if first_byte == b'\xfd': 126 return transaction_output.__dict__
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')