From 05881a94507a2b70b26093d48c5361ed3a19fc4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Sun, 17 Mar 2024 17:43:59 +0100 Subject: [PATCH] Persist blocks locally in a file --- .gitignore | 1 + blockchain.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b64..1deed07 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +/blockchain diff --git a/blockchain.py b/blockchain.py index 3f4c191..6f8e38f 100644 --- a/blockchain.py +++ b/blockchain.py @@ -64,6 +64,7 @@ class Block: balances: dict # (sender_pubkey, id) tuples used_transaction_ids: set + block_number: int valid: bool def from_bytes(block_raw): @@ -84,6 +85,7 @@ class Block: own_hash = hashlib.sha256(block_raw).digest(), balances = None, used_transaction_ids = None, + block_number = None, valid = False, ) def validate(self, blockchain): @@ -122,6 +124,7 @@ class Block: if self.valid: self.calculate_balances(prev_block) self.calculate_used_transaction_ids(prev_block) + self.calculate_block_number(prev_block) return self.valid def calculate_balances(self, prev_block): if prev_block is None: @@ -147,6 +150,11 @@ class Block: if t is not None: used_transaction_ids.add((t.sender, t.id)) self.used_transaction_ids = used_transaction_ids + def calculate_block_number(self, prev_block): + if prev_block is None: + self.block_number = 0 + else: + self.block_number = prev_block.block_number + 1 def get_difficulty_info(self, steps, blockchain): if steps == 0: return self.difficulty_sum, self.timestamp @@ -162,8 +170,7 @@ class Block: transaction = 148 * b"\0" else: transaction = self.transaction.get_transaction_raw() - return - transaction + \ + return transaction + \ self.message + \ self.miner_pubkey + \ self.previous_hash + \ @@ -177,6 +184,22 @@ class Blockchain: self.__block_map = {} self.__latest_block_hash = None self.__lock = Lock() + self.__load_blocks_from_disk() + def __load_blocks_from_disk(self): + block_obj = None + try: + with open("blockchain", "rb") as f: + while True: + block = f.read(292) + if len(block) < 292: + break + block_obj = self.add_block(block) + if not block_obj.validate(self): + break + except FileNotFoundError: + pass + if block_obj is not None: + self.set_latest_block(block_obj.own_hash) def set_latest_block(self, block_hash): new_block = self.get_block(block_hash) assert new_block is not None @@ -190,11 +213,33 @@ class Blockchain: new_difficulty_sum = new_block.get_difficulty_info(1, self)[0] if new_difficulty_sum <= current_difficulty_sum: return False + else: + latest_block = None with self.__lock: if self.__latest_block_hash != latest_block_hash: continue self.__latest_block_hash = block_hash + self.__persist_block_update(latest_block, new_block) return True + def __persist_block_update(self, old_block, new_block): + if old_block is not None: + while old_block.block_number > new_block.block_number: + old_block = self.__block_map[old_block.previous_hash] + block_list = [] + while new_block is not old_block: + block_list.append(new_block) + if new_block.previous_hash == 32 * b"\0": + break + new_block = self.__block_map[new_block.previous_hash] + if old_block is not None and old_block.block_number > new_block.block_number: + old_block = self.__block_map[old_block.previous_hash] + start_block_number = block_list[-1].block_number + start_addr = start_block_number * 292 + open_mode = "wb" if start_addr == 0 else "r+b" + with open("blockchain", open_mode) as f: + f.seek(start_addr) + for block in reversed(block_list): + f.write(block.get_block_raw()) def add_block(self, block_raw): with self.__lock: block = Block.from_bytes(block_raw)