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 verify_request_monitoring(self, partner_address: Address, requesting_address: Address) -> bool: """ One should only use this method to verify integrity and signatures of a RequestMonitoring message. """ if not self.non_closing_signature: return False balance_proof_data = pack_balance_proof( nonce=self.balance_proof.nonce, balance_hash=self.balance_proof.balance_hash, additional_hash=self.balance_proof.additional_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=self.balance_proof.chain_id, token_network_address=self.balance_proof.token_network_address, channel_identifier=self.balance_proof.channel_identifier, ), ) blinded_data = pack_signed_balance_proof( msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, nonce=self.balance_proof.nonce, balance_hash=self.balance_proof.balance_hash, additional_hash=self.balance_proof.additional_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=self.balance_proof.chain_id, token_network_address=self.balance_proof.token_network_address, channel_identifier=self.balance_proof.channel_identifier, ), partner_signature=self.balance_proof.signature, ) reward_proof_data = pack_reward_proof( chain_id=self.balance_proof.chain_id, token_network_address=self.balance_proof.token_network_address, reward_amount=self.reward_amount, monitoring_service_contract_address=self. monitoring_service_contract_address, non_closing_participant=requesting_address, non_closing_signature=self.non_closing_signature, ) reward_proof_signature = self.reward_proof_signature or EMPTY_SIGNATURE return (recover(balance_proof_data, self.balance_proof.signature) == partner_address and recover(blinded_data, self.non_closing_signature) == requesting_address and recover(reward_proof_data, reward_proof_signature) == requesting_address)
def test_request_monitoring() -> None: properties = factories.BalanceProofSignedStateProperties( pkey=PARTNER_PRIVKEY) balance_proof = factories.create(properties) partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state( balance_proof) request_monitoring = RequestMonitoring( balance_proof=partner_signed_balance_proof, non_closing_participant=ADDRESS, reward_amount=TokenAmount(55), signature=EMPTY_SIGNATURE, monitoring_service_contract_address=MSC_ADDRESS, ) assert request_monitoring request_monitoring.sign(signer) as_dict = DictSerializer.serialize(request_monitoring) assert DictSerializer.deserialize(as_dict) == request_monitoring # RequestMonitoring can be created directly from BalanceProofSignedState direct_created = RequestMonitoring.from_balance_proof_signed_state( balance_proof=balance_proof, non_closing_participant=ADDRESS, reward_amount=TokenAmount(55), monitoring_service_contract_address=MSC_ADDRESS, ) # `direct_created` is not signed while request_monitoring is assert DictSerializer().serialize( direct_created) != DictSerializer().serialize(request_monitoring) direct_created.sign(signer) # Instances created from same balance proof are equal assert direct_created == request_monitoring other_balance_proof = factories.create( factories.replace(properties, message_hash=keccak(b"2"))) other_instance = RequestMonitoring.from_balance_proof_signed_state( balance_proof=other_balance_proof, non_closing_participant=ADDRESS, reward_amount=TokenAmount(55), monitoring_service_contract_address=MSC_ADDRESS, ) other_instance.sign(signer) # different balance proof ==> non-equality assert other_instance != request_monitoring # test signature verification assert request_monitoring.non_closing_signature reward_proof_data = pack_reward_proof( token_network_address=request_monitoring.balance_proof. token_network_address, chain_id=request_monitoring.balance_proof.chain_id, reward_amount=request_monitoring.reward_amount, monitoring_service_contract_address=MSC_ADDRESS, non_closing_participant=ADDRESS, non_closing_signature=request_monitoring.non_closing_signature, ) assert request_monitoring.reward_proof_signature assert recover(reward_proof_data, request_monitoring.reward_proof_signature) == ADDRESS blinded_data = pack_signed_balance_proof( msg_type=MessageTypeId.BALANCE_PROOF_UPDATE, nonce=request_monitoring.balance_proof.nonce, balance_hash=request_monitoring.balance_proof.balance_hash, additional_hash=request_monitoring.balance_proof.additional_hash, canonical_identifier=factories.make_canonical_identifier( chain_identifier=request_monitoring.balance_proof.chain_id, token_network_address=request_monitoring.balance_proof. token_network_address, channel_identifier=request_monitoring.balance_proof. channel_identifier, ), partner_signature=request_monitoring.balance_proof.signature, ) assert recover(blinded_data, request_monitoring.non_closing_signature) == ADDRESS balance_proof_data = pack_balance_proof( nonce=request_monitoring.balance_proof.nonce, balance_hash=request_monitoring.balance_proof.balance_hash, additional_hash=request_monitoring.balance_proof.additional_hash, canonical_identifier=factories.make_canonical_identifier( chain_identifier=request_monitoring.balance_proof.chain_id, token_network_address=request_monitoring.balance_proof. token_network_address, channel_identifier=request_monitoring.balance_proof. channel_identifier, ), ) assert (recover( balance_proof_data, request_monitoring.balance_proof.signature) == PARTNER_ADDRESS) assert request_monitoring.verify_request_monitoring( PARTNER_ADDRESS, ADDRESS)
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)" )