def test_merkle_root_1_item_unlockable( web3, get_accounts, token_network_test_utils, secret_registry_contract, ): A = get_accounts(1)[0] pending_transfers_tree = get_pending_transfers_tree(web3, [6]) secret_registry_contract.functions.registerSecret( pending_transfers_tree.unlockable[0][TestLockIndex.SECRET], ).transact({'from': A}) assert secret_registry_contract.functions.getSecretRevealBlockHeight( pending_transfers_tree.unlockable[0][TestLockIndex.SECRETHASH], ).call() == web3.eth.blockNumber ( locksroot, unlocked_amount, ) = token_network_test_utils.functions.getMerkleRootAndUnlockedAmountPublic( pending_transfers_tree.packed_transfers, ).call() merkle_root = pending_transfers_tree.merkle_root assert locksroot == merkle_root assert unlocked_amount == 6
def test_merkle_tree_length_fail( web3, get_accounts, token_network_test_utils, secret_registry_contract, ): network_utils = token_network_test_utils A = get_accounts(1)[0] pending_transfers_tree = get_pending_transfers_tree(web3, [2, 3, 6], [5]) secret_registry_contract.functions.registerSecret( pending_transfers_tree.unlockable[0][TestLockIndex.SECRET], ).transact({'from': A}) assert secret_registry_contract.functions.getSecretRevealBlockHeight( pending_transfers_tree.unlockable[0][TestLockIndex.SECRETHASH], ).call() == web3.eth.blockNumber packed = pending_transfers_tree.packed_transfers # packed length must be a multiple of 96 with pytest.raises(TransactionFailed): network_utils.functions.getMerkleRootAndUnlockedAmountPublic(packed[0:-1]).call() # last merkle tree component only contains expiration + locked_amount with pytest.raises(TransactionFailed): network_utils.functions.getMerkleRootAndUnlockedAmountPublic(packed[0:-32]).call() # last merkle tree component only contains expiration with pytest.raises(TransactionFailed): network_utils.functions.getMerkleRootAndUnlockedAmountPublic(packed[0:-64]).call() assert len(packed) % 96 == 0 network_utils.functions.getMerkleRootAndUnlockedAmountPublic(packed).call() network_utils.functions.getMerkleRootAndUnlockedAmountPublic(packed[0:-96]).call()
def test_unlock_wrong_locksroot( web3, token_network, create_settled_channel, get_accounts, ): (A, B) = get_accounts(2) settle_timeout = 8 pending_transfers_tree_A = get_pending_transfers_tree(web3, [1, 3, 5], [], settle_timeout) pending_transfers_tree_A_fake = get_pending_transfers_tree(web3, [1, 3, 6], [], settle_timeout) channel_identifier = create_settled_channel( A, pending_transfers_tree_A.locked_amount, pending_transfers_tree_A.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A_fake.packed_transfers, ).transact() # Fails for an empty merkle tree with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, b'', ).transact() token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact()
def test_channel_unlock_smaller_locked_amount( web3, token_network, custom_token, secret_registry_contract, create_settled_channel, get_accounts, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Mock pending transfers data pending_transfers_tree_A = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_A.unlockable) unlocked_amount = get_unlocked_amount( secret_registry_contract, pending_transfers_tree_A.packed_transfers, ) # We settle the channel with a smaller locked amount than we will need for the # actual merkle tree of pending transfers channel_identifier = create_settled_channel( A, pending_transfers_tree_A.locked_amount - 1, pending_transfers_tree_A.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_contract == pending_transfers_tree_A.locked_amount - 1 # This should pass, even though the locked amount in storage is smaller. # B will receive less tokens. token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_A == ( pre_balance_A + pending_transfers_tree_A.locked_amount - unlocked_amount - 1 ) assert balance_B == pre_balance_B + unlocked_amount assert balance_contract == 0
def test_channel_unlock_bigger_unlocked_amount( web3, token_network, custom_token, secret_registry_contract, create_settled_channel, get_accounts, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Mock pending transfers data pending_transfers_tree_A = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_A.unlockable) unlocked_amount = get_unlocked_amount( secret_registry_contract, pending_transfers_tree_A.packed_transfers, ) assert unlocked_amount < pending_transfers_tree_A.locked_amount # We settle the channel with a smaller locked amount than the amount that can be unlocked channel_identifier = create_settled_channel( A, unlocked_amount - 1, pending_transfers_tree_A.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_contract == unlocked_amount - 1 # This should pass, even though the locked amount in storage is smaller. # A will receive the entire locked amount, corresponding to the locks that have been unlocked # and B will receive nothing. token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_A == pre_balance_A assert balance_B == pre_balance_B + unlocked_amount - 1 assert balance_contract == 0
def test_merkle_root_odd_even_components( web3, get_accounts, token_network_test_utils, secret_registry_contract, reveal_secrets, ): (A, B) = get_accounts(2) # Even number of merkle tree components pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 8, 3]) reveal_secrets(A, pending_transfers_tree.unlockable) ( locksroot, unlocked_amount, ) = token_network_test_utils.functions.getMerkleRootAndUnlockedAmountPublic( pending_transfers_tree.packed_transfers, ).call() merkle_root = pending_transfers_tree.merkle_root assert locksroot == merkle_root assert unlocked_amount == 9 # Odd number of merkle tree components pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 8]) reveal_secrets(A, pending_transfers_tree.unlockable) ( locksroot, unlocked_amount, ) = token_network_test_utils.functions.getMerkleRootAndUnlockedAmountPublic( pending_transfers_tree.packed_transfers, ).call() merkle_root = pending_transfers_tree.merkle_root assert locksroot == merkle_root assert unlocked_amount == 9
def test_channel_unlock_unregistered_locks( web3, token_network, get_accounts, create_channel_and_deposit, withdraw_channel, close_and_update_channel, custom_token, ): (A, B) = get_accounts(2) settle_timeout = TEST_SETTLE_TIMEOUT_MIN pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) locked_A = pending_transfers_tree.locked_amount (vals_A, vals_B) = ( ChannelValues(deposit=35, withdrawn=10, transferred=0, locked=locked_A), ChannelValues(deposit=40, withdrawn=10, transferred=20, locked=0), ) vals_A.locksroot = '0x' + get_merkle_root(pending_transfers_tree.merkle_tree).hex() vals_B.locksroot = fake_bytes(32, '03') channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(channel_identifier, A, vals_A.withdrawn, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, A) close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) # Secret hasn't been registered before settlement timeout web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # Someone unlocks A's pending transfers - all tokens should be refunded token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree.packed_transfers, ).transact({'from': A}) # A gets back locked tokens assert ( custom_token.functions.balanceOf(A).call() == vals_A.deposit - vals_A.transferred + vals_B.transferred )
def test_unlock_tampered_merkle_proof_fails( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Regular channel life-cycle: open -> settle -> unlock -> open -> settle -> unlock # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree.unlockable) # Settle the channel channel_identifier = create_settled_channel( A, pending_transfers_tree.locked_amount, pending_transfers_tree.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # Unlock with tampered locks does not work types = ['uint256', 'uint256', 'bytes32'] for index in range(len(pending_transfers_tree.transfers)): pending_transfers = list(pending_transfers_tree.transfers) pending_transfers[index][2:] = random_secret() packed_transfers_tampered = get_packed_transfers(tuple(pending_transfers), types) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, packed_transfers_tampered, ).transact({'from': A}) # Unlock with correct merkle tree does work token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree.packed_transfers, ).transact({'from': A})
def test_unlock_fails_with_partial_merkle_proof( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree.unlockable) # Settle the channel channel_identifier = create_settled_channel( A, pending_transfers_tree.locked_amount, pending_transfers_tree.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # Unlock with one leave missing does not work types = ['uint256', 'uint256', 'bytes32'] for index in range(len(pending_transfers_tree.transfers)): pending_transfers = list(pending_transfers_tree.transfers) del pending_transfers[index] packed_transfers_tampered = get_packed_transfers(tuple(pending_transfers), types) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, packed_transfers_tampered, ).transact({'from': A}) # Unlock with full merkle tree does work token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree.packed_transfers, ).transact({'from': A})
def test_unlock_twice_fails( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Regular channel life-cycle: open -> settle -> unlock -> open -> settle -> unlock # Mock pending transfers data pending_transfers_tree_1 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_1.unlockable) # Settle the channel channel_identifier = create_settled_channel( A, pending_transfers_tree_1.locked_amount, pending_transfers_tree_1.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A}) # Calling unlock twice does not work with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A})
def test_channel_unlock_no_locked_amount_fail( web3, token_network, custom_token, secret_registry_contract, create_settled_channel, get_accounts, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Mock pending transfers data pending_transfers_tree_A = get_pending_transfers_tree(web3, [2, 5], [4], settle_timeout) reveal_secrets(A, pending_transfers_tree_A.unlockable) channel_identifier = create_settled_channel( A, 0, EMPTY_LOCKSROOT, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, b'', ).transact() with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact()
def test_channel_unlock_with_a_large_expiration( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues( deposit=20, transferred=5, locked=0, locksroot=EMPTY_LOCKSROOT, ) values_B = ChannelValues( deposit=30, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Mock pending transfers data with large expiration date pending_transfers_tree = get_pending_transfers_tree( web3, [1, 3, 5], [2, 4], settle_timeout + 100, ) values_B.locksroot = pending_transfers_tree.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree.transfers) # Reveal secrets before settlement window ends reveal_secrets(A, pending_transfers_tree.unlockable) close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) # Settle channel after a "long" time web3.testing.mine(settle_timeout + 50) call_settle(token_network, channel_identifier, A, values_A, B, values_B) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() # Unlock the tokens must still work token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_A == pre_balance_A + 9 assert balance_B == pre_balance_B + 6 assert balance_contract == pre_balance_contract - values_B.locked
def test_settle_wrong_balance_hash( web3, get_accounts, token_network, create_channel_and_deposit, close_and_update_channel, get_block, reveal_secrets, ): (A, B) = get_accounts(2) vals_A = ChannelValues( deposit=35, withdrawn=0, transferred=5, claimable_locked=10, unclaimable_locked=2, ) vals_B = ChannelValues( deposit=40, withdrawn=0, transferred=15, claimable_locked=5, unclaimable_locked=4, ) channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) # Mock pending transfers data for A -> B pending_transfers_tree_A = get_pending_transfers_tree( web3, unlockable_amount=vals_A.claimable_locked, expired_amount=vals_A.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.merkle_root # Reveal A's secrets. reveal_secrets(A, pending_transfers_tree_A.unlockable) # Mock pending transfers data for B -> A pending_transfers_tree_B = get_pending_transfers_tree( web3, unlockable_amount=vals_B.claimable_locked, expired_amount=vals_B.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.merkle_root # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, B, vals_A, A, vals_B) vals_A_fail = deepcopy(vals_A) vals_A_fail.transferred += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.transferred = 0 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.transferred = MAX_UINT256 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, B, vals_B, A, vals_A_fail) vals_A_fail = deepcopy(vals_A) vals_A_fail.locked += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.locked = 0 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.locked = MAX_UINT256 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, B, vals_B, A, vals_A_fail) vals_A_fail = deepcopy(vals_A) vals_A_fail.locksroot = EMPTY_LOCKSROOT with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.locksroot = fake_bytes(32, '01') with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_B_fail = deepcopy(vals_B) vals_B_fail.transferred += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail.transferred = 0 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, B, vals_B_fail, A, vals_A) vals_B_fail.transferred = MAX_UINT256 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail = deepcopy(vals_B) vals_B_fail.locked += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail.locked = 0 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, B, vals_B_fail, A, vals_A) vals_B_fail.locked = MAX_UINT256 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail = deepcopy(vals_B) vals_B_fail.locksroot = EMPTY_LOCKSROOT with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail.locksroot = fake_bytes(32, '01') with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) # Channel is settled call_settle(token_network, channel_identifier, A, vals_A, B, vals_B)
def test_channel_settle_invalid_balance_proof_values( web3, get_accounts, custom_token, token_network, create_channel_and_deposit, withdraw_channel, close_and_update_channel, settle_state_tests, reveal_secrets, channel_test_values, ): (A, B, C, D) = get_accounts(4) (vals_A, vals_B) = channel_test_values # We just need to test that settleChannel does not fail # We cannot ensure correctly computed final balances if the balance proofs # are invalid. # We can just test that participants do not get more tokens than the channel deposit # We make sure the contract has more tokens than A, B will deposit create_channel_and_deposit(C, D, 40, 60) # Start channel lifecycle for A, B channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(channel_identifier, A, vals_A.withdrawn, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, A) # For the purpose of this test, it is not important when the secrets are revealed, # as long as the secrets connected to pending transfers that should be finalized, # are revealed before their expiration. # Mock pending transfers data for A -> B pending_transfers_tree_A = get_pending_transfers_tree( web3, unlockable_amount=vals_A.claimable_locked, expired_amount=vals_A.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.merkle_root # Reveal A's secrets. reveal_secrets(A, pending_transfers_tree_A.unlockable) # Mock pending transfers data for B -> A pending_transfers_tree_B = get_pending_transfers_tree( web3, unlockable_amount=vals_B.claimable_locked, expired_amount=vals_B.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.merkle_root # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # We do the balance & state tests here for each channel and also compare with # the expected settlement amounts settle_state_tests( channel_identifier, A, vals_A, B, vals_B, pre_balance_A, pre_balance_B, pre_balance_contract, )
def test_deprecation_switch_settle( web3, get_accounts, token_network, custom_token, reveal_secrets, create_channel, channel_deposit, close_and_update_channel, ): deprecation_executor = token_network.functions.deprecation_executor().call( ) (A, B) = get_accounts(2) deposit = 100 (vals_A, vals_B) = ( ChannelValues( deposit=deposit, withdrawn=0, transferred=5, claimable_locked=2, unclaimable_locked=4, ), ChannelValues( deposit=deposit, withdrawn=0, transferred=10, claimable_locked=4, unclaimable_locked=6, ), ) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf( token_network.address).call() channel_identifier = create_channel(A, B)[0] channel_deposit(channel_identifier, A, vals_A.deposit, B) channel_deposit(channel_identifier, B, vals_B.deposit, A) # Mock pending transfers data for A -> B pending_transfers_tree_A = get_pending_transfers_tree( web3, unlockable_amount=vals_A.claimable_locked, expired_amount=vals_A.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.merkle_root # Reveal A's secrets. reveal_secrets(A, pending_transfers_tree_A.unlockable) # Mock pending transfers data for B -> A pending_transfers_tree_B = get_pending_transfers_tree( web3, unlockable_amount=vals_B.claimable_locked, expired_amount=vals_B.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.merkle_root # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) # Set the deprecation switch to true token_network.functions.deprecate().transact({ 'from': deprecation_executor, }) assert token_network.functions.safety_deprecation_switch().call() is True # We need to make sure we can still close, settle & unlock the channels close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # Unlock B's pending transfers that were sent to A token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers, ).transact() # Unlock A's pending transfers that were sent to B token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() assert custom_token.functions.balanceOf(A).call() == pre_balance_A + 107 assert custom_token.functions.balanceOf(B).call() == pre_balance_B + 93 assert custom_token.functions.balanceOf( token_network.address, ).call() == pre_balance_contract
def test_settle_channel_state( web3, get_accounts, custom_token, token_network, create_channel_and_deposit, withdraw_channel, close_and_update_channel, settle_state_tests, ): (A, B) = get_accounts(2) vals_A = ChannelValues( deposit=40, withdrawn=10, transferred=20020, claimable_locked=3, unclaimable_locked=4, ) vals_B = ChannelValues( deposit=35, withdrawn=5, transferred=20030, claimable_locked=2, unclaimable_locked=3, ) pending_transfers_tree_A = get_pending_transfers_tree( web3, unlockable_amount=vals_A.claimable_locked, expired_amount=vals_A.unclaimable_locked, ) pending_transfers_tree_B = get_pending_transfers_tree( web3, unlockable_amount=vals_B.claimable_locked, expired_amount=vals_B.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.merkle_root vals_B.locksroot = pending_transfers_tree_B.merkle_root channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(channel_identifier, A, vals_A.withdrawn, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, A) close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # Balance & state tests settle_state_tests( channel_identifier, A, vals_A, B, vals_B, pre_balance_A, pre_balance_B, pre_balance_contract, ) # Some manual checks for the final balances, in case the settlement algorithms # used in `settle_state_tests` are incorrect # FIXME after setTotalWithdraw is implemented again post_balance_A = pre_balance_A + 33 post_balance_B = pre_balance_B + 15 post_balance_contract = pre_balance_contract - 48 # FIXME after setTotalWithdraw is implemented again # we add the withdrawn amount here, because it was never withdrawn due to the # removal of setTotalWithdraw assert custom_token.functions.balanceOf(A).call() == post_balance_A + 10 assert custom_token.functions.balanceOf(B).call() == post_balance_B + 5 assert custom_token.functions.balanceOf( token_network.address, ).call() == post_balance_contract - 15
def f( participants, channel_values, expected_settlement0, expected_settlement_onchain0, expected_final_balance_A0, expected_final_balance_B0, ): (A, B) = participants (vals_A, vals_B, balance_proof_type) = channel_values assert were_balance_proofs_valid(vals_A, vals_B) # Start channel lifecycle channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(channel_identifier, A, vals_A.withdrawn, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, A) # For the purpose of this test, it is not important when the secrets are revealed, # as long as the secrets connected to pending transfers that should be finalized, # are revealed before their expiration. # Mock pending transfers data for A -> B pending_transfers_tree_A = get_pending_transfers_tree( web3, unlockable_amount=vals_A.claimable_locked, expired_amount=vals_A.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.merkle_root # Reveal A's secrets. reveal_secrets(A, pending_transfers_tree_A.unlockable) # Mock pending transfers data for B -> A pending_transfers_tree_B = get_pending_transfers_tree( web3, unlockable_amount=vals_B.claimable_locked, expired_amount=vals_B.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.merkle_root # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) close_and_update_channel( channel_identifier, A, vals_A, B, vals_B, ) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # We do the balance & state tests here for each channel and also compare with # the expected settlement amounts settle_state_tests( channel_identifier, A, vals_A, B, vals_B, pre_balance_A, pre_balance_B, pre_balance_contract, ) # We compute again the settlement amounts here to compare with the other channel # settlement test values, which should be equal # Calculate how much A and B should receive settlement_equivalent = get_settlement_amounts(vals_A, vals_B) # Calculate how much A and B receive according to onchain computation settlement_onchain_equivalent = get_onchain_settlement_amounts(vals_A, vals_B) assert ( settlement_equivalent.participant1_balance == settlement_onchain_equivalent.participant1_balance ) assert ( settlement_equivalent.participant2_balance == settlement_onchain_equivalent.participant2_balance ) assert ( settlement_equivalent.participant1_locked == settlement_onchain_equivalent.participant1_locked ) assert ( settlement_equivalent.participant2_locked == settlement_onchain_equivalent.participant2_locked ) assert get_unlocked_amount( secret_registry_contract, pending_transfers_tree_B.packed_transfers, ) == vals_B.claimable_locked # A unlocks B's pending transfers info_B = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A, ).call() assert settlement_equivalent.participant2_locked == info_B[ ParticipantInfoIndex.LOCKED_AMOUNT ] if info_B[ParticipantInfoIndex.LOCKED_AMOUNT] == 0: with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers, ).transact() else: token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers, ).transact() # The locked amount should have been removed from contract storage info_B = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A, ).call() assert info_B[ParticipantInfoIndex.LOCKED_AMOUNT] == 0 assert info_B[ParticipantInfoIndex.LOCKSROOT] == EMPTY_LOCKSROOT # B unlocks A's pending transfers info_A = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B, ).call() assert settlement_equivalent.participant1_locked == info_A[ ParticipantInfoIndex.LOCKED_AMOUNT ] if info_A[ParticipantInfoIndex.LOCKED_AMOUNT] == 0: with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() else: token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() # The locked amount should have been removed from contract storage info_A = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B, ).call() assert info_A[ParticipantInfoIndex.LOCKED_AMOUNT] == 0 assert info_A[ParticipantInfoIndex.LOCKSROOT] == EMPTY_LOCKSROOT # Do the post settlement and unlock tests for valid balance proofs balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() # We MUST ensure balance correctness for valid last balance proofs if balance_proof_type is 'valid': # Calculate how much A and B should receive after the channel is settled and # unlock is called by both. ( expected_final_balance_A, expected_final_balance_B, ) = get_expected_after_settlement_unlock_amounts(vals_A, vals_B) expected_balance_A = pre_balance_A + expected_final_balance_A expected_balance_B = pre_balance_B + expected_final_balance_B assert balance_A == expected_balance_A assert balance_B <= expected_balance_B # For balance proofs where one of them is old, we need to compare with the expected # final balances for the valid last balance proofs expected_balance_A = pre_balance_A + expected_final_balance_A0 expected_balance_B = pre_balance_B + expected_final_balance_B0 # Tests for when B has submitted an old balance proof for A # A must not receive less tokens than expected with a valid last balance proof # B must not receive more tokens than expected with a valid last balance proof if balance_proof_type is 'old_last': assert balance_A >= expected_balance_A assert balance_B <= expected_balance_B # Tests for when A has submitted an old balance proof for B # A must not receive more tokens than expected with a valid last balance proof # B must not receive less tokens than expected with a valid last balance proof if balance_proof_type is 'last_old': assert balance_A <= expected_balance_A assert balance_B >= expected_balance_B # Regardless of the tokens received by the two participants, we must make sure tokens # are not stolen from the other channels. And we must make sure tokens are not locked in # the contract after the entire channel cycle is finalized. final_contract_balance = custom_token.functions.balanceOf(token_network.address).call() assert final_contract_balance == pre_balance_contract - get_total_available_deposit( vals_A, vals_B, ) assert custom_token.functions.balanceOf(token_network.address).call() == ( pre_balance_contract - (expected_final_balance_A0 + expected_final_balance_B0) )
def find_max_pending_transfers(gas_limit): """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract('SecretRegistry') tester.deploy_contract( 'HumanStandardToken', _initialAmount=100000, _decimalUnits=3, _tokenName='SomeToken', _tokenSymbol='SMT', ) tester.deploy_contract( 'TokenNetwork', _token_address=tester.contract_address('HumanStandardToken'), _secret_registry=tester.contract_address('SecretRegistry'), _chain_id=1, _settlement_timeout_min=100, _settlement_timeout_max=200, ) tester.call_transaction( 'HumanStandardToken', 'transfer', _to=tester.accounts[1], _value=10000, ) receipt = tester.call_transaction( 'TokenNetwork', 'openChannel', participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = int(hexlify(receipt['logs'][0]['topics'][1]), 16) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[0], _spender=tester.contract_address('TokenNetwork'), _value=10000, ) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[1], _spender=tester.contract_address('TokenNetwork'), _value=5000, ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print( "Measuring unlock()'s gas cost for different Merkle tree widths, can take a while..." ) before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = 10 additional_hash = urandom(32) token_network_identifier = tester.contract_address('TokenNetwork') while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size, ) balance_hash = hash_balance_data(3000, 2000, pending_transfers_tree.merkle_root) data_to_sign = pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=additional_hash, channel_identifier=channel_identifier, token_network_identifier=token_network_identifier, chain_id=1, ) signature = eth_sign(privkey=tester.private_keys[1], data=data_to_sign) tester.call_transaction( 'TokenNetwork', 'closeChannel', channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( 'TokenNetwork', 'settleChannel', channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b'\x00' * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree.merkle_root, ) receipt = tester.call_transaction( 'TokenNetwork', 'unlock', channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt['gasUsed'] if gas_used <= gas_limit: enough = tree_size print( f'{tree_size} pending transfers work ({gas_used} gas needed to unlock)' ) else: too_much = tree_size print( f'{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)' )
def test_channel_unlock_both_participants( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues( deposit=100, transferred=5, ) values_B = ChannelValues( deposit=100, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Mock pending transfers data for A pending_transfers_tree_A = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) values_A.locksroot = pending_transfers_tree_A.merkle_root values_A.locked = get_locked_amount(pending_transfers_tree_A.transfers) # Reveal A's secrets before settlement window ends reveal_secrets(A, pending_transfers_tree_A.unlockable) # Mock pending transfers data for B pending_transfers_tree_B = get_pending_transfers_tree(web3, [2, 4, 6], [5, 10], settle_timeout) values_B.locksroot = pending_transfers_tree_B.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree_B.transfers) # Reveal B's secrets before settlement window ends reveal_secrets(B, pending_transfers_tree_B.unlockable) close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) # Settle channel web3.testing.mine(settle_timeout) call_settle(token_network, channel_identifier, A, values_A, B, values_B) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() # A unlock's token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers, ).transact() # B unlock's token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() # Unlocked pending transfers A -> B, that belong to B unlockable_A = get_unlocked_amount( secret_registry_contract, pending_transfers_tree_A.packed_transfers, ) # Expired pending transfers A -> B, that belong to A expired_A = get_locked_amount(pending_transfers_tree_A.expired) # Unlocked pending transfers B -> A, that belong to A unlockable_B = get_unlocked_amount( secret_registry_contract, pending_transfers_tree_B.packed_transfers, ) # Expired pending transfers B -> A, that belong to B expired_B = get_locked_amount(pending_transfers_tree_B.expired) # check that A and B both received the expected amounts assert balance_contract == pre_balance_contract - values_B.locked - values_A.locked assert balance_A == pre_balance_A + unlockable_B + expired_A assert balance_B == pre_balance_B + unlockable_A + expired_B
def test_channel_unlock( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, event_handler, ): (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues( deposit=20, transferred=5, locked=0, locksroot=EMPTY_LOCKSROOT, ) values_B = ChannelValues( deposit=30, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) values_B.locksroot = pending_transfers_tree.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree.transfers) # Reveal secrets before settlement window ends reveal_secrets(A, pending_transfers_tree.unlockable) close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) # Settlement window must be over before settling the channel web3.testing.mine(settle_timeout) call_settle(token_network, channel_identifier, A, values_A, B, values_B) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() info_B = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A, ).call() assert info_B[ParticipantInfoIndex.LOCKSROOT] == values_B.locksroot assert info_B[ParticipantInfoIndex.LOCKED_AMOUNT] == values_B.locked # Unlock the tokens token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() info_B = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A, ).call() assert info_B[ParticipantInfoIndex.LOCKSROOT] == EMPTY_LOCKSROOT assert info_B[ParticipantInfoIndex.LOCKED_AMOUNT] == 0 balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_A == pre_balance_A + 9 assert balance_B == pre_balance_B + 6 assert balance_contract == pre_balance_contract - values_B.locked
def test_channel_settle_and_unlock( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Regular channel life-cycle: open -> settle -> unlock -> open -> settle -> unlock # Mock pending transfers data pending_transfers_tree_1 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_1.unlockable) # Settle the channel channel_identifier1 = create_settled_channel( A, pending_transfers_tree_1.locked_amount, pending_transfers_tree_1.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) token_network.functions.unlock( channel_identifier1, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A}) # Mock pending transfers data for a reopened channel pending_transfers_tree_2 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_2.unlockable) # Settle the channel again channel_identifier2 = create_settled_channel( A, pending_transfers_tree_2.locked_amount, pending_transfers_tree_2.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # 2nd unlocks should go through token_network.functions.unlock( channel_identifier2, B, A, pending_transfers_tree_2.packed_transfers, ).transact({'from': A}) # Edge channel life-cycle: open -> settle -> open -> settle -> unlock1 -> unlock2 # Mock pending transfers data pending_transfers_tree_1 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_1.unlockable) # Settle the channel channel_identifier3 = create_settled_channel( A, pending_transfers_tree_1.locked_amount, pending_transfers_tree_1.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # Mock pending transfers data for a reopened channel pending_transfers_tree_2 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_2.unlockable) # Settle the channel again channel_identifier4 = create_settled_channel( A, pending_transfers_tree_2.locked_amount, pending_transfers_tree_2.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # Both old and new unlocks should go through token_network.functions.unlock( channel_identifier4, B, A, pending_transfers_tree_2.packed_transfers, ).transact({'from': A}) token_network.functions.unlock( channel_identifier3, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A})
def test_merkle_tree_components_order( web3, get_accounts, token_network_test_utils, secret_registry_contract, reveal_secrets, token_network, create_settled_channel, ): network_utils = token_network_test_utils (A, B) = get_accounts(2) types = ['uint256', 'uint256', 'bytes32'] pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 8, 3]) reveal_secrets(A, pending_transfers_tree.unlockable) channel_identifier = create_settled_channel( A, pending_transfers_tree.locked_amount, pending_transfers_tree.merkle_root, B, 0, EMPTY_LOCKSROOT, ) # Merkle tree lockhashes are ordered lexicographicaly. # If we change the order, we change the computed merkle root. # However, the getMerkleRootAndUnlockedAmount orders neighbouring lockhashes # lexicographicaly, so simple item[i], item[i + 1] swap # will still result in the same merkle root. wrong_order = pending_transfers_tree.transfers wrong_order[1], wrong_order[0] = wrong_order[0], wrong_order[1] wrong_order_packed = get_packed_transfers(wrong_order, types) ( locksroot, unlocked_amount, ) = network_utils.functions.getMerkleRootAndUnlockedAmountPublic( wrong_order_packed, ).call() # Same merkle root this time assert locksroot == pending_transfers_tree.merkle_root assert unlocked_amount == 9 token_network.functions.unlock( channel_identifier, B, A, wrong_order_packed, ).call() wrong_order = pending_transfers_tree.transfers wrong_order[2], wrong_order[0] = wrong_order[0], wrong_order[2] wrong_order_packed = get_packed_transfers(wrong_order, types) ( locksroot, unlocked_amount, ) = network_utils.functions.getMerkleRootAndUnlockedAmountPublic( wrong_order_packed, ).call() assert locksroot != pending_transfers_tree.merkle_root assert unlocked_amount == 9 with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, wrong_order_packed, ).transact() wrong_order = pending_transfers_tree.transfers wrong_order[0], wrong_order[-1] = wrong_order[-1], wrong_order[0] wrong_order_packed = get_packed_transfers(wrong_order, types) ( locksroot, unlocked_amount, ) = network_utils.functions.getMerkleRootAndUnlockedAmountPublic( wrong_order_packed, ).call() assert locksroot != pending_transfers_tree.merkle_root assert unlocked_amount == 9 with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, wrong_order_packed, ).transact() ( locksroot, unlocked_amount, ) = network_utils.functions.getMerkleRootAndUnlockedAmountPublic( pending_transfers_tree.packed_transfers, ).call() assert locksroot == pending_transfers_tree.merkle_root assert unlocked_amount == 9 token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree.packed_transfers, ).transact()
def test_reverse_participants_unlock( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B, C) = get_accounts(3) settle_timeout = 12 # Mock pending transfers data pending_transfers_tree_A = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) pending_transfers_tree_B = get_pending_transfers_tree(web3, [3, 4, 6], [4], settle_timeout) assert pending_transfers_tree_A.merkle_root != pending_transfers_tree_B.merkle_root # Reveal secrets reveal_secrets(A, pending_transfers_tree_A.unlockable) reveal_secrets(B, pending_transfers_tree_B.unlockable) # Settle the channel channel_identifier = create_settled_channel( A, pending_transfers_tree_A.locked_amount, pending_transfers_tree_A.merkle_root, B, pending_transfers_tree_B.locked_amount, pending_transfers_tree_B.merkle_root, settle_timeout, ) # A trying to unlock its own locksroot & locked amount MUST fail with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_A.packed_transfers, ).transact({'from': A}) # Delegate trying to unlock A's own locksroot & locked amount on behalf of A MUST fail with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_A.packed_transfers, ).transact({'from': C}) # B trying to unlock its own locksroot & locked amount MUST fail with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_B.packed_transfers, ).transact({'from': B}) # Delegate trying to unlock B's own locksroot & locked amount on behalf of B MUST fail with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_B.packed_transfers, ).transact({'from': B}) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, A, pending_transfers_tree_A.packed_transfers, ).transact({'from': A}) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, B, pending_transfers_tree_B.packed_transfers, ).transact({'from': B}) # Someone trying to unlock B's locksroot & locked amount on behalf of A MUST succeed token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers, ).transact({'from': C}) # Someone trying to unlock A's locksroot & locked amount on behalf of B MUST succeed token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers, ).transact({'from': C})
def test_channel_unlock_registered_expired_lock_refunds( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, reveal_secrets, close_and_update_channel, ): (A, B) = get_accounts(2) max_lock_expiration = 3 settle_timeout = 8 values_A = ChannelValues( deposit=20, transferred=5, locked=0, locksroot=EMPTY_LOCKSROOT, ) values_B = ChannelValues( deposit=30, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree( web3, [1, 3, 5], [2, 4], min_expiration_delta=max_lock_expiration - 2, max_expiration_delta=max_lock_expiration, ) values_B.locksroot = pending_transfers_tree.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree.transfers) # Locks expire web3.testing.mine(max_lock_expiration) # Secrets are revealed before settlement window, but after expiration for (_, _, secrethash, secret) in pending_transfers_tree.unlockable: secret_registry_contract.functions.registerSecret(secret).transact({'from': A}) assert secret_registry_contract.functions.getSecretRevealBlockHeight( secrethash, ).call() == web3.eth.blockNumber close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) web3.testing.mine(settle_timeout) # settle channel call_settle(token_network, channel_identifier, A, values_A, B, values_B) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() # Unlock works after channel is settled token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() # check that all tokens have been refunded, as locks have expired already assert balance_A == pre_balance_A assert balance_B == pre_balance_B + values_B.locked assert balance_contract == pre_balance_contract - values_B.locked
def test_unlock_different_channel_same_participants_fail( web3, token_network, get_accounts, create_settled_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 # Mock pending transfers data pending_transfers_tree_1 = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) reveal_secrets(A, pending_transfers_tree_1.unlockable) channel_identifier = create_settled_channel( A, pending_transfers_tree_1.locked_amount, pending_transfers_tree_1.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) # The first channel is settled, so we create another one pending_transfers_tree_2 = get_pending_transfers_tree(web3, [3, 5], [2, 4, 3], settle_timeout) reveal_secrets(A, pending_transfers_tree_2.unlockable) channel_identifier2 = create_settled_channel( A, pending_transfers_tree_2.locked_amount, pending_transfers_tree_2.merkle_root, B, 0, EMPTY_LOCKSROOT, settle_timeout, ) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_2.packed_transfers, ).transact({'from': A}) with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier2, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A}) token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_1.packed_transfers, ).transact({'from': A}) token_network.functions.unlock( channel_identifier2, B, A, pending_transfers_tree_2.packed_transfers, ).transact({'from': A})
def test_channel_unlock_before_settlement_fails( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, ): (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues( deposit=20, transferred=5, locked=0, locksroot=EMPTY_LOCKSROOT, ) values_B = ChannelValues( deposit=30, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree(web3, [1, 3, 5], [2, 4], settle_timeout) values_B.locksroot = pending_transfers_tree.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree.transfers) # Reveal secrets before settlement window ends reveal_secrets(A, pending_transfers_tree.unlockable) # Unlock fails before channel is not settled with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Unlock fails before channel is not settled with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) # Unlock fails before settlement window is over and channel is not settled with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() # Settlement window must be over before settling the channel web3.testing.mine(settle_timeout) # Unlock fails before settle is called with pytest.raises(TransactionFailed): token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() # settle channel call_settle(token_network, channel_identifier, A, values_A, B, values_B) pre_balance_A = custom_token.functions.balanceOf(A).call() pre_balance_B = custom_token.functions.balanceOf(B).call() pre_balance_contract = custom_token.functions.balanceOf(token_network.address).call() # Unlock works after channel is settled token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() balance_A = custom_token.functions.balanceOf(A).call() balance_B = custom_token.functions.balanceOf(B).call() balance_contract = custom_token.functions.balanceOf(token_network.address).call() assert balance_A == pre_balance_A + 9 assert balance_B == pre_balance_B + 6 assert balance_contract == pre_balance_contract - values_B.locked
def test_unlock_channel_event( web3, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, event_handler, ): (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues( deposit=20, transferred=5, locked=0, locksroot=EMPTY_LOCKSROOT, ) values_B = ChannelValues( deposit=30, transferred=40, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, values_A.deposit, B) channel_deposit(channel_identifier, B, values_B.deposit, A) # Mock pending transfers data pending_transfers_tree = get_pending_transfers_tree( web3, [1, 3, 5], [2, 4], settle_timeout + 100, ) values_B.locksroot = pending_transfers_tree.merkle_root values_B.locked = get_locked_amount(pending_transfers_tree.transfers) # Reveal secrets before settlement window ends reveal_secrets(A, pending_transfers_tree.unlockable) close_and_update_channel( channel_identifier, A, values_A, B, values_B, ) # Settlement window must be over before settling the channel web3.testing.mine(settle_timeout) call_settle(token_network, channel_identifier, A, values_A, B, values_B) ev_handler = event_handler(token_network) # Unlock the tokens txn_hash = token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree.packed_transfers, ).transact() unlocked_amount = get_unlocked_amount( secret_registry_contract, pending_transfers_tree.packed_transfers, ) # Add event ev_handler.add(txn_hash, ChannelEvent.UNLOCKED, check_channel_unlocked( channel_identifier, A, B, values_B.locksroot, unlocked_amount, values_B.locked - unlocked_amount, )) # Check that event was properly emitted ev_handler.check()
def find_max_pending_transfers(gas_limit): """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract('SecretRegistry') tester.deploy_contract( 'HumanStandardToken', _initialAmount=100000, _decimalUnits=3, _tokenName='SomeToken', _tokenSymbol='SMT', ) tester.deploy_contract( 'TokenNetwork', _token_address=tester.contract_address('HumanStandardToken'), _secret_registry=tester.contract_address('SecretRegistry'), _chain_id=1, _settlement_timeout_min=100, _settlement_timeout_max=200, ) tester.call_transaction( 'HumanStandardToken', 'transfer', _to=tester.accounts[1], _value=10000, ) receipt = tester.call_transaction( 'TokenNetwork', 'openChannel', participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = int(encode_hex(receipt['logs'][0]['topics'][1]), 16) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[0], _spender=tester.contract_address('TokenNetwork'), _value=10000, ) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[1], _spender=tester.contract_address('TokenNetwork'), _value=5000, ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print("Measuring unlock()'s gas cost for different Merkle tree widths, can take a while...") before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = 10 additional_hash = urandom(32) token_network_identifier = tester.contract_address('TokenNetwork') while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size, ) balance_hash = hash_balance_data(3000, 2000, pending_transfers_tree.merkle_root) data_to_sign = pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=additional_hash, channel_identifier=channel_identifier, token_network_identifier=token_network_identifier, chain_id=1, ) signature = eth_sign(privkey=tester.private_keys[1], data=data_to_sign) tester.call_transaction( 'TokenNetwork', 'closeChannel', channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( 'TokenNetwork', 'settleChannel', channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b'\x00' * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree.merkle_root, ) receipt = tester.call_transaction( 'TokenNetwork', 'unlock', channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt['gasUsed'] if gas_used <= gas_limit: enough = tree_size print(f'{tree_size} pending transfers work ({gas_used} gas needed to unlock)') else: too_much = tree_size print(f'{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)')
def test_lock_data_from_merkle_tree( web3, get_accounts, token_network_test_utils, secret_registry_contract, reveal_secrets, ): network_utils = token_network_test_utils (A, B) = get_accounts(2) unlockable_amounts = [3, 5] expired_amounts = [2, 8, 7] pending_transfers_tree = get_pending_transfers_tree( web3, unlockable_amounts, expired_amounts, max_expiration_delta=5, ) reveal_secrets(A, pending_transfers_tree.unlockable) def claimable(index): amount = pending_transfers_tree.transfers[index][1] return amount if amount in unlockable_amounts else 0 def get_lockhash(index): return pending_transfers_tree.merkle_tree.layers[0][index] # Lock data is ordered lexicographically, regardless of expiration status (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32, ).call() assert lockhash == get_lockhash(0) assert claimable_amount == claimable(0) (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 96, ).call() assert lockhash == get_lockhash(1) assert claimable_amount == claimable(1) (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 2 * 96, ).call() assert lockhash == get_lockhash(2) assert claimable_amount == claimable(2) (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 3 * 96, ).call() assert lockhash == get_lockhash(3) assert claimable_amount == claimable(3) (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 4 * 96, ).call() assert lockhash == get_lockhash(4) assert claimable_amount == claimable(4) # Register last secret after expiration web3.testing.mine(5) last_lock = pending_transfers_tree.expired[-1] # expiration assert web3.eth.blockNumber > last_lock[0] # register secret secret_registry_contract.functions.registerSecret(last_lock[3]).transact() # ensure registration was done assert secret_registry_contract.functions.getSecretRevealBlockHeight( last_lock[2], ).call() == web3.eth.blockNumber # Check that last secret is still regarded as expired (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 4 * 96, ).call() assert lockhash == get_lockhash(4) assert claimable_amount == claimable(4) # If the offset is bigger than the length of the merkle tree, return (0, 0) (lockhash, claimable_amount) = network_utils.functions.getLockDataFromMerkleTreePublic( pending_transfers_tree.packed_transfers, 32 + 5 * 96, ).call() assert lockhash == b'\x00' * 32 assert claimable_amount == 0
def test_channel_cycle( web3, token_network, create_channel, channel_deposit, secret_registry_contract, get_accounts, print_gas, create_balance_proof, create_balance_proof_update_signature, ): (A, B) = get_accounts(2) settle_timeout = 11 (channel_identifier, txn_hash) = create_channel(A, B, settle_timeout) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + '.openChannel') txn_hash = channel_deposit(channel_identifier, A, 20, B) txn_hash = channel_deposit(channel_identifier, B, 10, A) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + '.setTotalDeposit') pending_transfers_tree1 = get_pending_transfers_tree( web3, [1, 1, 2, 3], [2, 1]) locksroot1 = get_merkle_root(pending_transfers_tree1.merkle_tree) locked_amount1 = get_locked_amount(pending_transfers_tree1.transfers) pending_transfers_tree2 = get_pending_transfers_tree(web3, [3], [], 7) locksroot2 = get_merkle_root(pending_transfers_tree2.merkle_tree) locked_amount2 = get_locked_amount(pending_transfers_tree2.transfers) balance_proof_A = create_balance_proof( channel_identifier, A, 10, locked_amount1, 5, locksroot1, ) balance_proof_B = create_balance_proof( channel_identifier, B, 5, locked_amount2, 3, locksroot2, ) balance_proof_update_signature_B = create_balance_proof_update_signature( B, channel_identifier, *balance_proof_A, ) for lock in pending_transfers_tree1.unlockable: txn_hash = secret_registry_contract.functions.registerSecret( lock[3]).transact({'from': A}) for lock in pending_transfers_tree2.unlockable: txn_hash = secret_registry_contract.functions.registerSecret( lock[3]).transact({'from': A}) print_gas(txn_hash, CONTRACT_SECRET_REGISTRY + '.registerSecret') txn_hash = token_network.functions.closeChannel( channel_identifier, B, *balance_proof_B, ).transact({'from': A}) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + '.closeChannel') txn_hash = token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A, balance_proof_update_signature_B, ).transact({'from': B}) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + '.updateNonClosingBalanceProof') web3.testing.mine(settle_timeout) txn_hash = token_network.functions.settleChannel( channel_identifier, B, 5, locked_amount2, locksroot2, A, 10, locked_amount1, locksroot1, ).transact() print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + '.settleChannel') txn_hash = token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree2.packed_transfers, ).transact() print_gas( txn_hash, '{0}.unlock {1} locks'.format( CONTRACT_TOKEN_NETWORK, len(pending_transfers_tree2.transfers), )) txn_hash = token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree1.packed_transfers, ).transact() print_gas( txn_hash, '{0}.unlock {1} locks'.format( CONTRACT_TOKEN_NETWORK, len(pending_transfers_tree1.transfers), ))
def find_max_pending_transfers(gas_limit): """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract("SecretRegistry") tester.deploy_contract( "HumanStandardToken", _initialAmount=100000, _decimalUnits=3, _tokenName="SomeToken", _tokenSymbol="SMT", ) tester.deploy_contract( "TokenNetwork", _token_address=tester.contract_address("HumanStandardToken"), _secret_registry=tester.contract_address("SecretRegistry"), _chain_id=1, _settlement_timeout_min=100, _settlement_timeout_max=200, ) tester.call_transaction("HumanStandardToken", "transfer", _to=tester.accounts[1], _value=10000) receipt = tester.call_transaction( "TokenNetwork", "openChannel", participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = int(encode_hex(receipt["logs"][0]["topics"][1]), 16) tester.call_transaction( "HumanStandardToken", "approve", sender=tester.accounts[0], _spender=tester.contract_address("TokenNetwork"), _value=10000, ) tester.call_transaction( "HumanStandardToken", "approve", sender=tester.accounts[1], _spender=tester.contract_address("TokenNetwork"), _value=5000, ) tester.call_transaction( "TokenNetwork", "setTotalDeposit", channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( "TokenNetwork", "setTotalDeposit", channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print( "Measuring unlock()'s gas cost for different Merkle tree widths, can take a while..." ) before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = 10 additional_hash = urandom(32) token_network_identifier = tester.contract_address("TokenNetwork") while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size) balance_hash = hash_balance_data(3000, 2000, pending_transfers_tree.merkle_root) # FIXME: outdated data_to_sign = pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=additional_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=1, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) signature = LocalSigner(tester.private_keys[1]).sign(data=data_to_sign) tester.call_transaction( "TokenNetwork", "closeChannel", channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( "TokenNetwork", "settleChannel", channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b"\x00" * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree.merkle_root, ) receipt = tester.call_transaction( "TokenNetwork", "unlock", channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt["gasUsed"] if gas_used <= gas_limit: enough = tree_size print( f"{tree_size} pending transfers work ({gas_used} gas needed to unlock)" ) else: too_much = tree_size print( f"{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)" )