示例#1
0
    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
示例#2
0
    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
示例#3
0
    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
示例#4
0
    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
示例#5
0
    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)
示例#6
0
    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)
示例#7
0
    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)
示例#8
0
    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)
示例#9
0
    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
示例#10
0
    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)
示例#11
0
    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)
示例#12
0
    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
示例#13
0
    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
示例#14
0
    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"])
示例#15
0
    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)
示例#16
0
    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()
示例#17
0
    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)
示例#18
0
    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
示例#19
0
    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)
示例#20
0
    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
示例#21
0
    def test_account_change_representative_invalid_account_id(self):
        account = Account(**ACCOUNT_KWARGS)

        with pytest.raises(InvalidAccount):
            account.change_representative("invalid")