def test_monitor_on_wrong_token_network_registry( token_network_in_another_token_network_registry: Contract, monitoring_service_external: Contract, monitor_data: Dict, ms_address: HexAddress, web3: Web3, ) -> None: A, B = monitor_data["participants"] # wait until MS is allowed to monitor mine_blocks(web3, monitor_data["first_allowed"] - web3.eth.block_number) # monitor() call fails because the TokenNetwork is not registered on the # supposed TokenNetworkRegistry with pytest.raises(TransactionFailed, match="Unknown TokenNetwork"): call_and_transact( monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network_in_another_token_network_registry.address, monitor_data["reward_proof_signature"], ), {"from": ms_address}, )
def print_gas_user_deposit( user_deposit_contract: Contract, custom_token: Contract, get_accounts: Callable, web3: Web3, print_gas: Callable, ) -> None: """Abusing pytest to print gas cost of UserDeposit functions The `transfer` function is not included because it's only called by trusted contracts as part of another function. """ (A,) = get_accounts(1) call_and_transact(custom_token.functions.mint(20), {"from": A}) call_and_transact( custom_token.functions.approve(user_deposit_contract.address, 20), {"from": A} ) # deposit txn_hash = call_and_transact(user_deposit_contract.functions.deposit(A, 10), {"from": A}) print_gas(txn_hash, CONTRACT_USER_DEPOSIT + ".deposit") txn_hash = call_and_transact(user_deposit_contract.functions.deposit(A, 20), {"from": A}) print_gas(txn_hash, CONTRACT_USER_DEPOSIT + ".deposit (increase balance)") # plan withdraw txn_hash = call_and_transact(user_deposit_contract.functions.planWithdraw(10), {"from": A}) print_gas(txn_hash, CONTRACT_USER_DEPOSIT + ".planWithdraw") # withdraw withdraw_delay = user_deposit_contract.functions.withdraw_delay().call() mine_blocks(web3, withdraw_delay) txn_hash = call_and_transact(user_deposit_contract.functions.withdraw(10), {"from": A}) print_gas(txn_hash, CONTRACT_USER_DEPOSIT + ".withdraw")
def deploy_contract_txhash( web3: Web3, contracts_manager: ContractManager, deployer_address: HexAddress, contract_name: str, libs: Dict = None, **kwargs: Any, ) -> Tuple[HexAddress, Contract]: json_contract = contracts_manager.get_contract(contract_name) abi = json_contract["abi"] bytecode = json_contract["bin"] bytecode_runtime = None if isinstance(libs, dict) and len(libs.keys()) > 0: bytecode = link_code(bytecode, libs) bytecode_runtime = link_code(json_contract["bin-runtime"], libs) if bytecode_runtime is not None: contract = web3.eth.contract(abi=abi, bytecode=bytecode, bytecode_runtime=bytecode_runtime) else: contract = web3.eth.contract(abi=abi, bytecode=bytecode) mine_blocks(web3, 3) # Failure does not fire an exception. Check the receipt for status. txhash = contract.constructor(**kwargs).transact({"from": deployer_address}) mine_blocks(web3, 1) receipt = web3.eth.getTransactionReceipt(txhash) if receipt["status"] != 1: raise TransactionFailed("deployment failed") return txhash, contract(receipt["contractAddress"])
def test_settle2_no_bp_success( web3: Web3, custom_token: Contract, token_network: Contract, create_channel_and_deposit: Callable, get_accounts: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """The simplest settlement, tested against the V2 ABI settle""" (A, B) = get_accounts(2) deposit_A = 10 deposit_B = 6 settle_timeout = TEST_SETTLE_TIMEOUT_MIN channel_identifier = create_channel_and_deposit(A, B, deposit_A, deposit_B) closing_sig = create_close_signature_for_no_balance_proof( A, channel_identifier) # Close channel with no balance proof call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ), {"from": A}, ) # Do not call updateNonClosingBalanceProof # Settlement window must be over before settling the channel mine_blocks(web3, settle_timeout + 1) # Settling the channel should work with no balance proofs call_and_transact( token_network.functions.settleChannel2( channel_identifier=channel_identifier, participant1_settlement=dict( participant=A, transferred_amount=0, locked_amount=0, locksroot=LOCKSROOT_OF_NO_LOCKS, ), participant2_settlement=dict( participant=B, transferred_amount=0, locked_amount=0, locksroot=LOCKSROOT_OF_NO_LOCKS, ), ), {"from": A}, ) assert custom_token.functions.balanceOf(A).call() == deposit_A assert custom_token.functions.balanceOf(B).call() == deposit_B
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_withdraw_to_beneficiary( user_deposit_contract: Contract, deposit_to_udc: Callable, get_accounts: Callable, web3: Web3, event_handler: Callable, custom_token: Contract, ) -> None: """Test the interaction between planWithdraw, withdrawToBeneficiary and effectiveBalance""" ev_handler = event_handler(user_deposit_contract) (A, B) = get_accounts(2) deposit_to_udc(A, 30) assert user_deposit_contract.functions.balances(A).call() == 30 assert user_deposit_contract.functions.effectiveBalance(A).call() == 30 # plan withdraw of 20 tokens tx_hash = call_and_transact( user_deposit_contract.functions.planWithdraw(20), {"from": A}) ev_handler.assert_event( tx_hash, UserDepositEvent.WITHDRAW_PLANNED, dict(withdrawer=A, plannedBalance=10), ) assert user_deposit_contract.functions.balances(A).call() == 30 assert user_deposit_contract.functions.effectiveBalance(A).call() == 10 # beneficiary can not be zero address with pytest.raises(TransactionFailed, match="beneficiary is zero"): user_deposit_contract.functions.withdrawToBeneficiary( 18, ZERO_ADDRESS).call({"from": A}) # withdraw won't work before withdraw_delay elapsed withdraw_delay = user_deposit_contract.functions.withdraw_delay().call() mine_blocks(web3, withdraw_delay - 1) with pytest.raises(TransactionFailed, match="withdrawing too early"): user_deposit_contract.functions.withdrawToBeneficiary(18, B).call( {"from": A}) # can't withdraw more then planned mine_blocks(web3, 1) # now withdraw_delay is over with pytest.raises(TransactionFailed, match="withdrawing more than planned"): user_deposit_contract.functions.withdrawToBeneficiary(21, B).call( {"from": A}) # actually withdraw 18 tokens assert custom_token.functions.balanceOf(B).call() == 0 call_and_transact( user_deposit_contract.functions.withdrawToBeneficiary(18, B), {"from": A}) assert user_deposit_contract.functions.balances(A).call() == 12 assert user_deposit_contract.functions.effectiveBalance(A).call() == 12 assert custom_token.functions.balanceOf(B).call() == 18
def fn( web3: Web3, deployer_address: HexAddress, abi: List, bytecode: str, **kwargs: Dict ) -> Contract: contract = web3.eth.contract(abi=abi, bytecode=bytecode) txhash = deploy_contract_txhash(web3, deployer_address, abi, bytecode, **kwargs) contract_address = web3.eth.getTransactionReceipt(txhash)["contractAddress"] mine_blocks(web3, 1) if web3.eth.getTransactionReceipt(txhash)["status"] != 1: raise TransactionFailed("deployment failed") return contract(contract_address)
def test_update_not_allowed_after_settlement_period( token_network: Contract, create_channel: Callable, channel_deposit: Callable, get_accounts: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, web3: Web3, ) -> None: """ updateNonClosingBalanceProof cannot be called after the settlement period. """ (A, B) = get_accounts(2) settle_timeout = TEST_SETTLE_TIMEOUT_MIN deposit_A = 20 channel_identifier = create_channel(A, B, settle_timeout)[0] channel_deposit(channel_identifier, A, deposit_A, B) balance_proof_A = create_balance_proof(channel_identifier, A, 10, 0, 5, fake_bytes(32, "02")) balance_proof_B = create_balance_proof(channel_identifier, B, 5, 0, 3, fake_bytes(32, "02")) closing_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B._asdict(), ) 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.closeChannel( channel_identifier, B, A, *balance_proof_B._asdict().values(), closing_sig_A), {"from": A}, ) mine_blocks(web3, settle_timeout + 1) with pytest.raises(TransactionFailed): token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A._asdict().values(), balance_proof_update_signature_B, ).call({"from": A})
def test_withdraw( user_deposit_contract: Contract, deposit_to_udc: Callable, get_accounts: Callable, web3: Web3, event_handler: Callable, ) -> None: """Test the interaction between planWithdraw, widthdraw and effectiveBalance""" ev_handler = event_handler(user_deposit_contract) (A, ) = get_accounts(1) deposit_to_udc(A, 30) assert user_deposit_contract.functions.balances(A).call() == 30 assert user_deposit_contract.functions.effectiveBalance(A).call() == 30 # plan withdraw of 20 tokens tx_hash = call_and_transact( user_deposit_contract.functions.planWithdraw(20), {"from": A}) ev_handler.assert_event( tx_hash, UserDepositEvent.WITHDRAW_PLANNED, dict(withdrawer=A, plannedBalance=10), ) assert user_deposit_contract.functions.balances(A).call() == 30 assert user_deposit_contract.functions.effectiveBalance(A).call() == 10 # withdraw won't work before withdraw_delay elapsed withdraw_delay = user_deposit_contract.functions.withdraw_delay().call() mine_blocks(web3, withdraw_delay - 1) with pytest.raises(TransactionFailed, match="withdrawing too early"): user_deposit_contract.functions.withdraw(18).call({"from": A}) # can't withdraw more then planned mine_blocks(web3, 1) # now withdraw_delay is over with pytest.raises(TransactionFailed, match="withdrawing more than planned"): user_deposit_contract.functions.withdraw(21).call({"from": A}) # actually withdraw 18 tokens call_and_transact(user_deposit_contract.functions.withdraw(18), {"from": A}) assert user_deposit_contract.functions.balances(A).call() == 12 assert user_deposit_contract.functions.effectiveBalance(A).call() == 12
def test_deposit_notapproved( token_network: Contract, custom_token: Contract, create_channel: Callable, get_accounts: Callable, web3: Web3, ) -> None: """ Calling setTotalDeposit() fails without approving transfers on the token contract """ (A, B) = get_accounts(2) channel_identifier = create_channel(A, B)[0] deposit_A = 1 call_and_transact(custom_token.functions.mint(deposit_A), {"from": A}) mine_blocks(web3, 1) balance = custom_token.functions.balanceOf(A).call() assert balance >= deposit_A, f"minted {deposit_A} but the balance is still {balance}" with pytest.raises(TransactionFailed): token_network.functions.setTotalDeposit(channel_identifier, A, deposit_A, B).call({"from": A}) assert (0 == token_network.functions.getChannelParticipantInfo( channel_identifier, A, B).call()[0])
def test_monitor_by_unregistered_service( token_network: Contract, monitoring_service_external: Contract, monitor_data: Dict, ms_address: HexAddress, web3: Web3, ) -> None: A, B = monitor_data["participants"] # wait until MS is allowed to monitor mine_blocks(web3, monitor_data["first_allowed"] - web3.eth.block_number) # only registered service providers may call `monitor` with pytest.raises(TransactionFailed, match="service not registered"): monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network.address, monitor_data["reward_proof_signature"], ).call({"from": B}) # See a success to make sure the above failure is not spurious call_and_transact( monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network.address, monitor_data["reward_proof_signature"], ), {"from": ms_address}, )
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_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 print_gas_channel_cycle( web3: Web3, token_network: Contract, create_channel: Callable, channel_deposit: Callable, withdraw_channel: Callable, secret_registry_contract: Contract, get_accounts: Callable, print_gas: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, ) -> None: """Abusing pytest to print gas costs of TokenNetwork's operations""" (A, B, C, D) = get_accounts(4) settle_timeout = 11 (channel_identifier, txn_hash) = create_channel(A, B, settle_timeout) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".openChannel") (_, txn_hash) = create_channel(C, D, settle_timeout) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".openChannel") txn_hash = channel_deposit(channel_identifier, A, 20, B) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".setTotalDeposit") txn_hash = channel_deposit(channel_identifier, B, 10, A) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".setTotalDeposit") txn_hash = withdraw_channel(channel_identifier, A, 5, UINT256_MAX, B) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".setTotalWithdraw") pending_transfers_tree1 = get_pending_transfers_tree( web3, [1, 1, 2, 3], [2, 1]) locksroot1 = pending_transfers_tree1.hash_of_packed_transfers locked_amount1 = get_locked_amount(pending_transfers_tree1.transfers) pending_transfers_tree2 = get_pending_transfers_tree(web3, [3], [], 7) locksroot2 = pending_transfers_tree2.hash_of_packed_transfers 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_update_signature_B = create_balance_proof_countersignature( participant=B, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, **balance_proof_A._asdict(), ) balance_proof_B = create_balance_proof(channel_identifier, B, 5, locked_amount2, 3, locksroot2) closing_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B._asdict(), ) for lock in pending_transfers_tree1.unlockable: txn_hash = call_and_transact( secret_registry_contract.functions.registerSecret(lock[3]), {"from": A}) print_gas(txn_hash, CONTRACT_SECRET_REGISTRY + ".registerSecret") for lock in pending_transfers_tree2.unlockable: txn_hash = call_and_transact( secret_registry_contract.functions.registerSecret(lock[3]), {"from": A}) print_gas(txn_hash, CONTRACT_SECRET_REGISTRY + ".registerSecret") txn_hash = call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, *balance_proof_B._asdict().values(), closing_sig_A), {"from": A}, ) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".closeChannel") txn_hash = call_and_transact( token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A._asdict().values(), balance_proof_update_signature_B, ), {"from": B}, ) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".updateNonClosingBalanceProof") mine_blocks(web3, settle_timeout) txn_hash = call_and_transact( token_network.functions.settleChannel( channel_identifier, B, 5, locked_amount2, locksroot2, A, 10, locked_amount1, locksroot1, )) print_gas(txn_hash, CONTRACT_TOKEN_NETWORK + ".settleChannel") txn_hash = call_and_transact( token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree2.packed_transfers)) print_gas( txn_hash, "{0}.unlock {1} locks".format(CONTRACT_TOKEN_NETWORK, len(pending_transfers_tree2.transfers)), ) txn_hash = call_and_transact( token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree1.packed_transfers)) print_gas( txn_hash, "{0}.unlock {1} locks".format(CONTRACT_TOKEN_NETWORK, len(pending_transfers_tree1.transfers)), )
def print_gas_monitoring_service( token_network: Contract, monitoring_service_external: Contract, get_accounts: Callable, create_channel: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, service_registry: Contract, custom_token: Contract, deposit_to_udc: Callable, print_gas: Callable, get_private_key: Callable, create_service_account: Callable, web3: Web3, ) -> None: """Abusing pytest to print gas cost of MonitoringService functions""" # setup: two parties + MS (A, MS) = get_accounts(2) B = create_service_account() reward_amount = TokenAmount(10) deposit_to_udc(B, reward_amount) # register MS in the ServiceRegistry contract call_and_transact(custom_token.functions.mint(SERVICE_DEPOSIT * 2), {"from": MS}) call_and_transact( custom_token.functions.approve(service_registry.address, SERVICE_DEPOSIT), {"from": MS}, ) call_and_transact(service_registry.functions.deposit(SERVICE_DEPOSIT), {"from": MS}) # open a channel (c1, c2) channel_identifier = create_channel(A, B)[0] # create balance and reward proofs balance_proof_A = create_balance_proof(channel_identifier, B, transferred_amount=10, nonce=1) closing_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_A._asdict(), ) balance_proof_B = create_balance_proof(channel_identifier, A, transferred_amount=20, nonce=2) non_closing_signature_B = create_balance_proof_countersignature( participant=B, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, **balance_proof_B._asdict(), ) reward_proof_signature = sign_reward_proof( privatekey=get_private_key(B), monitoring_service_contract_address=monitoring_service_external. address, chain_id=token_network.functions.chain_id().call(), token_network_address=token_network.address, non_closing_participant=B, reward_amount=reward_amount, non_closing_signature=non_closing_signature_B, ) # c1 closes channel call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, *balance_proof_A._asdict().values(), closing_sig_A), {"from": A}, ) mine_blocks(web3, 4) # MS calls `MSC::monitor()` using c1's BP and reward proof txn_hash = call_and_transact( monitoring_service_external.functions.monitor( A, B, balance_proof_B.balance_hash, balance_proof_B.nonce, balance_proof_B.additional_hash, balance_proof_B.original_signature, non_closing_signature_B, # non-closing signature reward_amount, token_network.address, # token network address reward_proof_signature, ), {"from": MS}, ) print_gas(txn_hash, CONTRACT_MONITORING_SERVICE + ".monitor") mine_blocks(web3, 1) # MS claims the reward txn_hash = call_and_transact( monitoring_service_external.functions.claimReward( channel_identifier, token_network.address, A, B), {"from": MS}, ) print_gas(txn_hash, CONTRACT_MONITORING_SERVICE + ".claimReward")
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""" controller = token_network.functions.controller().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 call_and_transact(token_network.functions.deprecate(), {"from": controller}) 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) mine_blocks(web3, 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 call_and_transact( token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers)) # Unlock A's pending transfers that were sent to B call_and_transact( token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers)) 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_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( participant=A, channel_identifier=channel_identifier1, msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, **balance_proof_B._asdict(), ) closing_sig = create_close_signature_for_no_balance_proof( B, channel_identifier1) call_and_transact( token_network.functions.closeChannel( channel_identifier1, A, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ), {"from": B}, ) call_and_transact( token_network.functions.updateNonClosingBalanceProof( channel_identifier1, B, A, *balance_proof_B._asdict().values(), balance_proof_update_signature_A, ), {"from": A}, ) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( 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, ), {"from": A}, ) # Make sure we cannot update balance proofs after settleChannel is called with pytest.raises(TransactionFailed, match="TN/update: channel id mismatch"): token_network.functions.updateNonClosingBalanceProof( channel_identifier1, B, A, *balance_proof_B._asdict().values(), 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) call_and_transact( token_network.functions.closeChannel( channel_identifier2, A, B, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ), {"from": B}, ) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed, match="TN/update: invalid non-closing sig"): token_network.functions.updateNonClosingBalanceProof( channel_identifier2, B, A, *balance_proof_B._asdict().values(), 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( participant=A, channel_identifier=channel_identifier2, msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, **balance_proof_B2._asdict(), ) call_and_transact( token_network.functions.updateNonClosingBalanceProof( channel_identifier2, B, A, *balance_proof_B2._asdict().values(), balance_proof_update_signature_A2, ), {"from": A}, )
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 f( participants: Tuple, channel_values: Tuple, expected_final_balance_A0: int, expected_final_balance_B0: int, ) -> None: (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, UINT256_MAX, B) withdraw_channel(channel_identifier, B, vals_B.withdrawn, UINT256_MAX, 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_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) 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.locked_amounts.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).call() else: call_and_transact( token_network.functions.unlock( channel_identifier, A, B, pending_transfers_tree_B.packed_transfers)) # 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] == NONEXISTENT_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).call() else: call_and_transact( token_network.functions.unlock( channel_identifier, B, A, pending_transfers_tree_A.packed_transfers)) # 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] == NONEXISTENT_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 == "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 == "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 == "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 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_settled_channel_fail( web3: Web3, token_network: Contract, create_channel: Callable, channel_deposit: Callable, get_accounts: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """Test getChannelInfo and closeChannel on an already settled channel""" (A, B) = get_accounts(2) channel_identifier = create_channel(A, B, TEST_SETTLE_TIMEOUT_MIN)[0] channel_deposit(channel_identifier, A, 5, B) (_, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.OPENED 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}, ) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier=channel_identifier, participant1=A, participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=NONEXISTENT_LOCKSROOT, participant2=B, participant2_transferred_amount=0, participant2_locked_amount=0, participant2_locksroot=NONEXISTENT_LOCKSROOT, ), {"from": A}, ) (settle_block_number, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.REMOVED assert settle_block_number == 0 with pytest.raises(TransactionFailed, match=" TN: channel not open"): 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, ).call({"from": A})
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_timeout_inrange( token_network: Contract, get_accounts: Callable, web3: Web3, create_close_signature_for_no_balance_proof: Callable, ) -> None: """The TokenNetwork constructor must enforce that settle timeout is in the valid range. Also asserts that the constants.py and the netting channel contract values are synched. """ (A, B) = get_accounts(2) small_settle_timeout = TEST_SETTLE_TIMEOUT_MIN - 1 large_settle_timeout = TEST_SETTLE_TIMEOUT_MAX + 1 with pytest.raises(TransactionFailed): token_network.functions.openChannel(A, B, small_settle_timeout).call() with pytest.raises(TransactionFailed): token_network.functions.openChannel(A, B, large_settle_timeout).call() call_and_transact(token_network.functions.openChannel(A, B, TEST_SETTLE_TIMEOUT_MIN)) channel_identifier = token_network.functions.getChannelIdentifier(A, B).call() (settle_block_number, _) = token_network.functions.getChannelInfo( channel_identifier, A, B ).call() assert settle_block_number == TEST_SETTLE_TIMEOUT_MIN 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=fake_bytes(32), nonce=0, additional_hash=fake_bytes(32), non_closing_signature=fake_bytes(65), closing_signature=closing_sig, ), {"from": A}, ) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier, A, 0, 0, LOCKSROOT_OF_NO_LOCKS, B, 0, 0, LOCKSROOT_OF_NO_LOCKS, ), {"from": A}, ) call_and_transact(token_network.functions.openChannel(A, B, TEST_SETTLE_TIMEOUT_MAX)) channel_identifier = token_network.functions.getChannelIdentifier(A, B).call() (settle_block_number, _) = token_network.functions.getChannelInfo( channel_identifier, A, B ).call() assert settle_block_number == TEST_SETTLE_TIMEOUT_MAX
def test_withdraw_wrong_state( web3: Web3, token_network: Contract, create_channel_and_deposit: Callable, get_accounts: Callable, withdraw_channel: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: """setTotalWithdraw() should fail on a closed or settled channel""" (A, B) = get_accounts(2) withdraw_A = 1 assert token_network.functions.getChannelIdentifier(A, B).call() == 0 channel_identifier = create_channel_and_deposit(A, B, 10, 14, TEST_SETTLE_TIMEOUT_MIN) (_, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.OPENED # Channel is open, withdraw must work withdraw_channel(channel_identifier, A, withdraw_A, UINT256_MAX, B) 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}, ) (_, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.CLOSED with pytest.raises(TransactionFailed): withdraw_channel(channel_identifier, A, withdraw_A, UINT256_MAX, B) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier, A, 0, 0, LOCKSROOT_OF_NO_LOCKS, B, 0, 0, LOCKSROOT_OF_NO_LOCKS, ), {"from": A}, ) (_, state) = token_network.functions.getChannelInfo(channel_identifier, A, B).call() assert state == ChannelState.REMOVED with pytest.raises(TransactionFailed): withdraw_channel(channel_identifier, A, withdraw_A, UINT256_MAX, B)
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 = call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, EMPTY_BALANCE_HASH, 0, EMPTY_ADDITIONAL_HASH, EMPTY_SIGNATURE, closing_sig, ), {"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.block_number < settle_block_number with pytest.raises(TransactionFailed): call_settle(token_network, channel_identifier, A, vals_A, B, vals_B) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) assert web3.eth.block_number > 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_claimReward_with_settle_call( token_network: Contract, monitoring_service_external: Contract, user_deposit_contract: Contract, event_handler: Callable, monitor_data: Dict, ms_address: HexAddress, web3: Web3, with_settle: bool, ) -> None: A, B = monitor_data["participants"] channel_identifier = monitor_data["channel_identifier"] # wait until MS is allowed to monitor mine_blocks(web3, monitor_data["first_allowed"] - web3.eth.block_number) # MS updates closed channel on behalf of B txn_hash = call_and_transact( monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network.address, monitor_data["reward_proof_signature"], ), {"from": ms_address}, ) # claiming before settlement timeout must fail with pytest.raises(TransactionFailed, match="channel not settled yet"): monitoring_service_external.functions.claimReward( channel_identifier, token_network.address, A, B ).call({"from": ms_address}) # Settle channel after settle_timeout elapsed mine_blocks(web3, 4) if with_settle: call_and_transact( token_network.functions.settleChannel( channel_identifier, B, # participant_B 10, # participant_B_transferred_amount 0, # participant_B_locked_amount LOCKSROOT_OF_NO_LOCKS, # participant_B_locksroot A, # participant_A 20, # participant_A_transferred_amount 0, # participant_A_locked_amount LOCKSROOT_OF_NO_LOCKS, # participant_A_locksroot ) ) # Claim reward for MS call_and_transact( monitoring_service_external.functions.claimReward( channel_identifier, token_network.address, A, B ), {"from": ms_address}, ) # Check REWARD_CLAIMED event reward_identifier = Web3.keccak( encode_single("uint256", channel_identifier) + Web3.toBytes(hexstr=token_network.address) ) ms_ev_handler = event_handler(monitoring_service_external) ms_ev_handler.assert_event( txn_hash, MonitoringServiceEvent.REWARD_CLAIMED, dict( ms_address=ms_address, amount=REWARD_AMOUNT, reward_identifier=reward_identifier, ), ) # Check that MS balance has increased by claiming the reward ms_balance_after_reward = user_deposit_contract.functions.balances(ms_address).call() assert ms_balance_after_reward == REWARD_AMOUNT
def test_settle_channel_event( web3: Web3, get_accounts: Callable, token_network: Contract, create_channel: Callable, channel_deposit: Callable, create_balance_proof: Callable, create_balance_proof_countersignature: Callable, event_handler: Callable, ) -> None: """A successful settleChannel() call causes a SETTLED event""" ev_handler = event_handler(token_network) (A, B) = get_accounts(2) deposit_A = 10 settle_timeout = TEST_SETTLE_TIMEOUT_MIN channel_identifier = create_channel(A, B)[0] channel_deposit(channel_identifier, A, deposit_A, B) balance_proof_A = create_balance_proof(channel_identifier, A, 10, 0, 1, LOCKSROOT_OF_NO_LOCKS) balance_proof_B = create_balance_proof(channel_identifier, B, 5, 0, 3, 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(), ) close_sig_A = create_balance_proof_countersignature( participant=A, channel_identifier=channel_identifier, msg_type=MessageTypeId.BALANCE_PROOF, **balance_proof_B._asdict(), ) call_and_transact( token_network.functions.closeChannel( channel_identifier, B, A, *balance_proof_B._asdict().values(), close_sig_A), {"from": A}, ) call_and_transact( token_network.functions.updateNonClosingBalanceProof( channel_identifier, A, B, *balance_proof_A._asdict().values(), balance_proof_update_signature_B, ), {"from": B}, ) mine_blocks(web3, settle_timeout + 1) txn_hash = call_and_transact( token_network.functions.settleChannel( channel_identifier=channel_identifier, participant1=B, participant1_transferred_amount=5, participant1_locked_amount=0, participant1_locksroot=LOCKSROOT_OF_NO_LOCKS, participant2=A, participant2_transferred_amount=10, participant2_locked_amount=0, participant2_locksroot=LOCKSROOT_OF_NO_LOCKS, ), {"from": A}, ) ev_handler.add(txn_hash, ChannelEvent.SETTLED, check_channel_settled(channel_identifier, 5, 5)) ev_handler.check()
def test_monitor( token_network: Contract, monitoring_service_external: Contract, monitor_data: Dict, ms_address: HexAddress, event_handler: Callable, web3: Web3, ) -> None: A, B = monitor_data["participants"] # UpdateNonClosingBalanceProof is tested speparately, so we assume that all # parameters passed to it are handled correctly. # changing reward amount must lead to a failure during reward signature check with pytest.raises(TransactionFailed, match="Reward proof with wrong non_closing_participant"): txn_hash = monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT + 1, token_network.address, monitor_data["reward_proof_signature"], ).call({"from": ms_address}) # monitoring too early must fail with pytest.raises(TransactionFailed, match="not allowed to monitor"): assert web3.eth.block_number < monitor_data["first_allowed"] txn_hash = call_and_transact( monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network.address, monitor_data["reward_proof_signature"], ), {"from": ms_address}, ) # wait until MS is allowed to monitor mine_blocks(web3, monitor_data["first_allowed"] - web3.eth.block_number) # successful monitor call txn_hash = call_and_transact( monitoring_service_external.functions.monitor( A, B, *monitor_data["balance_proof_B"]._asdict().values(), monitor_data["non_closing_signature"], REWARD_AMOUNT, token_network.address, monitor_data["reward_proof_signature"], ), {"from": ms_address}, ) # NEW_BALANCE_PROOF_RECEIVED must get emitted ms_ev_handler = event_handler(monitoring_service_external) ms_ev_handler.assert_event( txn_hash, MonitoringServiceEvent.NEW_BALANCE_PROOF_RECEIVED, dict( token_network_address=token_network.address, channel_identifier=monitor_data["channel_identifier"], reward_amount=REWARD_AMOUNT, nonce=monitor_data["balance_proof_B"].nonce, ms_address=ms_address, raiden_node_address=B, ), )
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_withdraw_replay_reopened_channel( web3: Web3, token_network: Contract, create_channel: Callable, channel_deposit: Callable, get_accounts: Callable, create_withdraw_signatures: Callable, create_close_signature_for_no_balance_proof: Callable, ) -> None: (A, B) = get_accounts(2) deposit_A = 20 withdraw_A = 5 channel_identifier1 = create_channel(A, B)[0] channel_deposit(channel_identifier1, A, deposit_A, B) (signature_A_for_A, signature_B_for_A) = create_withdraw_signatures([A, B], channel_identifier1, A, withdraw_A, UINT256_MAX) call_and_transact( token_network.functions.setTotalWithdraw( channel_identifier1, A, withdraw_A, UINT256_MAX, signature_A_for_A, signature_B_for_A, ), {"from": A}, ) closing_sig = create_close_signature_for_no_balance_proof( B, channel_identifier1) call_and_transact( token_network.functions.closeChannel( channel_identifier=channel_identifier1, non_closing_participant=A, closing_participant=B, balance_hash=EMPTY_BALANCE_HASH, nonce=0, additional_hash=EMPTY_ADDITIONAL_HASH, non_closing_signature=EMPTY_SIGNATURE, closing_signature=closing_sig, ), {"from": B}, ) mine_blocks(web3, TEST_SETTLE_TIMEOUT_MIN + 1) call_and_transact( token_network.functions.settleChannel( channel_identifier1, A, 0, 0, LOCKSROOT_OF_NO_LOCKS, B, 0, 0, LOCKSROOT_OF_NO_LOCKS, ), {"from": A}, ) # Reopen the channel and make sure we cannot use the old withdraw proof channel_identifier2 = create_channel(A, B)[0] channel_deposit(channel_identifier2, A, deposit_A, B) assert channel_identifier1 != channel_identifier2 with pytest.raises(TransactionFailed): token_network.functions.setTotalWithdraw( channel_identifier2, A, withdraw_A, UINT256_MAX, signature_A_for_A, signature_B_for_A, ).call({"from": A}) # Signed message with correct channel_identifier must work (signature_A_for_A2, signature_B_for_A2) = create_withdraw_signatures([A, B], channel_identifier2, A, withdraw_A, UINT256_MAX) call_and_transact( token_network.functions.setTotalWithdraw( channel_identifier2, A, withdraw_A, UINT256_MAX, signature_A_for_A2, signature_B_for_A2, ), {"from": A}, )