changeset 8:4d259e84160d

fix OverFlow bug
author Dennis Concepcion Martin <dennisconcepcionmartin@gmail.com>
date Sun, 24 Oct 2021 17:38:23 +0200
parents e4afde8d5a7e
children e218f70e19e9
files main.py src/puppy/block.py src/puppy/block_structure.py src/puppy/helpers.py
diffstat 4 files changed, 196 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/main.py	Sun Oct 24 15:19:56 2021 +0200
+++ b/main.py	Sun Oct 24 17:38:23 2021 +0200
@@ -1,8 +1,15 @@
-def main():
-    file_path = '/Users/dennis/Bitcoin/blocks/blk00000.dat'
+import os
+from puppy.block import deserialize_block
+
 
-    with open(file_path, 'rb') as f:
-        pass
+def main():
+    filename = '/Users/dennis/Bitcoin/blocks/blk00000.dat'
+    file_size = os.path.getsize(filename)
+    print('File size in bytes: {}'. format(file_size))
+
+    with open(filename, 'rb') as f:
+        while f.tell() < file_size:
+            block = deserialize_block(f)
 
 
 if __name__ == '__main__':
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/puppy/block.py	Sun Oct 24 17:38:23 2021 +0200
@@ -0,0 +1,108 @@
+from puppy.block_structure import *
+from puppy.helpers import get_var_int
+from puppy.helpers import compute_hash
+
+"""
+Deserialize methods
+"""
+
+
+def deserialize_block(f):
+    """
+    Deserialize block
+    :param f: buffer, required
+    :return: dict
+    """
+
+    block = Block()
+    block.magic_number = f.read(4).hex()
+    block.block_size = f.read(4)[::-1].hex()
+    block_header, block.block_hash = deserialize_header(f)
+    block.number_of_transactions = get_var_int(f)
+
+    transactions = []
+    for transaction_number in range(int(block.number_of_transactions, 16)):
+        transactions.append(deserialize_transaction_data(f, block.block_size))
+
+    block_dict = block.__dict__
+    block_dict['header'] = block_header
+    block_dict['transactions'] = transactions
+
+    return block_dict
+
+
+def deserialize_header(f):
+    """
+    Deserialize block header
+    More info: https://learnmeabitcoin.com/technical/block-header
+    :param f: buffer, required
+    :return: (dict, string)
+    """
+
+    # Compute block hash
+    before = f.tell()
+    header = f.read(80)
+    block_hash = compute_hash(header)
+    f.seek(before)
+
+    header = Header()
+    header.version = f.read(4)[::-1].hex()
+    header.previous_block_hash = f.read(32)[::-1].hex()
+    header.merkle_root = f.read(32)[::-1].hex()
+    header.time = f.read(4)[::-1].hex()
+    header.bits = f.read(4)[::-1].hex()
+    header.nonce = f.read(4)[::-1].hex()
+
+    return header.__dict__, block_hash
+
+
+def deserialize_transaction_data(f, block_size):
+    """
+    Deserialize transaction data
+    More info: https://learnmeabitcoin.com/technical/transaction-data
+    :param f: buffer, required
+    :param block_size: string, required
+    :return: dict
+    """
+
+    start_transaction_data = f.tell()
+
+    transaction = Transaction()
+    transaction.version = f.read(4)[::-1].hex()
+    transaction.number_of_inputs = get_var_int(f)
+
+    transaction_inputs = []
+    for input_number in range(int(transaction.number_of_inputs, 16)):
+        transaction_input = TransactionInput()
+        transaction_input.id = f.read(32)[::-1].hex()
+        transaction_input.vout = f.read(4)[::-1].hex()
+        transaction_input.script_sig_size = get_var_int(f)
+        transaction_input.script_sig = f.read(int(transaction_input.script_sig_size, 16)).hex()
+        transaction_input.sequence = f.read(4)[::-1].hex()
+        transaction_inputs.append(transaction_input.__dict__)
+
+    transaction.number_of_outputs = get_var_int(f)
+
+    transaction_outputs = []
+    for output_number in range(int(transaction.number_of_outputs, 16)):
+        transaction_output = TransactionOutput()
+        transaction_output.value = f.read(8)[::-1].hex()
+        transaction_output.script_pub_key_size = get_var_int(f)
+        transaction_output.script_pub_key = f.read(int(transaction_output.script_pub_key_size, 16)).hex()
+        transaction_outputs.append(transaction_output.__dict__)
+
+    transaction.lock_time = f.read(4)[::-1].hex()
+
+    # Compute transaction id
+    end_transaction_data = f.tell()
+    transaction_data_size = end_transaction_data - start_transaction_data
+    f.seek(start_transaction_data)
+    transaction_data = f.read(transaction_data_size)
+    f.seek(end_transaction_data)
+    transaction.id = compute_hash(transaction_data)
+
+    transaction_dict = transaction.__dict__
+    transaction_dict['inputs'] = transaction_inputs
+    transaction_dict['outputs'] = transaction_outputs
+
+    return transaction_dict
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/puppy/block_structure.py	Sun Oct 24 17:38:23 2021 +0200
@@ -0,0 +1,36 @@
+class Block:
+    block_hash = str()
+    magic_number = str()
+    block_size = str()
+    number_of_transactions = str()
+
+
+class Header:
+    version = str()
+    previous_block_hash = str()
+    merkle_root = str()
+    time = str()
+    bits = str()
+    nonce = str()
+
+
+class Transaction:
+    id = str()
+    version = str()
+    number_of_inputs = str()
+    number_of_outputs = str()
+    lock_time = str()
+
+
+class TransactionInput:
+    id = str()
+    vout = str()
+    script_sig_size = str()
+    script_sig = str()
+    sequence = str()
+
+
+class TransactionOutput:
+    value = str()
+    script_pub_key_size = str()
+    script_pub_key = str()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/puppy/helpers.py	Sun Oct 24 17:38:23 2021 +0200
@@ -0,0 +1,41 @@
+import hashlib
+
+"""
+Helper methods
+"""
+
+
+def get_var_int(f):
+    """
+    A VarInt (variable integer) is a field used in transaction data to indicate the number of upcoming fields,
+    or the length of an upcoming field.
+    More info: https://learnmeabitcoin.com/technical/varint
+    :param f: buffer, required
+    :return: string
+    """
+
+    prefix = f.read(1).hex()
+
+    if int(prefix, 16) == 253:
+        number_of_transactions = f.read(2)[::-1].hex()
+    elif int(prefix, 16) == 254:
+        number_of_transactions = f.read(4)[::-1].hex()
+    elif int(prefix, 16) == 255:
+        number_of_transactions = f.read(8)[::-1].hex()
+    else:
+        number_of_transactions = prefix
+
+    return number_of_transactions
+
+
+def compute_hash(data):
+    """
+    Get hash
+    :param data: bytes, required
+    :return: string
+    """
+
+    h = hashlib.sha256(data).digest()
+    h = hashlib.sha256(h).digest()
+
+    return h[::-1].hex()
\ No newline at end of file