def test_transaction_pool(self): pool = TransactionPool() wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) amount1 = 50 transaction1 = Transaction.create_transaction( wallet, self.test_recipient_address1, amount1) pool.add(transaction1) transaction2 = Transaction.create_transaction( wallet, self.test_recipient_address1, amount1) pool.add(transaction2) amount2 = 30 transaction3 = Transaction.create_transaction( wallet, self.test_recipient_address1, amount2) transaction3.transaction_id = transaction1.transaction_id # Set tx3 id to tx1 id pool.add(transaction3) self.assertTrue(len(pool.transactions) == 2) self.assertEqual( pool.transactions[transaction1.transaction_id].outputs[0].address, wallet.public_key_hex) self.assertEqual( pool.transactions[transaction1.transaction_id].outputs[0].amount, wallet.balance - amount2) self.assertEqual( pool.transactions[transaction1.transaction_id].outputs[1].address, self.test_recipient_address1) self.assertEqual( pool.transactions[transaction1.transaction_id].outputs[1].amount, amount2)
def test_valid_transactions(self): pool = TransactionPool() wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) amount1 = 50 transaction1 = Transaction.create_transaction( wallet, self.test_recipient_address1, amount1) pool.add(transaction1) transaction2 = Transaction.create_transaction( wallet, self.test_recipient_address1, amount1) pool.add(transaction2) transactions = pool.get_valid_transactions() self.assertTrue(len(transactions) == 2) # Let's corrupt balance transaction1.outputs[0].amount = 1000 transactions = pool.get_valid_transactions() self.assertTrue(len(transactions) == 1) # Let's corrupt signature transaction2.outputs[0].address = 'corrupted-address' transactions = pool.get_valid_transactions() self.assertTrue(len(transactions) == 0)
def create_test_transaction(self, timestamp=None): if timestamp == None: timestamp = ChainUtil.generate_timestamp() transaction = Transaction( None, TransactionInput(timestamp, self.amount, self.address, self.signature), [TransactionOutput(self.amount, self.address)], ) return transaction
def test_transaction_verification(self): amount = 50 wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) transaction = Transaction.create_transaction( wallet, self.test_recipient_address1, amount) self.assertTrue(transaction.verify()) transaction.outputs[0].address = 'changed public key' self.assertFalse(transaction.verify())
def create_test_transaction(self, timestamp=None, amount=0, address='myaddress', signature='mysignature'): if timestamp == None: timestamp = ChainUtil.generate_timestamp() transaction = Transaction( None, TransactionInput(timestamp, amount, address, signature), [TransactionOutput(amount, address)], ) return transaction
def test_reward_transactions(self): miner_wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) transaction1 = Transaction.create_reward_transaction(1, miner_wallet) self.assertEqual(transaction1.outputs[0].address, miner_wallet.public_key_hex) self.assertEqual(transaction1.outputs[0].amount, MINING_REWARD) self.assertEqual(transaction1.input.address, REWARD_TRANSACTION_ADDRESS) self.assertEqual(transaction1.input.signature, REWARD_TRANSACTION_SIGNATURE)
def mine(self): # 1. Add reward for the miner # 2. Create/mine block from valid transactions # 3. Sync the chain with other p2p servers (TODO: add after finishing proof of concept) # 4. Clear the local pool # 5. Broadcast that pool is clear to other nodes via p2p height = len(self.block_chain.chain) + 1 valid_transactions = self.transaction_pool.get_valid_transactions() reward_transaction = Transaction.create_reward_transaction(height, self.wallet) valid_transactions.append(reward_transaction) block = self.block_chain.append_block(valid_transactions) # TODO: Broadcast Sync Chain action across p2p nodes self.transaction_pool.clear() # TODO: Broadcast Clear Transaction Pool action across p2p nodes return block
def test_create_transaction(self): amount = 50 wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) transaction = Transaction.create_transaction( wallet, self.test_recipient_address1, amount) self.assertEqual(transaction.outputs[0].address, wallet.public_key_hex) self.assertEqual(transaction.outputs[0].amount, wallet.balance - amount) self.assertEqual(transaction.outputs[1].address, self.test_recipient_address1) self.assertEqual(transaction.outputs[1].amount, amount) self.assertEqual(transaction.input.amount, wallet.balance)
def test_transaction_update(self): amount1 = 50 wallet = Wallet(self.test_password, self.test_private_key_pem, self.test_public_key_pem, balance=TEST_WALLET_BALANCE) transaction = Transaction.create_transaction( wallet, self.test_recipient_address1, amount1) self.assertTrue(transaction.verify()) amount2 = 30 transaction.update(wallet, self.test_recipient_address2, amount2) self.assertTrue(transaction.verify()) self.assertEqual(transaction.outputs[0].amount, wallet.balance - (amount1 + amount2)) transaction.outputs[0].amount = 10000000 self.assertFalse(transaction.verify())
def load_miner(cls, session, wallet_name): manager = Manager(session) wallet = manager.load_wallet(wallet_name) if wallet: chain = [] for block in manager.load_blocks(): chain.append(Block.from_database_format(block)) transactions = [] for transaction in manager.load_transactions(): transactions.append(Transaction.from_database_format(transaction)) miner = cls( Wallet.from_database_format(wallet), BlockChain(chain=chain), TransactionPool(transactions=transactions) ) return miner return None
def _test_generate_genesis_block(self): pb = Block( ChainUtil.generate_timestamp(), None, '0000000000000000000000000000000000000000000000000000000000000000', 0, 3, []) address = ('000000000000000000000000000000000000000000000000000' '0000000000000556e757365642067656e6573697320626c6f63' '6b2061646472657373202d2031312f30332f323031382032333' 'a3036204553540000000000') signature = '47656e6573697320626c6f636b2063726561746564206f6e2031312f30332f32303138' timestamp = 1541319432152 transaction = Transaction( None, TransactionInput(timestamp, amount, address, signature), [TransactionOutput(amount, address)], ) block = Block.mine(pb, [transaction])
def genesis(cls): # 0000ef80bfa3b57331b1200969d8044267ce1af1a4d2980308e2b6af4569ff33 timestamp = 1541320453883 nonce = 46645 difficulty = 4 prev_hash = '0000000000000000000000000000000000000000000000000000000000000000' address = ('00000000000000000000000000000000000000000000' '00000000000000000000556e757365642067656e6573' '697320626c6f636b2061646472657373202d2031312f' '30332f323031382032333a3036204553540000000000') signature = '47656e6573697320626c6f636b2063726561746564206f6e2031312f30332f32303138' amount = 0 transaction = Transaction( '7f5f7b62dfe211e89b13685b35ad2541', TransactionInput(1541319432152, amount, address, signature), [TransactionOutput(amount, address)]) data = [transaction] hash = Block.create_hash(timestamp, prev_hash, nonce, difficulty, data) return cls(timestamp, prev_hash, hash, nonce, difficulty, data)
def create_transaction(self, recipient_address, amount, transaction_pool, block_chain): self.balance = self.calculate_balance(block_chain) if amount > self.balance: raise ValueError('Amount {} exceeds wallet balance of {}'.format( amount, self.balance)) # ########################################################################################## # Optimization code # We make sure that multiple TransactionOutput objects are appended to existing transactions # ########################################################################################## transaction = transaction_pool.find_by_public_key(self.public_key_hex) if transaction: transaction.update(self, recipient_address, amount) else: transaction = Transaction.create_transaction( self, recipient_address, amount) transaction_pool.add(transaction) # ########################################################################################## return transaction
def from_database_format(cls, item): transactions = json.loads(item.data) return cls( item.timestamp, item.prev_hash, item.hash, item.nonce, item.difficulty, [Transaction.from_database_format(data) for data in transactions])