Implement exchange of open transactions
This commit is contained in:
@@ -50,6 +50,11 @@ class Transaction:
|
||||
self.amount.to_bytes(8, "big") + \
|
||||
self.transaction_fee.to_bytes(8, "big") + \
|
||||
self.signature
|
||||
def sorting_id(self):
|
||||
return (-self.transaction_fee, self.sender, self.id)
|
||||
def __eq__(self, other):
|
||||
return (self.id, self.sender, self.receiver, self.amount, self.transaction_fee) == \
|
||||
(other.id, other.sender, other.receiver, other.amount, other.transaction_fee)
|
||||
|
||||
@dataclass
|
||||
class Block:
|
||||
@@ -178,6 +183,82 @@ class Block:
|
||||
self.difficulty_sum.to_bytes(32, "big") + \
|
||||
self.nonce.to_bytes(8, "big")
|
||||
|
||||
class OpenTransactions:
|
||||
def __init__(self, blockchain):
|
||||
self.__blockchain = blockchain
|
||||
self.__open_transactions = []
|
||||
self.__lock = Lock()
|
||||
self.__recalculate_hashes()
|
||||
def add(self, transaction):
|
||||
assert transaction.is_valid()
|
||||
with self.__lock:
|
||||
# pre-check 1: Check for duplicates
|
||||
for existing_transaction in self.__open_transactions:
|
||||
if transaction == existing_transaction:
|
||||
return
|
||||
# pre-check 2: Check if there is space
|
||||
if not self.__has_space(transaction):
|
||||
return
|
||||
# Add the transaction
|
||||
self.__open_transactions.append(transaction)
|
||||
self.__cleanup()
|
||||
def update(self):
|
||||
with self.__lock:
|
||||
self.__cleanup()
|
||||
def get_hash(self, i):
|
||||
assert i < 1024
|
||||
with self.__lock:
|
||||
return self.__hashes[i]
|
||||
def get_transaction(self, i):
|
||||
with self.__lock:
|
||||
if i >= len(self.__open_transactions):
|
||||
return None
|
||||
return self.__open_transactions[i]
|
||||
def __has_space(self, transaction):
|
||||
if len(self.__open_transactions) < 1000:
|
||||
return True
|
||||
return transaction.sorting_id() < self.__open_transactions[-1].sorting_id()
|
||||
def __cleanup(self):
|
||||
# sort everything
|
||||
self.__open_transactions.sort(key = Transaction.sorting_id)
|
||||
# drop out invalid ones
|
||||
# - reused ids
|
||||
# - paying more money than available
|
||||
latest_block = self.__blockchain.get_latest_block()
|
||||
if latest_block is None:
|
||||
self.__open_transactions = []
|
||||
self.__recalculate_hashes()
|
||||
return
|
||||
used_transaction_ids = latest_block.used_transaction_ids.copy()
|
||||
balances = latest_block.balances.copy()
|
||||
def is_valid(transaction):
|
||||
sender_tuple = (transaction.sender, transation.id)
|
||||
if sender_tuple in used_transaction_ids:
|
||||
return False
|
||||
balance = balances.get(transaction.sender) or 0
|
||||
if transaction.amount + transaction.transaction_fee > balance:
|
||||
return False
|
||||
used_transaction_ids.add(sender_tuple)
|
||||
balances[transaction.sender] = balance - transaction.amount - transaction.transaction_fee
|
||||
return True
|
||||
self.__open_transactions = [transaction for transaction in self.__open_transactions if is_valid(transaction)]
|
||||
# limit to 1024
|
||||
self.__open_transactions = self.__open_transactions[0:1024]
|
||||
self.__recalculate_hashes()
|
||||
def __recalculate_hashes(self):
|
||||
self.__hashes = []
|
||||
current_hash = 32 * b"\0"
|
||||
for i in range(1024):
|
||||
if i >= len(self.__open_transactions):
|
||||
transaction_data = 44 * b"\0"
|
||||
else:
|
||||
transaction = self.__open_transactions[i]
|
||||
transaction_data = transaction.transaction_fee.to_bytes(8, "big") + \
|
||||
transaction.sender_pubkey + \
|
||||
transaction.id.to_bytes(4, "big")
|
||||
current_hash = hashlib.sha256(current_hash + transaction_data).digest()
|
||||
self.__hashes.append(current_hash)
|
||||
|
||||
class Blockchain:
|
||||
def __init__(self):
|
||||
# maps block hashes to block instances
|
||||
@@ -185,6 +266,7 @@ class Blockchain:
|
||||
self.__latest_block_hash = None
|
||||
self.__lock = Lock()
|
||||
self.__load_blocks_from_disk()
|
||||
self.open_transactions = OpenTransactions(self)
|
||||
def __load_blocks_from_disk(self):
|
||||
last_valid = None
|
||||
try:
|
||||
@@ -222,6 +304,7 @@ class Blockchain:
|
||||
self.__latest_block_hash = block_hash
|
||||
if persist:
|
||||
self.__persist_block_update(latest_block, new_block)
|
||||
self.open_transactions.update()
|
||||
return True
|
||||
def __persist_block_update(self, old_block, new_block):
|
||||
if old_block is not None:
|
||||
|
||||
Reference in New Issue
Block a user