def test_account_receive_legacy_multiple(self, legacy_pocketable_block_factory): """ Receive two legacy receive blocks to start an account blockchain """ account = Account(**ACCOUNT_KWARGS) # Create two fake blocks to receive 10000 and 20000 raw respectively link_block_a = legacy_pocketable_block_factory( account_id=ACCOUNT_ID, amount=10000) link_block_b = legacy_pocketable_block_factory( account_id=ACCOUNT_ID, amount=20000) account.receive_block(link_block_a) account.receive_block(link_block_b) assert account.balance == 30000 first_block, second_block = account.blocks # Blockchain forms a linked list assert first_block.next == second_block assert second_block.prev == first_block assert second_block.block_type == "state" assert second_block.tx_type == "receive" assert second_block.previous == first_block.block_hash assert second_block.amount == 20000 assert second_block.balance == 30000 assert second_block.link == link_block_b.block_hash
def test_account_receive_first(self, pocketable_block_factory): """ Receive the first state block on an account and check that it's an open block """ account = Account(**ACCOUNT_KWARGS) assert account.balance == 0 # Create a fake block to receive 10000 raw link_block = pocketable_block_factory(account_id=ACCOUNT_ID, amount=10000) account.receive_block(link_block) first_block = account.blocks[0] assert account.balance == 10000 # Check the receiving block assert first_block.link == first_block.link_block.block_hash assert first_block.amount == 10000 assert first_block.block_type == "state" assert first_block.tx_type == "open" # Check the sending block assert first_block.link_block.link_as_account == ACCOUNT_ID assert first_block.prev is None assert first_block.next is None
def test_account_receive_legacy_first( self, legacy_pocketable_block_factory): """ Receive a legacy receive block to start an account blockchain """ account = Account(**ACCOUNT_KWARGS) # Create a fake block to receive 10000 raw link_block = legacy_pocketable_block_factory( account_id=ACCOUNT_ID, amount=10000) account.receive_block(link_block) assert account.balance == 10000 first_block = account.blocks[0] # Check the receiving block assert first_block.link == first_block.link_block.block_hash assert first_block.amount == 10000 assert first_block.block_type == "state" assert first_block.tx_type == "open" assert first_block.prev is None assert first_block.next is None
def test_account_change_representative_empty(self): account = Account(**ACCOUNT_KWARGS) account.change_representative(ACCOUNT_ID_B) # Changing the representative on an empty account doesn't # create a new block assert len(account.blocks) == 0 assert account.representative == ACCOUNT_ID_B
def test_account_create_private(self): account = Account(**ACCOUNT_KWARGS) assert account.private_key == PRIVATE_KEY # Private key can't be invalid kwargs = ACCOUNT_KWARGS.copy() kwargs["private_key"] = "invalid" with pytest.raises(InvalidPrivateKey): Account(**kwargs)
def test_account_add_block_different_account( self, account_factory): account = Account(**ACCOUNT_KWARGS) block = account_factory(balance=1000).blocks[0] with pytest.raises(InvalidAccountBlock) as exc: account.add_block(block) assert "The block doesn't belong to this account's blockchain" \ in str(exc.value)
def test_account_add_block_not_block( self, account_factory, pocketable_block_factory): account = Account(**ACCOUNT_KWARGS) link_block = pocketable_block_factory( account_id=ACCOUNT_ID, amount=1000) with pytest.raises(TypeError) as exc: account.add_block(link_block) assert "Argument has to be a Block instance" in str(exc.value)
def test_account_create_representative(self): account = Account(**ACCOUNT_KWARGS) assert account.representative == REPRESENTATIVE kwargs = ACCOUNT_KWARGS.copy() kwargs["representative"] = "invalid" # Invalid representative with pytest.raises(InvalidAccount) as exc: Account(**kwargs) assert "while changing property 'representative'" in str(exc.value)
def test_account_receive_duplicate(self, pocketable_block_factory): """ Try receiving the same block twice """ account = Account(**ACCOUNT_KWARGS) link_block = pocketable_block_factory(account_id=ACCOUNT_ID, amount=10000) account.receive_block(link_block) assert account.balance == 10000 # Second attempt will do nothing assert account.receive_block(link_block) is None assert account.balance == 10000
def test_account_create_account_id(self): account = Account(**ACCOUNT_KWARGS) assert account.account_id == ACCOUNT_ID # Account ID can't be invalid kwargs = ACCOUNT_KWARGS.copy() kwargs["account_id"] = "invalid" with pytest.raises(InvalidAccount): Account(**kwargs) # Account ID is required del kwargs["account_id"] with pytest.raises(ValueError) as exc: Account(**kwargs) assert "Field 'account_id' is required" in str(exc.value)
def test_account_create_source(self): kwargs = { "account_id": ACCOUNT_ID, "private_key": PRIVATE_KEY, "public_key": PUBLIC_KEY } # Invalid account source with pytest.raises(ValueError) as exc: Account(source="non existent", **kwargs) assert "is not a valid AccountSource" in str(exc.value) # Missing account source with pytest.raises(ValueError) as exc: Account(**kwargs) assert "Field 'source' is required" in str(exc.value)
def test_account_send_legacy_block(self, legacy_pocketable_block_factory): """ Receive NANO from a legacy block and then spend it """ account = Account(**ACCOUNT_KWARGS) account.receive_block( legacy_pocketable_block_factory(account_id=ACCOUNT_ID, amount=10000) ) first_block = account.blocks[0] send_block = account.send(account_id=ACCOUNT_ID_B, amount=6000) assert account.balance == 4000 assert send_block.balance == 4000 # Since we're sending, the amount is negative assert send_block.amount == -6000 assert send_block.previous == first_block.block_hash
def test_account_add_block_invalid_precomputed_work( self, pocketable_block_factory): account = Account(**ACCOUNT_KWARGS) account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) # Precompute work account.precomputed_work = PrecomputedWork( work=solve_work( account.blocks[-1].block_hash, difficulty=TEST_DIFFICULTY ), difficulty=TEST_DIFFICULTY ) # Use an arbitrarily high difficulty to make the precomputed work # invalid account.precomputed_work.difficulty = "f"*16 # Precomputed work is discarded silently since it was invalid # at the time the block was received block = account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) assert not block.work assert not account.precomputed_work
def test_account_complete_blockchain(self): """ Complete account's blockchain one block at a time and check the account's balance after each block """ from tests.wallet.data.account_blocks import ACCOUNTS for account_entry in ACCOUNTS: block_entries = account_entry["blocks"] account_id = account_entry["account_id"] account = Account( account_id=account_id, source=AccountSource.WATCHING ) assert account.balance == 0 for entry in block_entries: block_data = entry["block_data"] link_block_entry = entry.get("link_block", None) link_block = None if link_block_entry: link_block = LinkBlock( block_data=link_block_entry["block_data"], amount=int(link_block_entry["amount"]) ) block = Block( block_data=block_data, link_block=link_block ) account.add_block(block) assert account.balance == int(entry["balance"]) assert account.blocks[-1].balance == int(entry["balance"]) assert account.blocks[-1].tx_type == entry["tx_type"] assert account.blocks[-1].amount == int(entry["amount"])
def test_account_add_block_wrong_order(self, pocketable_block_factory): """ Try adding blocks to an account in wrong order """ account = Account(**ACCOUNT_KWARGS) for _ in range(0, 3): account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) block_a = account.blocks[0] block_c = account.blocks[2] # Recreate account account = Account(**ACCOUNT_KWARGS) account.add_block(block_a) # Block added in wrong order with pytest.raises(InvalidAccountBlock) as exc: account.add_block(block_c) assert "isn't a successor to the current head" in str(exc.value)
def wrapper(*args, **kwargs): wallet = args[0] with mock_node.lock: mock_wallet = wallet_from_str(mock_node.shared.wallet) if not mock_wallet: return logger.info( "Synchronizing mock wallet with the actual wallet " "from '{}'".format(wrapper._wrapped_method.__name__) ) for account in wallet.account_map.values(): account_id = account.account_id if account_id not in mock_wallet.account_map: mock_wallet.add_account( Account.from_dict(account.to_dict()) ) mock_account = mock_wallet.account_map[account_id] for block in account.blocks: if block.block_hash not in mock_account.block_map: mock_account.add_block( Block.from_dict(block.to_dict()) ) mock_block = mock_account.block_map[block.block_hash] if not mock_block.confirmed: mock_block.confirmed = block.confirmed if not mock_block.work: mock_block.work = block.work mock_block.difficulty = block.difficulty if not mock_block.signature: mock_block.signature = block.signature mock_account.confirmed_head = None mock_account.update_confirmed_head() mock_account.precomputed_work = copy.deepcopy( account.precomputed_work ) mock_node.shared.wallet = wallet_to_str(wallet) mock_node.synchronize_responses()
def test_account_add_block_first_non_open(self, pocketable_block_factory): """ Try adding a non-open block as the first block to an account """ account = Account(**ACCOUNT_KWARGS) for _ in range(0, 2): account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) block_b = account.blocks[1] # Recreate account account = Account(**ACCOUNT_KWARGS) with pytest.raises(InvalidAccountBlock) as exc: account.add_block(block_b) assert "First block for an account has to be an 'open' block" in str(exc.value)
def test_account_legacy_send_balance(self): """ Test that balance is calculated correctly for legacy send blocks """ account = Account( account_id="xrb_1jeqoxg7cpo5xgyxhcwzsndeuqp6yyr9m63k8tfu9mskjdfo7iq95p1ktb8j", source=AccountSource.WATCHING, representative="xrb_3dmtrrws3pocycmbqwawk6xs7446qxa36fcncush4s1pejk16ksbmakis78m" ) account.add_block( Block( block_data={ "account": "xrb_1jeqoxg7cpo5xgyxhcwzsndeuqp6yyr9m63k8tfu9mskjdfo7iq95p1ktb8j", "type": "open", "representative": "xrb_3dmtrrws3pocycmbqwawk6xs7446qxa36fcncush4s1pejk16ksbmakis78m", "source": "01BB6121002B428124BB0C546B3195F36108D30F8E664DC60777F3B4102A705F", "work": "e847031ddf0c4cef", "signature": "E23FA05FC070E176195A4860EC2E73966AE040A91B6F972B5843B31879BBBD32EF0D0186FF9EC2EF1714274FF45B7FF715FDF99BB3ED4571AD8798A28BAF1B0E", }, link_block=LinkBlock( block_data={ "account": "xrb_1uz6t43ztbffhft3zonpwf4hrjd9gorg53j8ac48ijppo6t4kj1px9jnimwj", "destination": "xrb_1jeqoxg7cpo5xgyxhcwzsndeuqp6yyr9m63k8tfu9mskjdfo7iq95p1ktb8j", "type": "send", "previous": "D04DEDF65FE1A99AA6759285512A6DE6708961443538C80CA588B032017497D6", "work": "547ac17fc277bd4c", "signature": "60C7D5546289179A480477BBEB62CB9BE8ABDD6B52DA4FC95D3BB28D0C88922D1D9022537626D835E67A407C8CDDFE376DE2C46EE4F04F1FEE88D2AABEAA6D09", "balance": "00018CE88D23D425E48205A276400000" }, amount=1000000000000000000000000000 ) ) ) account.add_block( Block( block_data={ "account": "xrb_1jeqoxg7cpo5xgyxhcwzsndeuqp6yyr9m63k8tfu9mskjdfo7iq95p1ktb8j", "destination": "xrb_384jppoodym71cppdj3a9gaim197q9uq365u5jqt6c4zhth13t9as4sj5zs8", "type": "send", "previous": "2453CA428DE7BCA437CDCBCD587646B4E508701E78E5B364F1DF5CF3A80B9F24", "work": "6692fe717be16f9c", "signature": "11E5BFB1304CD12D6960403B98778F76A6023FFFF30174DD21304EE757BD264E8F87FC6649E916BC9DFA861ACB94631C5E388C6ABF412C08891F9818C6CA1B09", "balance": "00000000033A5A7A8401B34F47000000" } ) ) assert account.balance == 999000000000000000000000000 assert account.blocks[1].balance == 999000000000000000000000000 assert account.blocks[1].amount == -1000000000000000000000000
def test_account_legacy_block_disallowed_after_state( self, pocketable_block_factory, legacy_receive_block_factory): """ Add a state block and then try adding a legacy receive block """ account = Account(**ACCOUNT_KWARGS) account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=10000) ) with pytest.raises(InvalidAccountBlock) as exc: # Once a state block is added to a chain, only state blocks are # allowed account.add_block( legacy_receive_block_factory( prev_block=account.blocks[0], private_key=PRIVATE_KEY, amount=20000 ) ) assert "State block can't be followed by a legacy block" in str(exc.value)
def test_account_add_block_precomputed_work( self, pocketable_block_factory): account = Account(**ACCOUNT_KWARGS) account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) # Precompute work account.precomputed_work = PrecomputedWork( work=solve_work( account.blocks[-1].block_hash, difficulty=TEST_DIFFICULTY ), difficulty=TEST_DIFFICULTY ) block = account.receive_block( pocketable_block_factory(account_id=ACCOUNT_ID, amount=1000) ) # The precomputed work is used assert block.work assert not account.precomputed_work
def test_account_change_representative_invalid_account_id(self): account = Account(**ACCOUNT_KWARGS) with pytest.raises(InvalidAccount): account.change_representative("invalid")