def test_failed_input_lookup(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":2"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) # good tx id, bad input location tx3 = Transaction(["fakehash" + ":2"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) # bad tx id block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertEqual(block2.is_valid(), (False, "Required output not found")) block2 = TestBlock(1, [tx3], block.hash) self.assertEqual(block2.is_valid(), (False, "Required output not found"))
def test_input_txs_on_chain(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) # next two transactions spend same input twice (double spend) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .9), TransactionOutput("Alice", "Carol", 0) ]) tx3 = Transaction([tx2.hash + ":0"], [TransactionOutput("Bob", "Bob", .8)]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertTrue(block2.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block2)) block3 = TestBlock(1, [tx3], block.hash) self.assertEqual(block3.is_valid(), (False, "Input transaction not found")) block3 = TestBlock(2, [tx3], block2.hash) self.assertTrue(block3.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block3))
def test_doublespent_input_same_chain(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) # next two transactions spend same input twice (double spend) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", 0), TransactionOutput("Alice", "Carol", 0) ]) tx3 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Carol", 0), TransactionOutput("Alice", "Carol", 0) ]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertTrue(block2.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block2)) block3 = TestBlock(2, [tx3], block2.hash) self.assertEqual(block3.is_valid(), (False, "Double-spent input")) block3 = TestBlock(1, [tx3], block.hash) self.assertTrue( block3.is_valid()[0]) # doublespend should be allowed across forks
def test_merkle_root(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 3) ]) tx2 = Transaction([tx1.hash + ":0"], [ TransactionOutput("Alice", "Bob", 2), TransactionOutput("Alice", "Carol", 1) ]) tx3 = Transaction([tx2.hash + ":0"], [ TransactionOutput("Bob", "Carol", 1), TransactionOutput("Bob", "Bob", 1) ]) tx4 = Transaction([tx3.hash + ":0"], [ TransactionOutput("Carol", "Alice", 0.5), TransactionOutput("Carol", "Carol", 0.5) ]) block1 = TestBlock(0, [tx1], "genesis", is_genesis=True) block2 = TestBlock(0, [tx1, tx2], "genesis", is_genesis=True) block3 = TestBlock(0, [tx1, tx2, tx3, tx4], "genesis", is_genesis=True) for block in [block1, block2, block3]: block.set_dummy_timestamp() self.assertEqual( block1.merkle, "70dd3a969ee311d9749d8f1c2f03ab1dc4930ca0be58d231a73dfeeb85f5b68d") self.assertEqual( block2.merkle, "e0559c662f64c8fd1b638384ecbb1104335445a07114fd7d197316402e2f6f4c") self.assertEqual( block3.merkle, "039eceb401b485400f19bca99158b9dd2fcd13e9e4f287fde16f81fa58074a51")
def test_rejects_invalid_hash(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .4), TransactionOutput("Alice", "Carol", .4) ]) # test an invalid hash on genesis block is rejected, and valid hash is accepted block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.test_chain.add_block(block) old_hash = block.hash block.hash = "fff" self.assertEqual(block.is_valid(), (False, "Hash failed to match")) block.hash = old_hash # test an invalid hash on non-genesis block is rejected, and valid hash is accepted block2 = TestBlock(1, [tx2], block.hash) self.assertTrue(block2.is_valid()[0]) old_hash = block2.hash block2.hash = "fff" self.assertEqual(block2.is_valid(), (False, "Hash failed to match")) block2.hash = old_hash self.assertTrue(block2.is_valid()[0])
def test_input_txs_in_block(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) # create chain of transactions; tx2 spends tx1, tx3 spends tx2 tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .9), TransactionOutput("Alice", "Carol", 0) ]) tx3 = Transaction([tx2.hash + ":0"], [TransactionOutput("Bob", "Bob", .8)]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertTrue(block2.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block2)) block3 = TestBlock(1, [tx3], block.hash) self.assertEqual(block3.is_valid(), (False, "Input transaction not found")) block3 = TestBlock(1, [tx2, tx3], block.hash) self.assertTrue(block3.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block3))
def test_block_fromjson(): sender, recipient = Key(), Key() trx = Transaction(sender.address, sender.public_key, recipient.address, 'Hello world') trx.sign(sender.private_key) block = Node.mine_block(0, 0, [trx]) block2 = Block.from_json(block.json()) assert block.index == block2.index
def test_transaction_fromjson(): sender, recipient = Key(), Key() trx = Transaction(sender.address, sender.public_key, recipient.address, 'Hello world') trx.sign(sender.private_key) doc = trx.json(True) trx2 = Transaction.from_json(doc) assert trx.signature == trx2.signature
def test_valid_merkle_three_leafs(self): tx1 = Transaction([], [TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1)]) tx2 = Transaction([tx1.hash + ":1"], [TransactionOutput("Alice", "Bob", .9), TransactionOutput("Alice", "Carol", 0)]) tx3 = Transaction([tx2.hash + ":0"], [TransactionOutput("Bob", "Bob", .8)]) transactions = [tx1, tx2, tx3] block = TestBlock(0, transactions, "genesis", is_genesis=True) expected_value = sha256_2_string(sha256_2_string(str(tx1)) + sha256_2_string(str(tx2))) expected_value = sha256_2_string(expected_value + sha256_2_string(str(tx3))) self.assertEqual(block.calculate_merkle_root(), expected_value)
def _read_block_from_dict(self, dict): # print(dict) previous_hash = dict['prev_block'] block = Block(previous_hash=previous_hash) block.hash = dict['hash'] block.nonce = dict['nonce'] block.timestamp = dict['time_stamp'] is_coinbase = True for transaction_dict in dict['transactions']: sender = '' if 'sender_public_key' in transaction_dict: sender = transaction_dict['sender_public_key'] signature = None if 'signature' in transaction_dict: signature = transaction_dict['signature'] reciever = transaction_dict['receiver_public_key'] value = transaction_dict['value'] transaction_inputs = [] if 'input' in transaction_dict: for transaction_input_dict in transaction_dict['input']: transaction_output_id = transaction_input_dict[ 'transactionOutputId'] transaction_input = TransactionInput( transaction_output_id=transaction_output_id) transaction_inputs.append(transaction_input) transaction = Transaction(sender, reciever, value, transaction_inputs) transaction.transaction_id = transaction_dict['id'] transaction.signature = signature block.add_transaction(transaction, all_utxos=self.blockchain.all_utxos, minimum_transaction=self.minimum_transaction, fee=self.blockchain.fee, should_check=False, is_coinbase=is_coinbase) is_coinbase = False if 'output' in transaction_dict: for transaction_output_dict in transaction_dict['output']: value = transaction_output_dict['value'] parent_transaction_id = '' if 'parent_transaction_id' in transaction_output_dict: parent_transaction_id = transaction_output_dict[ 'parent_transaction_id'] recipient_public_key = transaction_output_dict[ 'recipient_public_key'] transaction_output = TransactionOutput( recipient_public_key_str=recipient_public_key, value=value, parent_transaction_id=parent_transaction_id) transaction_output.id = transaction_output_dict['id'] transaction.outputs.append(transaction_output) self.blockchain.append_transaction(transaction) self.blockchain.append_block(block)
def post(self): doc = json.loads(self.request.body) trx = Transaction(doc['sender_addr'], doc['sender_public'], doc['recipient_addr'], doc['payload']) trx.sign(doc['sender_private']) #: broadcast the new transaction broadcast_trx(doc) new_block = node.add_transaction(trx) #: broadcast new blocks self.finish(ok()) if new_block: logging.info("mined new block: %s", new_block.index) broadcast_block(new_block.json())
def test_blockchain_hash(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":0"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) block = TestBlock(0, [tx1, tx2], "genesis", is_genesis=True) block.set_dummy_vals() self.assertEqual( sha256_2_string(block.header()), "b0d35a2ffb46c6a8f48658d77f990656f7a9ec753b77342eb3b0e6a1d7acf934")
def test_blockchain_hash(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":0"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) block = TestBlock(0, [tx1, tx2], "genesis", is_genesis=True) block.set_dummy_timestamp() self.assertEqual( sha256_2_string(block.header()), "9fc4ae4f2e6a68a0e79a57c4491b03a72f9a4bcdbc6ab7213e0f9334d800c57d")
def test_valid_merkle_one_leaf(self): tx1 = Transaction([], [TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1)]) transactions = [tx1] block = TestBlock(0, transactions, "genesis", is_genesis=True) expected_value = sha256_2_string(str(tx1)) self.assertEqual(block.calculate_merkle_root(), expected_value)
def string_to_transaction(txstring): """ Takes a string as input and deserializes it into a transaction object for receipt over network. !!! WARNING !!! (don't ever do anything like this in production, it's not secure). Args: txstring (str): String representing a cryptocurrency transaction. Returns: :obj:`Transaction`: Parsed transaction object representing input, False or exception thrown on failure. """ transaction_parts = txstring.split("-")[1:] if len(transaction_parts) != 2: return False input_refs_str = transaction_parts[0] input_refs = remove_empties(input_refs_str.split(";")) outputs_as_str = remove_empties(transaction_parts[1].split(";")) outputs = [txout_interface.string_to_output(output_string) for output_string in outputs_as_str] if False in outputs: return False return Transaction(input_refs, outputs)
def test_double_tx_inclusion_same_block(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .4), TransactionOutput("Alice", "Carol", .4) ]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2, tx2], block.hash) self.assertEqual(block2.is_valid(), (False, "Double transaction inclusion"))
def create_transaction(self, sender, recipient, amount): """ Creates a new transaction to go into the next block :param sender: <str> sender address :param recipient: <str> recipient address :param amount: <float> amount :return: <Transaction> generated transaction """ transaction = Transaction(sender, recipient, amount) if transaction.validate(): self.__current_transactions.append(transaction) return transaction, True return None, False
def new_transaction(): values = request.get_json() txn = Transaction(**values) index = block_chain.add_transactions([txn]) response = { 'message': 'Transaction will be added to block {0}'.format(index) } return jsonify(response), 201
def from_json(json_obj: dict): transactions: List[Transaction] = [] for tx in json_obj["transactions"]: tx = Transaction.from_json(tx) transactions.append(tx) block_hash = Hexdata(json_obj["hash"]) number = int(json_obj["number"], 16) num_transactions = len(transactions) return Block(number, num_transactions, block_hash, transactions)
def test_user_consistency(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction( [tx1.hash + ":1"], [ TransactionOutput("Carol", "Bob", 0), TransactionOutput("Carol", "Carol", 0) ] ) # outputs created from wrong user (different than one that received inputs) tx3 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", 0), TransactionOutput("Carol", "Carol", 0) ]) # two outputs from different users tx4 = Transaction([tx1.hash + ":0", tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", 0), TransactionOutput("Alice", "Carol", 0) ]) # two inputs to different users tx5 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", 0), TransactionOutput("Alice", "Carol", 0) ]) # this one is valid block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertEqual(block2.is_valid(), (False, "User inconsistencies")) block2 = TestBlock(1, [tx3], block.hash) self.assertEqual(block2.is_valid(), (False, "User inconsistencies")) block2 = TestBlock(1, [tx4], block.hash) self.assertEqual(block2.is_valid(), (False, "User inconsistencies")) block2 = TestBlock(1, [tx5], block.hash) self.assertTrue(block2.is_valid()[0])
def test_failed_input_lookup(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":2"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) # good tx id, bad input location tx3 = Transaction(["fakehash" + ":2"], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Carol", 1) ]) # bad tx id # good txs tx4 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .2), TransactionOutput("Alice", "Carol", .2) ]) tx5 = Transaction([tx4.hash + ":0"], [TransactionOutput("Bob", "Bob", 0)]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertEqual(block2.is_valid(), (False, "Required output not found")) block2 = TestBlock(1, [tx3], block.hash) self.assertEqual(block2.is_valid(), (False, "Required output not found")) block2 = TestBlock( 1, [tx4, tx5], block.hash) # tx exists, but is in same block; this should work self.assertTrue(block2.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block2))
def test_no_money_creation(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", 3), TransactionOutput("Alice", "Carol", 0) ]) # single output creates money tx3 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .9), TransactionOutput("Alice", "Carol", .9) ]) # sum of outputs creates money block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2], block.hash) self.assertEqual(block2.is_valid(), (False, "Creating money")) block2 = TestBlock(1, [tx3], block.hash) self.assertEqual(block2.is_valid(), (False, "Creating money"))
def handle_reorg(self, last_number: int) -> int: """ Handles an Ethereum reorganization :param last_number: Last number in our db that is knwown to be reorged out :return new db height -- place to start indexing from again """ height = last_number block = Block.get_block(height) our_block_hash = block.get_hash().get() network_block = self.blockchain.get_block(height) network_block_hash = network_block.get_hash().get() # keep going back from last_number until we find a block where we're correct with the network while our_block_hash != network_block_hash: Block.remove_block(height) Transaction.remove_transactions(height) height -= 1 block = Block.get_block(height) our_block_hash = block.get_hash().get() network_block = self.blockchain.get_block(height) network_block_hash = network_block.get_hash().get() logging.info( f"Removed transactions for reorganization from block {last_number} to block {height}" ) return height
def test_malformed_txs(self): tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2_evil = BadTX([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .4), TransactionOutput("Alice", "Carol", .4) ]) tx2 = Transaction([tx1.hash + ":1"], [ TransactionOutput("Alice", "Bob", .4), TransactionOutput("Alice", "Carol", .4) ]) block = TestBlock(0, [tx1], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) block2 = TestBlock(1, [tx2_evil], block.hash) self.assertEqual(block2.is_valid(), (False, "Malformed transaction included")) block2 = TestBlock(1, [tx2], block.hash) self.assertTrue(block2.is_valid()[0])
def add_transaction(self, sender, recipient, signature): transaction = Transaction( sender=sender, recipient=recipient, value=1, signature=signature, ) self.transactions.append(transaction) # Automatically add blocks after transaction # only for hackathon presentation purposes. # New block after each transaction, change to # whatever you want or comment to make it valid. if len(self.transactions) == 1: self._add_block(self.chain[-1].get_hash()) return len(self.chain) + 1
def make_chain(): chain = Chain() sender, recipient = Key(), Key() trx = Transaction(sender.address, sender.public_key, recipient.address, "Hi") trx.sign(sender.private_key) genesis = Node.mine_block(0, 0, [trx]) chain.add_block(genesis) for _ in range(1, 2): trx = Transaction(sender.address, sender.public_key, recipient.address, "Hi") trx.sign(sender.private_key) block = Node.mine_block(chain.height, chain.last_block.hash, [trx]) chain.add_block(block) return chain
def test_rejects_too_many_txs(self): txs = [] for i in range(901): txs.append( Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ])) # test too many txs in genesis block = TestBlock(0, txs, "genesis", is_genesis=True) self.assertEqual(block.is_valid(), (False, "Too many transactions")) # 900 txs should be fine block = TestBlock(0, txs[1:], "genesis", is_genesis=True) self.assertTrue(block.is_valid()[0]) self.assertTrue(self.test_chain.add_block(block)) # test too many txs outside genesis block = TestBlock(1, txs, block.hash) self.assertEqual(block.is_valid(), (False, "Too many transactions"))
USERS = ["Alice", "Bob", "Charlie", "Dave", "Errol", "Frank"] HEIGHT_TO_REACH = 100 MAX_TXS_PER_BLOCK = 50 FORK_PROBABILITY = .3 # basic wallet functionality; track UTXOs for users user_utxos = {} for user in USERS: user_utxos[user] = [] # insert genesis block; populate all users w huge balance outputs = [] for user in USERS: genesis_utxo = TransactionOutput("Genesis", user, 100000000) outputs.append(genesis_utxo) genesis_tx = Transaction([], outputs) for user_num in range(len(USERS)): user = USERS[user_num] user_utxos[user].append((genesis_tx.hash + ":" + str(user_num), 100000000)) genesis_block = PoWBlock(0, [genesis_tx], "genesis", is_genesis=True) chaindb.chain.add_block(genesis_block) gossip.gossip_message("addblock", genesis_block) curr_height = 1 parent = genesis_block while curr_height <= HEIGHT_TO_REACH: chain = chaindb.chain txs = [] if random.random() < FORK_PROBABILITY:
import blockchain from blockchain.transaction import Transaction, TransactionOutput from blockchain.pow_block import PoWBlock import transaction # create some transactions tx1 = Transaction([], [ TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1) ]) tx2 = Transaction([tx1.hash + ":0"], [ TransactionOutput("Alice", "Bob", .4), TransactionOutput("Alice", "Carol", .4) ]) # create an unsealed block block = PoWBlock(0, [tx1, tx2], "genesis", is_genesis=True) # run the mining loop until a valid PoW seal is created (final hash should have 2 leading 0s) block.mine() # add the block to the blockchain assert (blockchain.chain.add_block(block)) # display the block print(block.header()) print(block.hash)
def test_transaction_validation(): sender, recipient = Key(), Key() trx = Transaction(sender.address, sender.public_key, recipient.address, 'Hello world') trx.sign(sender.private_key) assert trx.is_valid()
def test_block_validation(): sender, recipient = Key(), Key() trx = Transaction(sender.address, sender.public_key, recipient.address, 'Hello world') trx.sign(sender.private_key) assert Node.mine_block(0, 0, [trx]).is_valid()