def test_settlement_with_unauthorized_token_transfer( web3, get_accounts, custom_token, token_network, create_channel_and_deposit, withdraw_channel, close_and_update_channel, ): externally_transferred_amount = 5 (A, B) = get_accounts(2) (vals_A, vals_B) = ( ChannelValues(deposit=35, withdrawn=10, transferred=0, locked=0), ChannelValues(deposit=40, withdrawn=10, transferred=0, locked=0), ) vals_A.locksroot = fake_bytes(32, '02') vals_B.locksroot = fake_bytes(32, '03') create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(A, vals_A.withdrawn, B) withdraw_channel(B, vals_B.withdrawn, A) close_and_update_channel( A, vals_A, B, vals_B, ) # A does a transfer to the token_network without appropriate function call - tokens are lost custom_token.functions.transfer( token_network.address, externally_transferred_amount, ).transact({'from': A}) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) # Compute expected settlement amounts settlement = get_settlement_amounts(vals_A, vals_B) # Channel is settled call_settle(token_network, A, vals_A, B, vals_B) # Fetch onchain balances after settlement post_balance_A = custom_token.functions.balanceOf(A).call() post_balance_B = custom_token.functions.balanceOf(B).call() post_balance_contract = custom_token.functions.balanceOf( token_network.address).call() # A has lost the externally_transferred_amount assert (vals_A.withdrawn + settlement.participant1_balance - externally_transferred_amount == post_balance_A) # B's settlement works correctly assert (settlement.participant2_balance + vals_B.withdrawn == post_balance_B) # The externally_transferred_amount stays in the contract assert (post_balance_contract == externally_transferred_amount)
def test_settle_wrong_state_fail( web3, get_accounts, token_network, create_channel_and_deposit, get_block, ): """ settleChannel() fails on OPENED state and on CLOSED state before the settlement block """ (A, B) = get_accounts(2) vals_A = ChannelValues(deposit=35) vals_B = ChannelValues(deposit=40) channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) (settle_timeout, state) = token_network.functions.getChannelInfo( channel_identifier, A, B, ).call() assert state == ChannelState.OPENED assert settle_timeout == TEST_SETTLE_TIMEOUT_MIN with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) txn_hash = token_network.functions.closeChannel( channel_identifier, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, ).transact({'from': A}) (settle_block_number, state) = token_network.functions.getChannelInfo( channel_identifier, A, B, ).call() assert state == ChannelState.CLOSED assert settle_block_number == TEST_SETTLE_TIMEOUT_MIN + get_block(txn_hash) assert web3.eth.blockNumber < settle_block_number with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) assert web3.eth.blockNumber >= settle_block_number # Channel is settled call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) (settle_block_number, state) = token_network.functions.getChannelInfo( channel_identifier, A, B, ).call() assert state == ChannelState.REMOVED assert settle_block_number == 0
def test_settle_single_direct_transfer_for_closing_party( web3, get_accounts, custom_token, token_network, create_channel, channel_deposit, create_balance_proof, ): """ Test settle of a channel with one direct transfer to the participant that called close. """ (A, B) = get_accounts(2) (vals_A, vals_B) = ( ChannelValues(deposit=1, withdrawn=0, transferred=0), ChannelValues(deposit=10, withdrawn=0, transferred=5), ) settle_timeout = TEST_SETTLE_TIMEOUT_MIN 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) balance_proof_B = create_balance_proof(channel_identifier, B, vals_B.transferred, vals_B.locked_amounts.locked, 1, EMPTY_LOCKSROOT) token_network.functions.closeChannel( channel_identifier, B, *balance_proof_B).call_and_transact({"from": A}) 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() web3.testing.mine(settle_timeout + 1) token_network.functions.settleChannel( channel_identifier=channel_identifier, participant1=A, participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=EMPTY_LOCKSROOT, participant2=B, participant2_transferred_amount=vals_B.transferred, participant2_locked_amount=0, participant2_locksroot=EMPTY_LOCKSROOT, ).call_and_transact({"from": A}) # Calculate how much A and B should receive expected_settlement = get_settlement_amounts(vals_A, vals_B) # Calculate how much A and B receive according to onchain computation onchain_settlement = get_onchain_settlement_amounts(vals_A, vals_B) assert expected_settlement.participant1_balance == onchain_settlement.participant1_balance assert expected_settlement.participant2_balance == onchain_settlement.participant2_balance assert custom_token.functions.balanceOf(A).call() == pre_balance_A + 6 assert custom_token.functions.balanceOf(B).call() == pre_balance_B + 5 assert (custom_token.functions.balanceOf( token_network.address).call() == pre_balance_contract - 11)
def test_settle_wrong_state_fail( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel_and_deposit: Callable, get_block: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """ settleChannel() fails on OPENED state and on CLOSED state before the settlement block """ (A, B) = get_accounts(2) vals_A = ChannelValues(deposit=35) vals_B = ChannelValues(deposit=40) channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) (settle_timeout, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.OPENED assert settle_timeout == TEST_SETTLE_TIMEOUT_MIN with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) closing_sig = create_close_signature_for_no_balance_proof( A, channel_identifier) txn_hash = token_network.functions.closeChannel( channel_identifier, B, A, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ).call_and_transact({"from": A}) (settle_block_number, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.CLOSED assert settle_block_number == TEST_SETTLE_TIMEOUT_MIN + get_block(txn_hash) assert web3.eth.blockNumber < settle_block_number with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN + 1) assert web3.eth.blockNumber > settle_block_number # Channel is settled call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) (settle_block_number, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.REMOVED assert settle_block_number == 0
def test_close_replay_reopened_channel(web3, get_accounts, token_network, create_channel, channel_deposit, create_balance_proof): """ The same balance proof cannot close another channel between the same participants """ (A, B) = get_accounts(2) nonce = 3 values_A = ChannelValues(deposit=10, transferred=0) values_B = ChannelValues(deposit=20, transferred=15) channel_identifier1 = create_channel(A, B)[0] channel_deposit(channel_identifier1, B, values_B.deposit, A) balance_proof_B = create_balance_proof( channel_identifier1, B, values_B.transferred, values_B.locked_amounts.locked, nonce, values_B.locksroot, ) token_network.functions.closeChannel(channel_identifier1, B, *balance_proof_B).call_and_transact( {"from": A}) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN + 1) token_network.functions.settleChannel( channel_identifier=channel_identifier1, participant1=A, participant1_transferred_amount=values_A.transferred, participant1_locked_amount=values_A.locked_amounts.locked, participant1_locksroot=values_A.locksroot, participant2=B, participant2_transferred_amount=values_B.transferred, participant2_locked_amount=values_B.locked_amounts.locked, participant2_locksroot=values_B.locksroot, ).call_and_transact({"from": A}) # Reopen the channel and make sure we cannot use the old balance proof channel_identifier2 = create_channel(A, B)[0] channel_deposit(channel_identifier2, B, values_B.deposit, A) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed): token_network.functions.closeChannel( channel_identifier2, B, *balance_proof_B).call({"from": A}) # Balance proof with correct channel_identifier must work balance_proof_B2 = create_balance_proof( channel_identifier2, B, values_B.transferred, values_B.locked_amounts.locked, nonce, values_B.locksroot, ) token_network.functions.closeChannel(channel_identifier2, B, *balance_proof_B2).call_and_transact( {"from": A})
def test_channel_unlock_with_a_large_expiration( web3, custom_token, token_network, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, ): """ unlock() should still work after a delayed settleChannel() call """ (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues(deposit=20, transferred=5) 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_amounts = LockedAmounts( claimable_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).call_and_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_amounts.locked
def test_settle_with_locked_but_unregistered( 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_deposit_wrong_state_fail( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel: Callable, assign_tokens: Callable, ) -> None: """ setTotalDeposit() fails on Closed or Settled channels. """ (A, B) = get_accounts(2) vals_A = ChannelValues(deposit=2, transferred=0) vals_B = ChannelValues(deposit=2, transferred=0) channel_identifier = create_channel(A, B, TEST_SETTLE_TIMEOUT_MIN)[0] assign_tokens(A, vals_A.deposit) assign_tokens(B, vals_B.deposit) token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B).call_and_transact({"from": A}) token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A).call_and_transact({"from": B}) token_network.functions.closeChannel( channel_identifier, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE).call_and_transact({"from": A}) assign_tokens(A, 10) assign_tokens(B, 10) vals_A.deposit += 5 vals_B.deposit += 5 with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B).call({"from": A}) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A).call({"from": B}) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN + 1) call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B).call({"from": A}) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A).call({"from": B})
def test_channel_unlock_unregistered_locks( web3: Web3, token_network: Contract, get_accounts: Callable, create_channel_and_deposit: Callable, withdraw_channel: Callable, close_and_update_channel: Callable, custom_token: Contract, ) -> None: """ unlock() should refund tokens locked by secrets not registered before settlement """ (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_amounts=LockedAmounts(claimable_locked=locked_A), ), ChannelValues(deposit=40, withdrawn=10, transferred=20), ) vals_A.locksroot = pending_transfers_tree.hash_of_packed_transfers 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 + 1) 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 ).call_and_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 get( participant1, locked_amount1, locksroot1, participant2, locked_amount2, locksroot2, settle_timeout=TEST_SETTLE_TIMEOUT_MIN, ): participant1_values = ChannelValues( transferred=5, locked=locked_amount1, locksroot=locksroot1, ) participant2_values = ChannelValues( transferred=40, locked=locked_amount2, locksroot=locksroot2, ) participant1_values.deposit = ( participant1_values.locked + participant1_values.transferred - 5 ) participant2_values.deposit = ( participant2_values.locked + participant2_values.transferred + 5 ) channel_identifier = create_channel_and_deposit( participant1, participant2, participant1_values.deposit, participant2_values.deposit, settle_timeout, ) close_and_update_channel( channel_identifier, participant1, participant1_values, participant2, participant2_values, ) web3.testing.mine(settle_timeout) call_settle( token_network, channel_identifier, participant1, participant1_values, participant2, participant2_values, ) return channel_identifier
def get( participant1: HexAddress, locked_amount1: int, locksroot1: bytes, participant2: HexAddress, locked_amount2: int, locksroot2: bytes, settle_timeout: int = TEST_SETTLE_TIMEOUT_MIN, ) -> int: participant1_values = ChannelValues( transferred=5, locked_amounts=LockedAmounts(claimable_locked=locked_amount1), locksroot=locksroot1, ) participant2_values = ChannelValues( transferred=40, locked_amounts=LockedAmounts(claimable_locked=locked_amount2), locksroot=locksroot2, ) participant1_values.deposit = ( participant1_values.locked_amounts.locked + participant1_values.transferred - 5) participant2_values.deposit = ( participant2_values.locked_amounts.locked + participant2_values.transferred + 5) channel_identifier = create_channel_and_deposit( participant1, participant2, participant1_values.deposit, participant2_values.deposit, settle_timeout, ) close_and_update_channel( channel_identifier, participant1, participant1_values, participant2, participant2_values, ) mine_blocks(web3, settle_timeout) call_settle( token_network, channel_identifier, participant1, participant1_values, participant2, participant2_values, ) return channel_identifier
def test_close_replay_reopened_channel( web3, get_accounts, token_network, create_channel, channel_deposit, create_balance_proof, ): (A, B) = get_accounts(2) nonce = 3 values_A = ChannelValues( deposit=10, transferred=0, locked=0, locksroot=EMPTY_MERKLE_ROOT, ) values_B = ChannelValues( deposit=20, transferred=15, locked=0, locksroot=EMPTY_MERKLE_ROOT, ) channel_identifier1 = create_channel(A, B)[0] channel_deposit(channel_identifier1, B, values_B.deposit, A) balance_proof_B = create_balance_proof( channel_identifier1, B, values_B.transferred, values_B.locked, nonce, values_B.locksroot, ) token_network.functions.closeChannel( channel_identifier1, B, *balance_proof_B, ).transact({'from': A}) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN) token_network.functions.settleChannel( channel_identifier1, A, values_A.transferred, values_A.locked, values_A.locksroot, B, values_B.transferred, values_B.locked, values_B.locksroot, ).transact({'from': A}) # Reopen the channel and make sure we cannot use the old balance proof channel_identifier2 = create_channel(A, B)[0] channel_deposit(channel_identifier2, B, values_B.deposit, A) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed): token_network.functions.closeChannel( channel_identifier2, B, *balance_proof_B, ).transact({'from': A}) # Balance proof with correct channel_identifier must work balance_proof_B2 = create_balance_proof( channel_identifier2, B, values_B.transferred, values_B.locked, nonce, values_B.locksroot, ) token_network.functions.closeChannel( channel_identifier2, B, *balance_proof_B2, ).transact({'from': A})
def test_settle_channel_state( web3: Web3, get_accounts: Callable, custom_token: Contract, token_network: Contract, create_channel_and_deposit: Callable, withdraw_channel: Callable, close_and_update_channel: Callable, settle_state_tests: Callable, ) -> None: """settleChannel() with some balance proofs""" (A, B) = get_accounts(2) vals_A = ChannelValues( deposit=40, withdrawn=10, transferred=20020, locked_amounts=LockedAmounts(claimable_locked=3, unclaimable_locked=4), ) vals_B = ChannelValues( deposit=35, withdrawn=5, transferred=20030, locked_amounts=LockedAmounts(claimable_locked=2, unclaimable_locked=3), ) pending_transfers_tree_A = get_pending_transfers_tree_with_generated_lists( web3, unlockable_amount=vals_A.locked_amounts.claimable_locked, expired_amount=vals_A.locked_amounts.unclaimable_locked, ) pending_transfers_tree_B = get_pending_transfers_tree_with_generated_lists( web3, unlockable_amount=vals_B.locked_amounts.claimable_locked, expired_amount=vals_B.locked_amounts.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.hash_of_packed_transfers vals_B.locksroot = pending_transfers_tree_B.hash_of_packed_transfers channel_identifier = create_channel_and_deposit(A, B, vals_A.deposit, vals_B.deposit) withdraw_channel(channel_identifier, A, vals_A.withdrawn, UINT256_MAX, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, UINT256_MAX, A) close_and_update_channel(channel_identifier, A, vals_A, B, vals_B) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) 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 assert custom_token.functions.balanceOf(A).call() == post_balance_A assert custom_token.functions.balanceOf(B).call() == post_balance_B assert custom_token.functions.balanceOf( token_network.address).call() == post_balance_contract
def test_close_nonce_zero( get_accounts, token_network, create_channel, create_balance_proof, ): """ closeChannel with a balance proof with nonce zero should not change the channel state """ (A, B) = get_accounts(2) vals_B = ChannelValues( deposit=20, transferred=5, locksroot=fake_bytes(32, '03'), claimable_locked=3, unclaimable_locked=4, nonce=0, ) # Create channel and deposit channel_identifier = create_channel(A, B)[0] # Create balance proofs balance_proof_B = create_balance_proof( channel_identifier, B, vals_B.transferred, vals_B.locked, vals_B.nonce, vals_B.locksroot, ) ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0 token_network.functions.closeChannel( channel_identifier, B, *balance_proof_B, ).transact({'from': A}) # Even though we somehow provide valid values for the balance proof, they are not taken into # consideration if the nonce is 0. # The Raiden client enforces that the nonce is > 0 if off-chain transfers are made. ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0
def test_close_channel_state( web3, custom_token, token_network, create_channel, channel_deposit, get_accounts, get_block, create_balance_proof, txn_cost, ): """ Observe the effect of a successful closeChannel This test compares the state of the channel and the balances of Ethereum accounts before/after a successful closeChannel call. """ (A, B) = get_accounts(2) settle_timeout = TEST_SETTLE_TIMEOUT_MIN vals_B = ChannelValues( deposit=20, transferred=5, locksroot=fake_bytes(32, '03'), claimable_locked=3, unclaimable_locked=4, nonce=3, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, B, vals_B.deposit, A) # Check the state of the openned channel ( settle_block_number, state, ) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert settle_block_number == settle_timeout assert state == ChannelState.OPENED ( _, _, A_is_the_closer, A_balance_hash, A_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call() assert A_is_the_closer is False assert A_balance_hash == EMPTY_BALANCE_HASH assert A_nonce == 0 ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0 pre_eth_balance_A = web3.eth.getBalance(A) pre_eth_balance_B = web3.eth.getBalance(B) pre_eth_balance_contract = web3.eth.getBalance(token_network.address) 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() # Create a balance proof balance_proof_B = create_balance_proof( channel_identifier, B, vals_B.transferred, vals_B.locked, vals_B.nonce, vals_B.locksroot, ) txn_hash = token_network.functions.closeChannel( channel_identifier, B, *balance_proof_B, ).transact({'from': A}) # Test that no balances have changed. # There are no transfers to be made in closeChannel. assert web3.eth.getBalance(A) == pre_eth_balance_A - txn_cost(txn_hash) assert web3.eth.getBalance(B) == pre_eth_balance_B assert web3.eth.getBalance( token_network.address) == pre_eth_balance_contract assert custom_token.functions.balanceOf(A).call() == pre_balance_A assert custom_token.functions.balanceOf(B).call() == pre_balance_B assert custom_token.functions.balanceOf( token_network.address).call() == pre_balance_contract ( settle_block_number, state, ) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert settle_block_number == settle_timeout + get_block(txn_hash) assert state == ChannelState.CLOSED ( _, _, A_is_the_closer, A_balance_hash, A_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call() assert A_is_the_closer is True assert A_balance_hash == EMPTY_BALANCE_HASH assert A_nonce == 0 ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == balance_proof_B[0] assert B_nonce == vals_B.nonce
def test_update_replay_reopened_channel( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel: Callable, channel_deposit: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """ updateNonClosingBalanceProof() should refuse a balance proof with a stale channel id """ (A, B) = get_accounts(2) nonce_B = 5 values_A = ChannelValues(deposit=10, transferred=0) values_B = ChannelValues(deposit=20, transferred=15) channel_identifier1 = create_channel(A, B)[0] channel_deposit(channel_identifier1, B, values_B.deposit, A) balance_proof_B = create_balance_proof( channel_identifier1, B, values_B.transferred, values_B.locked_amounts.locked, nonce_B, values_B.locksroot, ) balance_proof_update_signature_A = create_balance_proof_countersignature( A, channel_identifier1, MessageTypeId.BALANCE_PROOF_UPDATE, *balance_proof_B ) closing_sig = create_close_signature_for_no_balance_proof(B, channel_identifier1) token_network.functions.closeChannel( channel_identifier1, A, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ).call_and_transact({"from": B}) token_network.functions.updateNonClosingBalanceProof( channel_identifier1, B, A, *balance_proof_B, balance_proof_update_signature_A ).call_and_transact({"from": A}) web3.testing.mine(TEST_SETTLE_TIMEOUT_MIN + 1) token_network.functions.settleChannel( channel_identifier1, A, values_A.transferred, values_A.locked_amounts.locked, values_A.locksroot, B, values_B.transferred, values_B.locked_amounts.locked, values_B.locksroot, ).call_and_transact({"from": A}) # Make sure we cannot update balance proofs after settleChannel is called with pytest.raises(TransactionFailed): token_network.functions.updateNonClosingBalanceProof( channel_identifier1, B, A, *balance_proof_B, balance_proof_update_signature_A ).call({"from": A}) # Reopen the channel and make sure we cannot use the old balance proof channel_identifier2 = create_channel(A, B)[0] channel_deposit(channel_identifier2, B, values_B.deposit, A) closing_sig = create_close_signature_for_no_balance_proof(B, channel_identifier2) token_network.functions.closeChannel( channel_identifier2, A, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ).call_and_transact({"from": B}) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed): token_network.functions.updateNonClosingBalanceProof( channel_identifier2, B, A, *balance_proof_B, balance_proof_update_signature_A ).call({"from": A}) # Correct channel_identifier must work balance_proof_B2 = create_balance_proof( channel_identifier2, B, values_B.transferred, values_B.locked_amounts.locked, nonce_B, values_B.locksroot, ) balance_proof_update_signature_A2 = create_balance_proof_countersignature( A, channel_identifier2, MessageTypeId.BALANCE_PROOF_UPDATE, *balance_proof_B2 ) token_network.functions.updateNonClosingBalanceProof( channel_identifier2, B, A, *balance_proof_B2, balance_proof_update_signature_A2 ).call_and_transact({"from": A})
def test_close_nonce_zero( get_accounts: Callable, token_network: Contract, create_channel: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, event_handler: Callable, ) -> None: """ closeChannel with a balance proof with nonce zero should not change the channel state """ (A, B) = get_accounts(2) vals_B = ChannelValues( deposit=20, transferred=5, locksroot=fake_bytes(32, "03"), locked_amounts=LockedAmounts(claimable_locked=3, unclaimable_locked=4), nonce=0, ) # Create channel and deposit channel_identifier = create_channel(A, B)[0] # Create balance proofs balance_proof_B = create_balance_proof( channel_identifier, B, vals_B.transferred, vals_B.locked_amounts.locked, vals_B.nonce, vals_B.locksroot, ) close_sig_A = create_balance_proof_countersignature( A, channel_identifier, MessageTypeId.BALANCE_PROOF, *balance_proof_B) ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0 ev_handler = event_handler(token_network) close_tx = token_network.functions.closeChannel( channel_identifier, B, A, *balance_proof_B, close_sig_A).call_and_transact({"from": A}) ev_handler.add( close_tx, ChannelEvent.CLOSED, check_channel_closed(channel_identifier, A, 0, balance_proof_B[0]), ) ev_handler.check() # Even though we somehow provide valid values for the balance proof, they are not taken into # consideration if the nonce is 0. # The Raiden client enforces that the nonce is > 0 if off-chain transfers are made. ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0
def test_deposit_wrong_state_fail( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel: Callable, assign_tokens: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """ setTotalDeposit() fails on Closed or Settled channels. """ (A, B) = get_accounts(2) vals_A = ChannelValues(deposit=2, transferred=0) vals_B = ChannelValues(deposit=2, transferred=0) channel_identifier = create_channel(A, B, TEST_SETTLE_TIMEOUT_MIN)[0] assign_tokens(A, vals_A.deposit) assign_tokens(B, vals_B.deposit) call_and_transact( token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B), {"from": A}, ) call_and_transact( token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A), {"from": B}, ) assert ( vals_A.deposit == token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call()[0]) assert ( vals_B.deposit == token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call()[0]) closing_sig = create_close_signature_for_no_balance_proof( A, channel_identifier) call_and_transact( token_network.functions.closeChannel( channel_identifier=channel_identifier, non_closing_participant=B, closing_participant=A, balance_hash=EMPTY_BALANCE_HASH, nonce=0, additional_hash=EMPTY_ADDITIONAL_HASH, non_closing_signature=EMPTY_SIGNATURE, closing_signature=closing_sig, ), {"from": A}, ) assign_tokens(A, 10) assign_tokens(B, 10) vals_A.deposit += 5 vals_B.deposit += 5 with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B).call({"from": A}) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A).call({"from": B}) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, A, vals_A.deposit, B).call({"from": A}) with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, B, vals_B.deposit, A).call({"from": B})
def test_settle_wrong_balance_hash( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel_and_deposit: Callable, close_and_update_channel: Callable, reveal_secrets: Callable, ) -> None: """Calling settleChannel() with various wrong arguments and see failures""" (A, B) = get_accounts(2) vals_A = ChannelValues( deposit=35, withdrawn=0, transferred=5, locked_amounts=LockedAmounts(claimable_locked=10, unclaimable_locked=2), ) vals_B = ChannelValues( deposit=40, withdrawn=0, transferred=15, locked_amounts=LockedAmounts(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_with_generated_lists( web3, unlockable_amount=vals_A.locked_amounts.claimable_locked, expired_amount=vals_A.locked_amounts.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.hash_of_packed_transfers # 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_with_generated_lists( web3, unlockable_amount=vals_B.locked_amounts.claimable_locked, expired_amount=vals_B.locked_amounts.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.hash_of_packed_transfers # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) close_and_update_channel(channel_identifier, A, vals_A, B, vals_B) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) 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 = UINT256_MAX 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_amounts.claimable_locked += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.locked_amounts.claimable_locked = 0 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A_fail, B, vals_B) vals_A_fail.locked_amounts.unclaimable_locked = 0 vals_A_fail.locked_amounts.claimable_locked = UINT256_MAX 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 = LOCKSROOT_OF_NO_LOCKS 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 = UINT256_MAX 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_amounts.claimable_locked += 1 with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B_fail) vals_B_fail.locked_amounts.claimable_locked = 0 with pytest.raises(AssertionError): call_settle(token_network, channel_identifier, B, vals_B_fail, A, vals_A) vals_B_fail.locked_amounts.unclaimable_locked = 0 vals_B_fail.locked_amounts.claimable_locked = UINT256_MAX 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 = LOCKSROOT_OF_NO_LOCKS 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_unlock_registered_expired_lock_refunds( web3, custom_token, token_network, secret_registry_contract, create_channel, channel_deposit, get_accounts, close_and_update_channel, ): """ unlock() should refund tokens locked with secrets revealed after the expiration """ (A, B) = get_accounts(2) max_lock_expiration = 3 settle_timeout = 8 values_A = ChannelValues(deposit=20, transferred=5) 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_amounts = LockedAmounts( claimable_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).call_and_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).call_and_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_amounts.locked assert balance_contract == pre_balance_contract - values_B.locked_amounts.locked
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, ): """ settleChannel() with some balance proofs """ (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 test_channel_unlock_before_settlement_fails( web3, custom_token, token_network, create_channel, channel_deposit, get_accounts, close_and_update_channel, reveal_secrets, ): """ unlock() should not work before settlement """ (A, B) = get_accounts(2) settle_timeout = 8 values_A = ChannelValues(deposit=20, transferred=5) 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_amounts = LockedAmounts( claimable_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).call() 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).call() 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).call() # 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).call() # 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).call_and_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_amounts.locked
def test_settle_single_direct_transfer_for_counterparty( web3, get_accounts, custom_token, token_network, create_channel, channel_deposit, create_balance_proof, create_balance_proof_update_signature, ): """ Test settle of a channel with one direct transfer to the participant that did not call close. """ (A, B) = get_accounts(2) (vals_A, vals_B) = ( ChannelValues(deposit=10, withdrawn=0, transferred=5, locked=0), ChannelValues(deposit=1, withdrawn=0, transferred=0, locked=0), ) settle_timeout = TEST_SETTLE_TIMEOUT_MIN 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) token_network.functions.closeChannel( channel_identifier, B, EMPTY_LOCKSROOT, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, ).transact({'from': A}) balance_proof_A = create_balance_proof( channel_identifier, A, vals_A.transferred, vals_A.locked, 1, EMPTY_LOCKSROOT, ) balance_proof_update_signature_B = create_balance_proof_update_signature( B, channel_identifier, *balance_proof_A, ) token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A, balance_proof_update_signature_B, ).transact({'from': 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() web3.testing.mine(settle_timeout) token_network.functions.settleChannel( channel_identifier, B, 0, 0, EMPTY_LOCKSROOT, A, vals_A.transferred, 0, EMPTY_LOCKSROOT, ).transact({'from': B}) # Calculate how much A and B should receive expected_settlement = get_settlement_amounts(vals_B, vals_A) # Calculate how much A and B receive according to onchain computation onchain_settlement = get_onchain_settlement_amounts(vals_B, vals_A) assert (expected_settlement.participant1_balance == onchain_settlement.participant1_balance) assert (expected_settlement.participant2_balance == onchain_settlement.participant2_balance) assert custom_token.functions.balanceOf(A).call() == pre_balance_A + 5 assert custom_token.functions.balanceOf(B).call() == pre_balance_B + 6 assert custom_token.functions.balanceOf( token_network.address, ).call() == pre_balance_contract - 11
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 scenario where both parties get some of the pending transfers """ (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_amounts = LockedAmounts( claimable_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_amounts = LockedAmounts( claimable_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).call_and_transact() # B unlock's token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers).call_and_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_amounts.locked - values_A.locked_amounts.locked) assert balance_A == pre_balance_A + unlockable_B + expired_A assert balance_B == pre_balance_B + unlockable_A + expired_B
def test_settle_single_direct_transfer_for_counterparty( web3: Web3, get_accounts: Callable, custom_token: Contract, token_network: Contract, create_channel: Callable, channel_deposit: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """Test settle of a channel with one direct transfer to the participant that did not call close. """ (A, B) = get_accounts(2) (vals_A, vals_B) = ( ChannelValues(deposit=10, withdrawn=0, transferred=5), ChannelValues(deposit=1, withdrawn=0, transferred=0), ) settle_timeout = TEST_SETTLE_TIMEOUT_MIN 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) closing_sig = create_close_signature_for_no_balance_proof( A, channel_identifier) call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ), {"from": A}, ) balance_proof_A = create_balance_proof( channel_identifier, A, vals_A.transferred, vals_A.locked_amounts.locked, 1, LOCKSROOT_OF_NO_LOCKS, ) balance_proof_update_signature_B = create_balance_proof_countersignature( participant=B, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, **balance_proof_A._asdict(), ) call_and_transact( token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A._asdict().values(), balance_proof_update_signature_B, ), {"from": 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() mine_blocks(web3, settle_timeout + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier=channel_identifier, participant1=B, participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=LOCKSROOT_OF_NO_LOCKS, participant2=A, participant2_transferred_amount=vals_A.transferred, participant2_locked_amount=0, participant2_locksroot=LOCKSROOT_OF_NO_LOCKS, ), {"from": B}, ) # Calculate how much A and B should receive expected_settlement = get_settlement_amounts(vals_B, vals_A) # Calculate how much A and B receive according to onchain computation onchain_settlement = get_onchain_settlement_amounts(vals_B, vals_A) assert expected_settlement.participant1_balance == onchain_settlement.participant1_balance assert expected_settlement.participant2_balance == onchain_settlement.participant2_balance assert custom_token.functions.balanceOf(A).call() == pre_balance_A + 5 assert custom_token.functions.balanceOf(B).call() == pre_balance_B + 6 assert (custom_token.functions.balanceOf( token_network.address).call() == pre_balance_contract - 11)
def test_close_channel_state( web3: Web3, custom_token: Contract, token_network: Contract, create_channel: Callable, channel_deposit: Callable, get_accounts: Callable, get_block: Callable, create_balance_proof: Callable, txn_cost: Callable, create_balance_proof_countersignature: Callable, ) -> None: """Observe the effect of a successful closeChannel This test compares the state of the channel and the balances of Ethereum accounts before/after a successful closeChannel call. """ (A, B) = get_accounts(2) settle_timeout = TEST_SETTLE_TIMEOUT_MIN vals_B = ChannelValues( deposit=20, transferred=5, locksroot=fake_bytes(32, "03"), locked_amounts=LockedAmounts(claimable_locked=3, unclaimable_locked=4), nonce=3, ) # Create channel and deposit channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, B, vals_B.deposit, A) # Check the state of the openned channel (settle_block_number, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert settle_block_number == settle_timeout assert state == ChannelState.OPENED ( _, _, A_is_the_closer, A_balance_hash, A_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call() assert A_is_the_closer is False assert A_balance_hash == EMPTY_BALANCE_HASH assert A_nonce == 0 ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == EMPTY_BALANCE_HASH assert B_nonce == 0 pre_eth_balance_A = web3.eth.get_balance(A) pre_eth_balance_B = web3.eth.get_balance(B) pre_eth_balance_contract = web3.eth.get_balance(token_network.address) 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() # Create a balance proof balance_proof_B = create_balance_proof( channel_identifier, B, vals_B.transferred, vals_B.locked_amounts.locked, vals_B.nonce, vals_B.locksroot, ) closing_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B._asdict(), ) txn_hash = call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, *balance_proof_B._asdict().values(), closing_sig_A), {"from": A}, ) # Test that no balances have changed. # There are no transfers to be made in closeChannel. assert web3.eth.get_balance(A) == pre_eth_balance_A - txn_cost(txn_hash) assert web3.eth.get_balance(B) == pre_eth_balance_B assert web3.eth.get_balance( token_network.address) == pre_eth_balance_contract assert custom_token.functions.balanceOf(A).call() == pre_balance_A assert custom_token.functions.balanceOf(B).call() == pre_balance_B assert custom_token.functions.balanceOf( token_network.address).call() == pre_balance_contract (settle_block_number, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert settle_block_number == settle_timeout + get_block(txn_hash) assert state == ChannelState.CLOSED ( _, _, A_is_the_closer, A_balance_hash, A_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call() assert A_is_the_closer is True assert A_balance_hash == EMPTY_BALANCE_HASH assert A_nonce == 0 ( _, _, B_is_the_closer, B_balance_hash, B_nonce, _, _, ) = token_network.functions.getChannelParticipantInfo( channel_identifier, B, A).call() assert B_is_the_closer is False assert B_balance_hash == balance_proof_B.balance_hash assert B_nonce == vals_B.nonce
def test_settlement_with_unauthorized_token_transfer( web3: Web3, get_accounts: Callable, custom_token: Contract, token_network: Contract, assign_tokens: Callable, create_channel_and_deposit: Callable, withdraw_channel: Callable, close_and_update_channel: Callable, ) -> None: """A participant transfers some tokens to the contract and so loses them""" externally_transferred_amount = 5 (A, B) = get_accounts(2) (vals_A, vals_B) = ( ChannelValues(deposit=35, withdrawn=10, transferred=0), ChannelValues(deposit=40, withdrawn=10, transferred=0), ) vals_A.locksroot = fake_bytes(32, "02") 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, UINT256_MAX, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, UINT256_MAX, A) close_and_update_channel(channel_identifier, A, vals_A, B, vals_B) # Assign additional tokens to A assign_tokens(A, externally_transferred_amount) assert custom_token.functions.balanceOf( A).call() >= externally_transferred_amount # Fetch onchain balances after settlement 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 does a transfer to the token_network without appropriate function call - tokens are lost call_and_transact( custom_token.functions.transfer(token_network.address, externally_transferred_amount), {"from": A}, ) assert custom_token.functions.balanceOf( token_network.address).call() == (pre_balance_contract + externally_transferred_amount) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) # Compute expected settlement amounts settlement = get_settlement_amounts(vals_A, vals_B) # Channel is settled call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) # Fetch onchain balances after settlement post_balance_A = custom_token.functions.balanceOf(A).call() post_balance_B = custom_token.functions.balanceOf(B).call() post_balance_contract = custom_token.functions.balanceOf( token_network.address).call() # A has lost the externally_transferred_amount assert (pre_balance_A + settlement.participant1_balance - externally_transferred_amount) == post_balance_A # B's settlement works correctly assert pre_balance_B + settlement.participant2_balance == post_balance_B # The externally_transferred_amount stays in the contract assert (pre_balance_contract - settlement.participant1_balance - settlement.participant2_balance + externally_transferred_amount) == post_balance_contract
def test_close_replay_reopened_channel( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel: Callable, channel_deposit: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, ) -> None: """The same balance proof cannot close another channel between the same participants""" (A, B) = get_accounts(2) nonce = 3 values_A = ChannelValues(deposit=10, transferred=0) values_B = ChannelValues(deposit=20, transferred=15) channel_identifier1 = create_channel(A, B)[0] channel_deposit(channel_identifier1, B, values_B.deposit, A) balance_proof_B = create_balance_proof( channel_identifier1, B, values_B.transferred, values_B.locked_amounts.locked, nonce, values_B.locksroot, ) closing_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier1, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B._asdict(), ) call_and_transact( token_network.functions.closeChannel( channel_identifier1, B, A, *balance_proof_B._asdict().values(), closing_sig_A, ), {"from": A}, ) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier=channel_identifier1, participant1=A, participant1_transferred_amount=values_A.transferred, participant1_locked_amount=values_A.locked_amounts.locked, participant1_locksroot=values_A.locksroot, participant2=B, participant2_transferred_amount=values_B.transferred, participant2_locked_amount=values_B.locked_amounts.locked, participant2_locksroot=values_B.locksroot, ), {"from": A}, ) # Reopen the channel and make sure we cannot use the old balance proof channel_identifier2 = create_channel(A, B)[0] channel_deposit(channel_identifier2, B, values_B.deposit, A) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed, match="TN/close: invalid closing sig"): token_network.functions.closeChannel( channel_identifier2, B, A, *balance_proof_B._asdict().values(), closing_sig_A, ).call({"from": A}) # Balance proof with correct channel_identifier must work balance_proof_B2 = create_balance_proof( channel_identifier2, B, values_B.transferred, values_B.locked_amounts.locked, nonce, values_B.locksroot, ) closing_sig_A2 = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier2, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B2._asdict(), ) call_and_transact( token_network.functions.closeChannel( channel_identifier2, B, A, *balance_proof_B2._asdict().values(), closing_sig_A2, ), {"from": A}, )
def test_settle_wrong_balance_hash( web3, get_accounts, token_network, create_channel_and_deposit, close_and_update_channel, get_block, reveal_secrets, ): """ Calling settleChannel() with various wrong arguments and see failures """ (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_deprecation_switch_settle( web3: Web3, get_accounts: Callable, token_network: Contract, custom_token: Contract, reveal_secrets: Callable, create_channel: Callable, channel_deposit: Callable, close_and_update_channel: Callable, ) -> None: """ Channel close and settlement still work after the depracation switch is turned on """ 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, locked_amounts=LockedAmounts(claimable_locked=2, unclaimable_locked=4), ), ChannelValues( deposit=deposit, withdrawn=0, transferred=10, locked_amounts=LockedAmounts(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_with_generated_lists( web3, unlockable_amount=vals_A.locked_amounts.claimable_locked, expired_amount=vals_A.locked_amounts.unclaimable_locked, ) vals_A.locksroot = pending_transfers_tree_A.hash_of_packed_transfers # 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_with_generated_lists( web3, unlockable_amount=vals_B.locked_amounts.claimable_locked, expired_amount=vals_B.locked_amounts.unclaimable_locked, ) vals_B.locksroot = pending_transfers_tree_B.hash_of_packed_transfers # Reveal B's secrets reveal_secrets(B, pending_transfers_tree_B.unlockable) # Set the deprecation switch to true token_network.functions.deprecate().call_and_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 + 1) 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).call_and_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).call_and_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