Persist blocks locally in a file
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
/blockchain
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class Block:
|
|||||||
balances: dict
|
balances: dict
|
||||||
# (sender_pubkey, id) tuples
|
# (sender_pubkey, id) tuples
|
||||||
used_transaction_ids: set
|
used_transaction_ids: set
|
||||||
|
block_number: int
|
||||||
valid: bool
|
valid: bool
|
||||||
|
|
||||||
def from_bytes(block_raw):
|
def from_bytes(block_raw):
|
||||||
@@ -84,6 +85,7 @@ class Block:
|
|||||||
own_hash = hashlib.sha256(block_raw).digest(),
|
own_hash = hashlib.sha256(block_raw).digest(),
|
||||||
balances = None,
|
balances = None,
|
||||||
used_transaction_ids = None,
|
used_transaction_ids = None,
|
||||||
|
block_number = None,
|
||||||
valid = False,
|
valid = False,
|
||||||
)
|
)
|
||||||
def validate(self, blockchain):
|
def validate(self, blockchain):
|
||||||
@@ -122,6 +124,7 @@ class Block:
|
|||||||
if self.valid:
|
if self.valid:
|
||||||
self.calculate_balances(prev_block)
|
self.calculate_balances(prev_block)
|
||||||
self.calculate_used_transaction_ids(prev_block)
|
self.calculate_used_transaction_ids(prev_block)
|
||||||
|
self.calculate_block_number(prev_block)
|
||||||
return self.valid
|
return self.valid
|
||||||
def calculate_balances(self, prev_block):
|
def calculate_balances(self, prev_block):
|
||||||
if prev_block is None:
|
if prev_block is None:
|
||||||
@@ -147,6 +150,11 @@ class Block:
|
|||||||
if t is not None:
|
if t is not None:
|
||||||
used_transaction_ids.add((t.sender, t.id))
|
used_transaction_ids.add((t.sender, t.id))
|
||||||
self.used_transaction_ids = used_transaction_ids
|
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):
|
def get_difficulty_info(self, steps, blockchain):
|
||||||
if steps == 0:
|
if steps == 0:
|
||||||
return self.difficulty_sum, self.timestamp
|
return self.difficulty_sum, self.timestamp
|
||||||
@@ -162,8 +170,7 @@ class Block:
|
|||||||
transaction = 148 * b"\0"
|
transaction = 148 * b"\0"
|
||||||
else:
|
else:
|
||||||
transaction = self.transaction.get_transaction_raw()
|
transaction = self.transaction.get_transaction_raw()
|
||||||
return
|
return transaction + \
|
||||||
transaction + \
|
|
||||||
self.message + \
|
self.message + \
|
||||||
self.miner_pubkey + \
|
self.miner_pubkey + \
|
||||||
self.previous_hash + \
|
self.previous_hash + \
|
||||||
@@ -177,6 +184,22 @@ class Blockchain:
|
|||||||
self.__block_map = {}
|
self.__block_map = {}
|
||||||
self.__latest_block_hash = None
|
self.__latest_block_hash = None
|
||||||
self.__lock = Lock()
|
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):
|
def set_latest_block(self, block_hash):
|
||||||
new_block = self.get_block(block_hash)
|
new_block = self.get_block(block_hash)
|
||||||
assert new_block is not None
|
assert new_block is not None
|
||||||
@@ -190,11 +213,33 @@ class Blockchain:
|
|||||||
new_difficulty_sum = new_block.get_difficulty_info(1, self)[0]
|
new_difficulty_sum = new_block.get_difficulty_info(1, self)[0]
|
||||||
if new_difficulty_sum <= current_difficulty_sum:
|
if new_difficulty_sum <= current_difficulty_sum:
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
latest_block = None
|
||||||
with self.__lock:
|
with self.__lock:
|
||||||
if self.__latest_block_hash != latest_block_hash:
|
if self.__latest_block_hash != latest_block_hash:
|
||||||
continue
|
continue
|
||||||
self.__latest_block_hash = block_hash
|
self.__latest_block_hash = block_hash
|
||||||
|
self.__persist_block_update(latest_block, new_block)
|
||||||
return True
|
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):
|
def add_block(self, block_raw):
|
||||||
with self.__lock:
|
with self.__lock:
|
||||||
block = Block.from_bytes(block_raw)
|
block = Block.from_bytes(block_raw)
|
||||||
|
|||||||
Reference in New Issue
Block a user