Beispiel #1
0
 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
Beispiel #2
0
    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)
Beispiel #4
0
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)"
            )