def test_discard_block(self): bc = BlockChain() cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b = bc.new_block(cbtx) mine(b) self.assertTrue(bc.discard_block(b)) b = Block(1337, bc.get_block_from_index(1).get_hash()) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b.add_transaction(cbtx) mine(b) self.assertFalse(bc.discard_block(b)) self.assertEqual(len(bc.chain), 2) b = Block(bc.get_block_from_index(-1).index, "obviously_fake_hash") cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b.add_transaction(cbtx) mine(b) self.assertFalse(bc.discard_block(b)) self.assertEqual(len(bc.chain), 2) b = Block( bc.get_block_from_index(1).index + 1, bc.get_block_from_index(1).get_hash()) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b.add_transaction(cbtx) # coinbase mine(b) self.assertTrue(bc.discard_block(b)) self.assertEqual(len(bc.chain), 3)
def test_use_pool(self): bc = BlockChain() w = Wallet() w.create_keys() # make some cash tx0 = Transaction("0", w.get_address(), 1) b = bc.new_block(tx0) mine(b) self.assertTrue(bc.discard_block(b)) self.assertEqual(bc.get_wallet_balance(w.get_address()), 1) tx_count = 10 for i in range(tx_count): t = Transaction(w.get_address(), 1, 1 / 100) w.sign_transaction(t) self.assertTrue(bc.add_transaction(t)) self.assertEqual(len(bc.transaction_pool), tx_count) b = bc.new_block(self.tx) max_txs_in_block = 2**Tree.MAX_DEPTH self.assertEqual(len(list(b.get_transactions())), min(max_txs_in_block, tx_count + 1)) # Transactions in new block are removed from tx pool when new block is successfully discarded to chain b = bc.new_block(self.tx) # coinbase tx mine(b) bc.discard_block(b) expected_txs_in_tx_pool = max(0, tx_count - (max_txs_in_block - 1)) self.assertEqual(len(bc.transaction_pool), expected_txs_in_tx_pool) leftover_count = min(max_txs_in_block - 1, expected_txs_in_tx_pool) self.assertEqual(len(list(bc.new_block(self.tx).get_transactions())), 1 + leftover_count)
def test_file_save(self): test_dir = tempfile.mkdtemp() test_file = path.join(test_dir, "blockchain.json") bc = BlockChain() b = bc.new_block(self.tx) mine(b) bc.discard_block(b) self.assertTrue(bc.save_to_file(test_file)) self.assertIsInstance(BlockChain.load_from_file(test_file), BlockChain) shutil.rmtree(test_dir)
def test_post_block(self): p2p = ClientAPITest.DummyP2p() bc = BlockChain() p2p.bc = bc app.p2p = p2p # make some cash first w, cbtx = WalletHelper.generate_wallet_and_coinbase() cash_block = bc.new_block(cbtx) mine(cash_block) bc.discard_block(cash_block) txs = [] for i in range(4): tx = Transaction(w.get_address(), i, 0.1) w.sign_transaction(tx) txs.append(tx) # mine current block cbtx = WalletHelper.generate_coinbase_with_unused_wallet() current_block = bc.new_block(cbtx) mine(current_block) # put current block in chain and create new block bc.discard_block(current_block) # create new block cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b = bc.new_block(cbtx) for tx in txs: b.add_transaction(tx) client = app.test_client() # POST new UN-mined block (should fail and return 400 bad request) response = client.post("/block", data=b.to_json()) self.assertEqual(response.status_code, 400) # mine new block mine(b) # POST new mined block (should work this time) response = client.post("/block", data=b.to_json()) self.assertEqual(response.status_code, 201)
def test_create_chain(self): bc = BlockChain() self.assertIsInstance(bc.new_block(), Block) self.assertEqual(len(bc.chain), 1) self.assertFalse(bc.get_block_from_index(1337)) # Cannot add block when previous has not been proven b = bc.new_block() self.assertFalse(bc.discard_block(b))
def test_block_since(self): bc = BlockChain() cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b = bc.new_block(cbtx) mine(b) bc.discard_block(b) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b2 = bc.new_block(cbtx) mine(b2) bc.discard_block(b2) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b3 = bc.new_block(cbtx) self.assertEqual(len(bc.get_blocks_since(b.get_hash())), 1) self.assertEqual(len(bc.get_blocks_since(b2.get_hash())), 0) # Block b3 is not in the blockchain self.assertIsNone(bc.get_blocks_since(b3.get_hash()))
def test_search_block(self): bc = BlockChain() for i in range(5): cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b = bc.new_block(cbtx) mine(b) self.assertTrue(bc.discard_block(b)) for i in range(6): h = bc.get_block_from_index(i).get_hash() self.assertEqual(bc.get_block_from_hash(h), bc.get_block_from_index(i)) self.assertFalse(bc.get_block_from_hash("abc"))
def test_add_block(self): bc = BlockChain() # Can add block when previous has been proven b = bc.new_block(self.tx) mine(b) self.assertTrue(bc.discard_block(b)) self.assertTrue(bc.get_block_from_index(1).validate_proof()) self.assertEqual(bc.get_block_from_index(0).index, 0) self.assertEqual(len(bc.chain), 2) self.assertNotEqual( bc.get_block_from_index(0).get_hash(), bc.get_block_from_index(-1).get_hash())
def test_blockchain_serialize(self): bc = BlockChain() b = bc.new_block(self.tx) mine(b) self.assertTrue(bc.discard_block(b)) bc2 = BlockChain.from_json(bc.to_json()) self.assertEqual( bc.get_block_from_index(-1).get_hash(), bc2.get_block_from_index(-1).get_hash()) self.assertEqual( bc.get_block_from_index(0).get_hash(), bc2.get_block_from_index(0).get_hash())
def test_block_chaining(self): bc = BlockChain() for i in range(5): cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b = bc.new_block(cbtx) mine(b) self.assertTrue(bc.discard_block(b)) self.assertEqual(len(bc.chain), 6) for i in range(6): self.assertEqual(bc.get_block_from_index(i).index, i) for i in range(1, 6): self.assertEqual( bc.get_block_from_index(i - 1).get_hash(), bc.get_block_from_index(i).prevhash)
def test_secure_wallet_balance(self): bc = BlockChain() w1 = Wallet() w1.create_keys() addr1 = w1.get_address() tx0 = Transaction("0", addr1, 1) b = bc.new_block(tx0) mine(b) bc.discard_block(b) balance = bc.get_secure_wallet_balance(addr1) self.assertEqual(balance, None) tx1 = Transaction(addr1, "toto", 1) w1.sign_transaction(tx1) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b1 = bc.new_block(cbtx) b1.add_transaction(tx1) mine(b1) bc.discard_block(b1) self.assertEqual(bc.get_secure_wallet_balance("toto"), None) for i in range(5): tx0 = Transaction("0", addr1, 1) b = bc.new_block(tx0) mine(b) bc.discard_block(b) # only 5 confirmations so far, tx is not there yet for secure balance self.assertEqual(bc.get_secure_wallet_balance("toto"), None) cbtx = WalletHelper.generate_coinbase_with_unused_wallet() b6 = bc.new_block(cbtx) mine(b6) bc.discard_block(b6) # tx appears after 6 confirmations only self.assertEqual(bc.get_secure_wallet_balance("toto"), 1)
def test_wallet_balance(self): bc = BlockChain() w1 = Wallet() w1.create_keys() addr1 = w1.get_address() w2 = Wallet() w2.create_keys() addr2 = w2.get_address() w3 = Wallet() w3.create_keys() addr3 = w3.get_address() tx0 = Transaction("0", addr1, 1) b = bc.new_block(tx0) mine(b) bc.discard_block(b) balance = bc.get_wallet_balance(addr1) self.assertEqual(balance, 1) tx1 = Transaction(addr1, addr2, 0.1) tx2 = Transaction(addr1, addr3, 0.2) tx3 = Transaction(addr2, addr3, 133) w1.sign_transaction(tx1) w1.sign_transaction(tx2) w2.sign_transaction(tx3) self.assertTrue(bc.add_transaction(tx1)) self.assertTrue(bc.add_transaction(tx2)) self.assertFalse(bc.add_transaction(tx3)) tx4 = Transaction(addr1, addr2, 1) w1.sign_transaction(tx4) self.assertFalse(bc.add_transaction(tx4)) balance = bc.get_wallet_balance(addr1) self.assertEqual(balance, 0.7) balance = bc.get_wallet_balance(addr2) self.assertEqual(balance, 0.1) balance = bc.get_wallet_balance(addr3) self.assertEqual(balance, 0.2) w4 = Wallet() w4.create_keys() addr4 = w4.get_address() cbtx4 = Transaction("0", addr4, 1) b4 = bc.new_block(cbtx4) mine(b4) bc.discard_block(b4) self.assertTrue(bc.get_wallet_balance(addr4), 1) ntx4 = Transaction(addr4, "toto", 1, magic=bc.magic) w4.sign_transaction(ntx4) self.assertTrue(bc.add_transaction(ntx4)) coinbase4 = Transaction("0", addr4, 1) nb4 = bc.new_block( coinbase4 ) # new block nb4 is filled with coinbase4 and ntx4 (fill_block() was called) mine(nb4) # mine block before adding to chain logger.debug("DISCARDING...") discarded = bc.discard_block(nb4) logger.debug("END DISCARD") self.assertTrue(discarded)