def detail_participants( # pylint: disable=unused-argument participant1, participant2, block_identifier, channel_identifier): transferred_amount = 1 locked_amount = 1 locksroot = make_32bytes() balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) our_details = ParticipantDetails( address=raiden.address, deposit=5, withdrawn=0, is_closer=False, balance_hash=balance_hash, nonce=1, locksroot=locksroot, locked_amount=locked_amount, ) transferred_amount = 1 locked_amount = 1 # Let's mock here that partner locksroot is 0x0 balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) partner_details = ParticipantDetails( address=participant, deposit=5, withdrawn=0, is_closer=True, balance_hash=balance_hash, nonce=1, locksroot=EMPTY_HASH, locked_amount=locked_amount, ) return ParticipantsDetails(our_details, partner_details)
def detail_participants(participant1, participant2, channel_identifier): transferred_amount = 1 locked_amount = 1 locksroot = make_32bytes() balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) our_details = ParticipantDetails( address=raiden.address, deposit=5, withdrawn=0, is_closer=False, balance_hash=balance_hash, nonce=1, locksroot=locksroot, locked_amount=locked_amount, ) transferred_amount = 1 locked_amount = 1 # Let's mock here that partner locksroot is 0x0 locksroot = EMPTY_HASH balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) partner_details = ParticipantDetails( address=participant, deposit=5, withdrawn=0, is_closer=True, balance_hash=balance_hash, nonce=1, locksroot=locksroot, locked_amount=locked_amount, ) return ParticipantsDetails(our_details, partner_details)
def detail_participants(_participant1, _participant2, _block_identifier, _channel_identifier): transferred_amount = TokenAmount(1) locked_amount = LockedAmount(1) locksroot = make_locksroot() balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) our_details = ParticipantDetails( address=raiden.address, deposit=TokenAmount(5), withdrawn=WithdrawAmount(0), is_closer=False, balance_hash=balance_hash, nonce=Nonce(1), locksroot=locksroot, locked_amount=locked_amount, ) transferred_amount = TokenAmount(1) locked_amount = LockedAmount(1) # Let's mock here that partner locksroot is 0x0 balance_hash = hash_balance_data(transferred_amount, locked_amount, locksroot) partner_details = ParticipantDetails( address=participant, deposit=TokenAmount(5), withdrawn=WithdrawAmount(0), is_closer=True, balance_hash=balance_hash, nonce=Nonce(1), locksroot=LOCKSROOT_OF_NO_LOCKS, locked_amount=locked_amount, ) return ParticipantsDetails(our_details, partner_details)
def _(properties: BalanceProofSignedStateProperties, defaults=None) -> BalanceProofSignedState: defaults = defaults or BALANCE_PROOF_SIGNED_STATE_DEFAULTS params = _properties_to_dict(properties, defaults) params.update( _properties_to_dict(params.pop('balance_proof'), defaults.balance_proof), ) signer = LocalSigner(params.pop('pkey')) if params['signature'] is EMPTY: keys = ('transferred_amount', 'locked_amount', 'locksroot') balance_hash = hash_balance_data(**_partial_dict(params, *keys)) canonical_identifier = CanonicalIdentifier( chain_identifier=params.pop('chain_id'), token_network_address=params.pop('token_network_identifier'), channel_identifier=params.pop('channel_identifier'), ) params['canonical_identifier'] = canonical_identifier data_to_sign = balance_proof.pack_balance_proof( balance_hash=balance_hash, additional_hash=params['message_hash'], canonical_identifier=canonical_identifier, nonce=params.get('nonce'), ) params['signature'] = signer.sign(data=data_to_sign) return BalanceProofSignedState(**params)
def __post_init__(self) -> None: typecheck(self.nonce, int) typecheck(self.transferred_amount, T_TokenAmount) typecheck(self.locked_amount, T_TokenAmount) typecheck(self.locksroot, T_Locksroot) if self.nonce <= 0: raise ValueError("nonce cannot be zero or negative") if self.nonce > UINT64_MAX: raise ValueError("nonce is too large") if self.transferred_amount < 0: raise ValueError("transferred_amount cannot be negative") if self.transferred_amount > UINT256_MAX: raise ValueError("transferred_amount is too large") if len(self.locksroot) != 32: raise ValueError("locksroot must have length 32") self.canonical_identifier.validate() self.balance_hash = hash_balance_data( transferred_amount=self.transferred_amount, locked_amount=self.locked_amount, locksroot=self.locksroot, )
def make_signed_balance_proof_from_unsigned( unsigned: BalanceProofUnsignedState, signer: Signer) -> BalanceProofSignedState: balance_hash = hash_balance_data( transferred_amount=unsigned.transferred_amount, locked_amount=unsigned.locked_amount, locksroot=unsigned.locksroot, ) additional_hash = make_additional_hash() data_to_sign = balance_proof.pack_balance_proof( balance_hash=balance_hash, additional_hash=additional_hash, canonical_identifier=unsigned.canonical_identifier, nonce=unsigned.nonce, ) signature = signer.sign(data=data_to_sign) sender = signer.address return BalanceProofSignedState( nonce=unsigned.nonce, transferred_amount=unsigned.transferred_amount, locked_amount=unsigned.locked_amount, locksroot=unsigned.locksroot, message_hash=additional_hash, signature=signature, sender=sender, canonical_identifier=unsigned.canonical_identifier, )
def make_signed_balance_proof( nonce: typing.Nonce = EMPTY, transferred_amount: typing.TokenAmount = EMPTY, locked_amount: typing.TokenAmount = EMPTY, token_network_address: typing.TokenNetworkID = EMPTY, channel_identifier: typing.ChannelID = EMPTY, locksroot: typing.Locksroot = EMPTY, extra_hash: typing.Keccak256 = EMPTY, private_key: bytes = EMPTY, sender_address: typing.Address = EMPTY, ) -> BalanceProofSignedState: nonce = if_empty(nonce, make_uint256()) transferred_amount = if_empty(transferred_amount, make_uint256()) locked_amount = if_empty(locked_amount, make_uint256()) token_network_address = if_empty(token_network_address, make_address()) channel_identifier = if_empty(channel_identifier, make_uint256()) locksroot = if_empty(locksroot, make_32bytes()) extra_hash = if_empty(extra_hash, make_keccak_hash()) private_key = if_empty(private_key, make_privatekey()) sender_address = if_empty(sender_address, make_address()) signer = LocalSigner(private_key) balance_hash = hash_balance_data( transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, ) data_to_sign = balance_proof.pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=extra_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=UNIT_CHAIN_ID, token_network_address=token_network_address, channel_identifier=channel_identifier, ), ) signature = signer.sign(data=data_to_sign) return BalanceProofSignedState( nonce=nonce, transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, message_hash=extra_hash, signature=signature, sender=sender_address, canonical_identifier=CanonicalIdentifier( chain_identifier=UNIT_CHAIN_ID, token_network_address=token_network_address, channel_identifier=channel_identifier, ), )
def make_signed_balance_proof( nonce, transferred_amount, locked_amount, token_network_address, channel_address, locksroot, extra_hash, private_key, sender_address, ): data_to_sign = balance_proof.signing_data( nonce, transferred_amount, locked_amount, channel_address, locksroot, extra_hash, ) balance_hash = hash_balance_data( transferred_amount, locked_amount, locksroot, ) data_to_sign = balance_proof.pack_signing_data( nonce=nonce, balance_hash=balance_hash, additional_hash=extra_hash, channel_identifier=channel_address, token_network_identifier=token_network_address, chain_id=UNIT_CHAIN_ID, ) signature = signing.sign(data_to_sign, private_key) signed_balance_proof = BalanceProofSignedState( nonce, transferred_amount, locked_amount, locksroot, token_network_address, channel_address, extra_hash, signature, sender_address, UNIT_CHAIN_ID, ) return signed_balance_proof
def _data_to_sign(self) -> bytes: balance_hash = hash_balance_data(self.transferred_amount, self.locked_amount, self.locksroot) balance_proof_packed = pack_balance_proof( nonce=self.nonce, balance_hash=balance_hash, additional_hash=AdditionalHash(self.message_hash), canonical_identifier=CanonicalIdentifier( chain_identifier=self.chain_id, token_network_address=self.token_network_address, channel_identifier=self.channel_identifier, ), ) return balance_proof_packed
def make_signed_balance_proof( nonce: typing.Nonce = EMPTY, transferred_amount: typing.TokenAmount = EMPTY, locked_amount: typing.TokenAmount = EMPTY, token_network_address: typing.TokenNetworkID = EMPTY, channel_identifier: typing.ChannelID = EMPTY, locksroot: typing.Locksroot = EMPTY, extra_hash: typing.Keccak256 = EMPTY, private_key: PrivateKey = EMPTY, sender_address: typing.Address = EMPTY, ) -> BalanceProofSignedState: nonce = if_empty(nonce, make_uint256()) transferred_amount = if_empty(transferred_amount, make_uint256()) locked_amount = if_empty(locked_amount, make_uint256()) token_network_address = if_empty(token_network_address, make_address()) channel_identifier = if_empty(channel_identifier, make_uint256()) locksroot = if_empty(locksroot, make_32bytes()) extra_hash = if_empty(extra_hash, make_keccak_hash()) private_key = if_empty(private_key, make_privatekey()) sender_address = if_empty(sender_address, make_address()) balance_hash = hash_balance_data( transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, ) data_to_sign = balance_proof.pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=extra_hash, channel_identifier=channel_identifier, token_network_identifier=token_network_address, chain_id=UNIT_CHAIN_ID, ) signature = eth_sign(privkey=private_key, data=data_to_sign) return BalanceProofSignedState( nonce=nonce, transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, token_network_identifier=token_network_address, channel_identifier=channel_identifier, message_hash=extra_hash, signature=signature, sender=sender_address, chain_id=UNIT_CHAIN_ID, )
def _verify_settle_state( self, transferred_amount: int, locked_amount: int, locksroot: typing.Locksroot, partner: typing.Address, partner_transferred_amount: int, partner_locked_amount: int, partner_locksroot: typing.Locksroot, ): """Check if our local state is up to date with on-chain state.""" participant_data = self.detail_participants(self.node_address, partner) our_balance_hash_ok = hash_balance_data( transferred_amount, locked_amount, locksroot, ) == participant_data.our_details.balance_hash partner_balance_hash_ok = hash_balance_data( partner_transferred_amount, partner_locked_amount, partner_locksroot, ) == participant_data.partner_details.balance_hash return our_balance_hash_ok and partner_balance_hash_ok
def _data_to_sign(self) -> bytes: balance_hash = hash_balance_data( self.transferred_amount, self.locked_amount, self.locksroot, ) balance_proof_packed = pack_signing_data( nonce=self.nonce, balance_hash=balance_hash, additional_hash=self.message_hash, channel_identifier=self.channel, token_network_identifier=self.token_network_address, chain_id=self.chain_id, ) return balance_proof_packed
def _data_to_sign(self) -> bytes: balance_hash = hash_balance_data( self.transferred_amount, self.locked_amount, self.locksroot, ) balance_proof_packed = pack_signing_data( nonce=self.nonce, balance_hash=balance_hash, additional_hash=self.message_hash, channel_identifier=self.channel_identifier, token_network_identifier=self.token_network_address, chain_id=self.chain_id, ) return balance_proof_packed
def sign2(self, private_key, chain_id): """ Creates the signature to the balance proof. Will be used in the SC refactoring. """ balance_hash = hash_balance_data( self.transferred_amount, self.locked_amount, self.locksroot, ) balance_proof_packed = pack_signing_data2( nonce=self.nonce, balance_hash=balance_hash, additional_hash=self.message_hash.decode(), channel_identifier=self.channel, token_network_address=self.token_network_address, chain_id=chain_id, ) self.signature = encode_hex( sign_data(self.privkey, balance_proof_packed), )
def from_balance_proof_signed_state( cls, balance_proof: BalanceProofSignedState ) -> "SignedBlindedBalanceProof": typecheck(balance_proof, BalanceProofSignedState) # pylint: disable=unexpected-keyword-arg return cls( channel_identifier=balance_proof.channel_identifier, token_network_address=balance_proof.token_network_address, nonce=balance_proof.nonce, additional_hash=balance_proof.message_hash, chain_id=balance_proof.chain_id, signature=balance_proof.signature, balance_hash=hash_balance_data( balance_proof.transferred_amount, balance_proof.locked_amount, balance_proof.locksroot, ), )
def _(properties: BalanceProofSignedStateProperties, defaults=None) -> BalanceProofSignedState: defaults = defaults or BalanceProofSignedStateProperties.DEFAULTS params = create_properties(properties, defaults).__dict__ signer = LocalSigner(params.pop("pkey")) if params["signature"] is GENERATE: keys = ("transferred_amount", "locked_amount", "locksroot") balance_hash = hash_balance_data(**_partial_dict(params, *keys)) data_to_sign = balance_proof.pack_balance_proof( balance_hash=balance_hash, additional_hash=params["message_hash"], canonical_identifier=params["canonical_identifier"], nonce=params.get("nonce"), ) params["signature"] = signer.sign(data=data_to_sign) return BalanceProofSignedState(**params)
def _(properties: BalanceProofSignedStateProperties, defaults=None) -> BalanceProofSignedState: defaults = defaults or BALANCE_PROOF_SIGNED_STATE_DEFAULTS params = _properties_to_dict(properties, defaults) params.update( _properties_to_dict(params.pop('balance_proof'), defaults.balance_proof), ) if params['signature'] is EMPTY: keys = ('transferred_amount', 'locked_amount', 'locksroot') balance_hash = hash_balance_data(**_partial_dict(params, *keys)) keys = ('nonce', 'channel_identifier', 'token_network_identifier') data_to_sign = balance_proof.pack_balance_proof( balance_hash=balance_hash, additional_hash=params['message_hash'], chain_id=UNIT_CHAIN_ID, **_partial_dict(params, *keys), ) params['signature'] = eth_sign(privkey=params.pop('pkey'), data=data_to_sign) return BalanceProofSignedState(**params)
def __post_init__(self) -> None: typecheck(self.nonce, int) typecheck(self.transferred_amount, T_TokenAmount) typecheck(self.locked_amount, T_LockedAmount) typecheck(self.locksroot, T_Locksroot) typecheck(self.message_hash, bytes) typecheck(self.signature, T_Signature) typecheck(self.sender, T_Address) if self.nonce <= 0: raise ValueError("nonce cannot be zero or negative") if self.nonce > UINT64_MAX: raise ValueError("nonce is too large") if self.transferred_amount < 0: raise ValueError("transferred_amount cannot be negative") if self.transferred_amount > UINT256_MAX: raise ValueError("transferred_amount is too large") if len(self.locksroot) != 32: raise ValueError("locksroot must have length 32") if len(self.message_hash) != 32: raise ValueError("message_hash is an invalid hash") if len(self.signature) != 65: raise ValueError("signature is an invalid signature") self.canonical_identifier.validate() self.balance_hash = hash_balance_data( transferred_amount=self.transferred_amount, locked_amount=self.locked_amount, locksroot=self.locksroot, )
def hash_balance_data(transferred_amount: TokenAmount, locked_amount: LockedAmount, locksroot: Locksroot) -> BalanceHash: return hash_balance_data(transferred_amount, locked_amount, locksroot)
def find_max_pending_transfers(gas_limit): """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract('SecretRegistry') tester.deploy_contract( 'HumanStandardToken', _initialAmount=100000, _decimalUnits=3, _tokenName='SomeToken', _tokenSymbol='SMT', ) tester.deploy_contract( 'TokenNetwork', _token_address=tester.contract_address('HumanStandardToken'), _secret_registry=tester.contract_address('SecretRegistry'), _chain_id=1, _settlement_timeout_min=100, _settlement_timeout_max=200, ) tester.call_transaction( 'HumanStandardToken', 'transfer', _to=tester.accounts[1], _value=10000, ) receipt = tester.call_transaction( 'TokenNetwork', 'openChannel', participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = int(hexlify(receipt['logs'][0]['topics'][1]), 16) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[0], _spender=tester.contract_address('TokenNetwork'), _value=10000, ) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[1], _spender=tester.contract_address('TokenNetwork'), _value=5000, ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print( "Measuring unlock()'s gas cost for different Merkle tree widths, can take a while..." ) before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = 10 additional_hash = urandom(32) token_network_identifier = tester.contract_address('TokenNetwork') while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size, ) balance_hash = hash_balance_data(3000, 2000, pending_transfers_tree.merkle_root) data_to_sign = pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=additional_hash, channel_identifier=channel_identifier, token_network_identifier=token_network_identifier, chain_id=1, ) signature = eth_sign(privkey=tester.private_keys[1], data=data_to_sign) tester.call_transaction( 'TokenNetwork', 'closeChannel', channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( 'TokenNetwork', 'settleChannel', channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b'\x00' * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree.merkle_root, ) receipt = tester.call_transaction( 'TokenNetwork', 'unlock', channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt['gasUsed'] if gas_used <= gas_limit: enough = tree_size print( f'{tree_size} pending transfers work ({gas_used} gas needed to unlock)' ) else: too_much = tree_size print( f'{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)' )
def find_max_pending_transfers(gas_limit): """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract('SecretRegistry') tester.deploy_contract( 'HumanStandardToken', _initialAmount=100000, _decimalUnits=3, _tokenName='SomeToken', _tokenSymbol='SMT', ) tester.deploy_contract( 'TokenNetwork', _token_address=tester.contract_address('HumanStandardToken'), _secret_registry=tester.contract_address('SecretRegistry'), _chain_id=1, _settlement_timeout_min=100, _settlement_timeout_max=200, ) tester.call_transaction( 'HumanStandardToken', 'transfer', _to=tester.accounts[1], _value=10000, ) receipt = tester.call_transaction( 'TokenNetwork', 'openChannel', participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = int(encode_hex(receipt['logs'][0]['topics'][1]), 16) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[0], _spender=tester.contract_address('TokenNetwork'), _value=10000, ) tester.call_transaction( 'HumanStandardToken', 'approve', sender=tester.accounts[1], _spender=tester.contract_address('TokenNetwork'), _value=5000, ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( 'TokenNetwork', 'setTotalDeposit', channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print("Measuring unlock()'s gas cost for different Merkle tree widths, can take a while...") before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = 10 additional_hash = urandom(32) token_network_identifier = tester.contract_address('TokenNetwork') while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size, ) balance_hash = hash_balance_data(3000, 2000, pending_transfers_tree.merkle_root) data_to_sign = pack_balance_proof( nonce=nonce, balance_hash=balance_hash, additional_hash=additional_hash, channel_identifier=channel_identifier, token_network_identifier=token_network_identifier, chain_id=1, ) signature = eth_sign(privkey=tester.private_keys[1], data=data_to_sign) tester.call_transaction( 'TokenNetwork', 'closeChannel', channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( 'TokenNetwork', 'settleChannel', channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b'\x00' * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree.merkle_root, ) receipt = tester.call_transaction( 'TokenNetwork', 'unlock', channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt['gasUsed'] if gas_used <= gas_limit: enough = tree_size print(f'{tree_size} pending transfers work ({gas_used} gas needed to unlock)') else: too_much = tree_size print(f'{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)')
def test_hash_balance_data(values, expected): assert (hash_balance_data(values[0], values[1], values[2]) == expected)
def test_hash_balance_data(values, expected): assert(hash_balance_data(values[0], values[1], values[2]) == expected)
def balance_hash(self): return hash_balance_data( transferred_amount=self.transferred_amount, locked_amount=self.locked_amount, locksroot=self.locksroot, )
def find_max_pending_transfers(gas_limit) -> None: """Measure gas consumption of TokenNetwork.unlock() depending on number of pending transfers and find the maximum number of pending transfers so gas_limit is not exceeded.""" tester = ContractTester(generate_keys=2) tester.deploy_contract("SecretRegistry") tester.deploy_contract( "HumanStandardToken", _initialAmount=100_000, _decimalUnits=3, _tokenName="SomeToken", _tokenSymbol="SMT", ) tester.deploy_contract( "TokenNetwork", _token_address=tester.contract_address("HumanStandardToken"), _secret_registry=tester.contract_address("SecretRegistry"), _chain_id=CHAIN_ID, _settlement_timeout_min=100, _settlement_timeout_max=200, _deprecation_executor=tester.accounts[0], _channel_participant_deposit_limit=10000, _token_network_deposit_limit=10000, ) tester.call_transaction("HumanStandardToken", "transfer", _to=tester.accounts[1], _value=10000) receipt = tester.call_transaction( "TokenNetwork", "openChannel", participant1=tester.accounts[0], participant2=tester.accounts[1], settle_timeout=150, ) channel_identifier = ChannelID( int(encode_hex(receipt["logs"][0]["topics"][1]), 16)) tester.call_transaction( "HumanStandardToken", "approve", sender=tester.accounts[0], _spender=tester.contract_address("TokenNetwork"), _value=10000, ) tester.call_transaction( "HumanStandardToken", "approve", sender=tester.accounts[1], _spender=tester.contract_address("TokenNetwork"), _value=5000, ) tester.call_transaction( "TokenNetwork", "setTotalDeposit", channel_identifier=channel_identifier, participant=tester.accounts[0], total_deposit=5000, partner=tester.accounts[1], ) tester.call_transaction( "TokenNetwork", "setTotalDeposit", channel_identifier=channel_identifier, participant=tester.accounts[1], total_deposit=2000, partner=tester.accounts[0], ) print( "Measuring unlock()'s gas cost for different Merkle tree widths, can take a while..." ) before_closing = tester.tester.take_snapshot() enough = 0 too_much = 1024 nonce = Nonce(10) additional_hash = AdditionalHash(urandom(32)) token_network_address = tester.contract_address("TokenNetwork") while enough + 1 < too_much: tree_size = (enough + too_much) // 2 tester.tester.revert_to_snapshot(before_closing) pending_transfers_tree = get_pending_transfers_tree( tester.web3, unlockable_amounts=[1] * tree_size, expired_amounts=[]) balance_hash = hash_balance_data( transferred_amount=TokenAmount(3000), locked_amount=TokenAmount(2000), locksroot=Locksroot( pending_transfers_tree.hash_of_packed_transfers), ) canonical_identifier = CanonicalIdentifier( chain_identifier=CHAIN_ID, token_network_address=token_network_address, channel_identifier=ChannelID(channel_identifier), ) data_to_sign = pack_balance_proof( nonce=Nonce(nonce), balance_hash=balance_hash, additional_hash=additional_hash, canonical_identifier=canonical_identifier, ) signature = LocalSigner(tester.private_keys[1]).sign(data=data_to_sign) tester.call_transaction( "TokenNetwork", "closeChannel", channel_identifier=channel_identifier, partner=tester.accounts[1], balance_hash=balance_hash, nonce=nonce, additional_hash=additional_hash, signature=signature, ) tester.tester.mine_blocks(160) # close settlement window tester.call_transaction( "TokenNetwork", "settleChannel", channel_identifier=channel_identifier, participant1=tester.accounts[0], participant1_transferred_amount=0, participant1_locked_amount=0, participant1_locksroot=b"\x00" * 32, participant2=tester.accounts[1], participant2_transferred_amount=3000, participant2_locked_amount=2000, participant2_locksroot=pending_transfers_tree. hash_of_packed_transfers, ) receipt = tester.call_transaction( "TokenNetwork", "unlock", channel_identifier=channel_identifier, participant=tester.accounts[0], partner=tester.accounts[1], merkle_tree_leaves=pending_transfers_tree.packed_transfers, ) gas_used = receipt["gasUsed"] if gas_used <= gas_limit: enough = tree_size print( f"{tree_size} pending transfers work ({gas_used} gas needed to unlock)" ) else: too_much = tree_size print( f"{tree_size} pending transfers are too much ({gas_used} gas needed to unlock)" )