Beispiel #1
0
    def transfer(self, to_address, amount):
        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'to_address': pex(to_address),
            'amount': amount,
        }
        log.debug('transfer called', **log_details)

        startgas = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL
        transaction_hash = self.proxy.transact(
            'transfer',
            safe_gas_limit(startgas),
            to_checksum_address(to_address),
            amount,
        )

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client, transaction_hash)
        if receipt_or_none:
            log.critical('transfer failed', **log_details)
            raise TransactionThrew('Transfer', receipt_or_none)

        # TODO: check Transfer event (issue: #2598)
        log.info('transfer successful', **log_details)
Beispiel #2
0
def test_filter_end_block_inclusive(deploy_client):
    """ A filter includes events from the block given in from_block
    until and including end_block. """
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    # call the create event function twice and wait for confirmation each time
    startgas = safe_gas_limit(
        contract_proxy.estimate_gas('pending', 'createEvent', 1))
    transaction_1 = contract_proxy.transact('createEvent', startgas, 1)
    deploy_client.poll(transaction_1)
    transaction_2 = contract_proxy.transact('createEvent', startgas, 2)
    deploy_client.poll(transaction_2)

    result_1 = deploy_client.get_filter_events(contract_proxy.contract_address)
    block_number_events = get_list_of_block_numbers(result_1)
    block_number_event_1 = block_number_events[0]
    block_number_event_2 = block_number_events[1]

    # inclusive to_block should return first event
    result_2 = deploy_client.get_filter_events(
        contract_proxy.contract_address,
        to_block=block_number_event_1,
    )
    assert get_list_of_block_numbers(result_2) == [block_number_event_1]

    # this should include the second event
    result_3 = deploy_client.get_filter_events(
        contract_proxy.contract_address,
        to_block=block_number_event_2,
    )
    assert get_list_of_block_numbers(result_3) == block_number_events
 def send_transaction():
     check_block = deploy_client.get_checking_block()
     startgas = contract_proxy.estimate_gas(check_block, "waste_storage", iterations)
     startgas = safe_gas_limit(startgas)
     transaction = contract_proxy.transact("waste_storage", startgas, iterations)
     deploy_client.poll(transaction)
     return deploy_client.get_transaction_receipt(transaction)
def test_duplicated_transaction_same_gas_price_raises(deploy_client):
    """ If the same transaction is sent twice a JSON RPC error is raised. """
    gas_price = 2000000000
    gas_price_strategy = make_fixed_gas_price_strategy(gas_price)
    deploy_client.web3.eth.setGasPriceStrategy(gas_price_strategy)
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    second_client = JSONRPCClient(
        web3=deploy_client.web3,
        privkey=deploy_client.privkey,
    )

    second_proxy = second_client.new_contract_proxy(
        contract_proxy.contract.abi,
        contract_proxy.contract_address,
    )

    check_block = deploy_client.get_checking_block()
    startgas = safe_gas_limit(contract_proxy.estimate_gas(check_block, 'ret'))

    with pytest.raises(TransactionAlreadyPending):
        second_proxy.transact('ret', startgas)
        contract_proxy.transact('ret', startgas)
Beispiel #5
0
def test_duplicated_transaction_same_gas_price_raises(deploy_client):
    """ If the same transaction is sent twice a JSON RPC error is raised. """
    gas_price = 2000000000
    gas_price_strategy = make_fixed_gas_price_strategy(gas_price)
    deploy_client.web3.eth.setGasPriceStrategy(gas_price_strategy)
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(to_checksum_address(address))) > 0

    second_client = JSONRPCClient(
        web3=deploy_client.web3,
        privkey=deploy_client.privkey,
    )

    second_proxy = second_client.new_contract_proxy(
        contract_proxy.contract.abi,
        contract_proxy.contract_address,
    )

    startgas = safe_gas_limit(contract_proxy.estimate_gas('pending', 'ret'))

    with pytest.raises(TransactionAlreadyPending):
        second_proxy.transact('ret', startgas)
        contract_proxy.transact('ret', startgas)
Beispiel #6
0
    def transfer(self, to_address: Address, amount: TokenAmount):
        # Note that given_block_identifier is not used here as there
        # are no preconditions to check before sending the transaction
        log_details = {
            "node": pex(self.node_address),
            "contract": pex(self.address),
            "to_address": pex(to_address),
            "amount": amount,
        }
        log.debug("transfer called", **log_details)

        startgas = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL
        transaction_hash = self.proxy.transact("transfer",
                                               safe_gas_limit(startgas),
                                               to_checksum_address(to_address),
                                               amount)

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            log.critical("transfer failed", **log_details)
            raise TransactionThrew("Transfer", receipt_or_none)

        # TODO: check Transfer event (issue: #2598)
        log.info("transfer successful", **log_details)
Beispiel #7
0
    def register_endpoint(self, node_address, endpoint):
        if node_address != self.client.address:
            raise ValueError("node_address doesnt match this node's address")

        log_details = {
            'node': pex(self.node_address),
            'node_address': pex(node_address),
            'endpoint': endpoint,
        }
        log.debug('registerEndpoint called', **log_details)

        transaction_hash = self.proxy.transact(
            'registerEndpoint',
            safe_gas_limit(GAS_REQUIRED_FOR_ENDPOINT_REGISTER),
            endpoint,
        )

        self.client.poll(transaction_hash)

        receipt_or_none = check_transaction_threw(self.client, transaction_hash)
        if receipt_or_none:
            log.critical('registerEndpoint failed', **log_details)
            raise TransactionThrew('Register Endpoint', receipt_or_none)

        log.debug('registerEndpoint successful', **log_details)
Beispiel #8
0
def test_reusing_nonce_with_lower_gas_raises(
        deploy_client: JSONRPCClient) -> None:
    """ If a _new_ transaction is sent but with a lower gas the exception
    `ReplacementTransactionUnderpriced` is raised.
    """
    # Use a _decreasing_ gas price strategy so that the second transactions is
    # lower than the first.
    deploy_client.web3.eth.setGasPriceStrategy(
        make_decreasing_gas_price_strategy(GasPrice(2000000000)))
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    client_invalid_nonce = JSONRPCClient(web3=deploy_client.web3,
                                         privkey=deploy_client.privkey)

    check_block = deploy_client.get_checking_block()
    gas_estimate = contract_proxy.estimate_gas(check_block, "ret")
    assert gas_estimate, "Gas estimation should not fail here"
    startgas = safe_gas_limit(gas_estimate)

    contract_proxy.transact("ret", startgas)

    # At this point `client_invalid_nonce` has a nonce that is `1` too low,
    # since a transaction was sent using `deploy_client` above and these two
    # instances share the same underlying private key.
    #
    # Note that the same function is called in this test but the gas is decreasing.
    with pytest.raises(ReplacementTransactionUnderpriced):
        client_invalid_nonce.new_contract_proxy(
            abi=contract_proxy.contract.abi,
            contract_address=contract_proxy.contract_address).transact(
                "ret_str", contract_proxy.estimate_gas(check_block, "ret_str"))
Beispiel #9
0
    def transfer(self, to_address, amount):
        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'to_address': pex(to_address),
            'amount': amount,
        }
        log.debug('transfer called', **log_details)

        startgas = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL
        transaction_hash = self.proxy.transact(
            'transfer',
            safe_gas_limit(startgas),
            to_checksum_address(to_address),
            amount,
        )

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            log.critical('transfer failed', **log_details)
            raise TransactionThrew('Transfer', receipt_or_none)

        # TODO: check Transfer event (issue: #2598)
        log.info('transfer successful', **log_details)
def test_filter_start_block_inclusive(deploy_client):
    """ A filter includes events from the block given in from_block """
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    check_block = deploy_client.get_checking_block()
    # call the create event function twice and wait for confirmation each time
    startgas = safe_gas_limit(contract_proxy.estimate_gas(check_block, "createEvent", 1))
    transaction_1 = contract_proxy.transact("createEvent", startgas, 1)
    deploy_client.poll(transaction_1)
    transaction_2 = contract_proxy.transact("createEvent", startgas, 2)
    deploy_client.poll(transaction_2)

    result_1 = deploy_client.get_filter_events(contract_proxy.contract_address)
    block_number_events = get_list_of_block_numbers(result_1)
    block_number_event_1 = block_number_events[0]
    block_number_event_2 = block_number_events[1]

    # inclusive from_block should return both events
    result_2 = deploy_client.get_filter_events(
        contract_proxy.contract_address, from_block=block_number_event_1
    )
    assert get_list_of_block_numbers(result_2) == block_number_events

    # a higher from_block must not contain the first event
    result_3 = deploy_client.get_filter_events(
        contract_proxy.contract_address, from_block=block_number_event_1 + 1
    )
    assert get_list_of_block_numbers(result_3) == [block_number_event_2]
Beispiel #11
0
    def register_endpoint(self, node_address, endpoint):
        if node_address != self.client.address:
            raise ValueError("node_address doesnt match this node's address")

        log_details = {
            'node': pex(self.node_address),
            'node_address': pex(node_address),
            'endpoint': endpoint,
        }
        log.debug('registerEndpoint called', **log_details)

        transaction_hash = self.proxy.transact(
            'registerEndpoint',
            safe_gas_limit(GAS_REQUIRED_FOR_ENDPOINT_REGISTER),
            endpoint,
        )

        self.client.poll(transaction_hash)

        receipt_or_none = check_transaction_threw(self.client, transaction_hash)
        if receipt_or_none:
            log.critical('registerEndpoint failed', **log_details)
            raise TransactionThrew('Register Endpoint', receipt_or_none)

        log.debug('registerEndpoint successful', **log_details)
    def _new_netting_channel(self, partner: typing.Address,
                             settle_timeout: int):
        if self.channel_exists_and_not_settled(self.node_address, partner):
            raise DuplicatedChannelError(
                'Channel with given partner address already exists')

        gas_limit = self.proxy.estimate_gas(
            'openChannel',
            self.node_address,
            partner,
            settle_timeout,
        )
        gas_limit = safe_gas_limit(gas_limit, GAS_REQUIRED_FOR_OPEN_CHANNEL)

        transaction_hash = self.proxy.transact(
            'openChannel',
            gas_limit,
            self.node_address,
            partner,
            settle_timeout,
        )

        if not transaction_hash:
            raise RuntimeError('open channel transaction failed')

        self.client.poll(transaction_hash)

        if check_transaction_threw(self.client, transaction_hash):
            raise DuplicatedChannelError('Duplicated channel')

        return transaction_hash
Beispiel #13
0
def test_duplicated_transaction_different_gas_price_raises(deploy_client):
    """ If the same transaction is sent twice a JSON RPC error is raised. """
    gas_price = 2000000000
    deploy_client.web3.eth.setGasPriceStrategy(
        make_decreasing_gas_price_strategy(gas_price))
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    second_client = JSONRPCClient(
        web3=deploy_client.web3,
        privkey=deploy_client.privkey,
    )

    second_proxy = second_client.new_contract_proxy(
        contract_proxy.contract.abi,
        contract_proxy.contract_address,
    )

    startgas = safe_gas_limit(contract_proxy.estimate_gas('pending', 'ret'))

    with pytest.raises(ReplacementTransactionUnderpriced):
        second_proxy.transact('ret', startgas)
        contract_proxy.transact('ret', startgas)
Beispiel #14
0
def test_resending_mined_transaction_raises(
        deploy_client: JSONRPCClient) -> None:
    """ If a mined transaction is re-sent the exception `EthereumNonceTooLow` is raised. """
    # Use a _fixed_ gas price strategy so that both transactions are identical.
    deploy_client.web3.eth.setGasPriceStrategy(
        make_fixed_gas_price_strategy(GasPrice(2000000000)))
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    # Create a new instance of the JSONRPCClient, this will store the current available nonce
    client_invalid_nonce = JSONRPCClient(deploy_client.web3,
                                         deploy_client.privkey)

    check_block = deploy_client.get_checking_block()
    gas_estimate = contract_proxy.estimate_gas(check_block, "ret")
    assert gas_estimate, "Gas estimation should not fail here"
    startgas = safe_gas_limit(gas_estimate)

    txhash = contract_proxy.transact("ret", startgas)
    deploy_client.poll(txhash)

    # At this point `client_invalid_nonce` has a nonce that is `1` too low,
    # since a transaction was sent using `deploy_client` above and these two
    # instances share the same underlying private key.
    #
    # Note that the same function is called in this test.
    with pytest.raises(EthereumNonceTooLow):
        client_invalid_nonce.new_contract_proxy(
            abi=contract_proxy.contract.abi,
            contract_address=contract_proxy.contract_address).transact(
                "ret", startgas)
Beispiel #15
0
def test_filter_end_block_inclusive(deploy_client):
    """ A filter includes events from the block given in from_block
    until and including end_block. """
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    # call the create event function twice and wait for confirmation each time
    startgas = safe_gas_limit(contract_proxy.estimate_gas('pending', 'createEvent', 1))
    transaction_1 = contract_proxy.transact('createEvent', startgas, 1)
    deploy_client.poll(transaction_1)
    transaction_2 = contract_proxy.transact('createEvent', startgas, 2)
    deploy_client.poll(transaction_2)

    result_1 = deploy_client.get_filter_events(contract_proxy.contract_address)
    block_number_events = get_list_of_block_numbers(result_1)
    block_number_event_1 = block_number_events[0]
    block_number_event_2 = block_number_events[1]

    # inclusive to_block should return first event
    result_2 = deploy_client.get_filter_events(
        contract_proxy.contract_address,
        to_block=block_number_event_1,
    )
    assert get_list_of_block_numbers(result_2) == [block_number_event_1]

    # this should include the second event
    result_3 = deploy_client.get_filter_events(
        contract_proxy.contract_address,
        to_block=block_number_event_2,
    )
    assert get_list_of_block_numbers(result_3) == block_number_events
Beispiel #16
0
    def approve(self, allowed_address: Address, allowance: TokenAmount):
        """ Aprove `allowed_address` to transfer up to `deposit` amount of token.

        Note:

            For channel deposit please use the channel proxy, since it does
            additional validations.
        """

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'allowed_address': pex(allowed_address),
            'allowance': allowance,
        }

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to approve will fail'
        gas_limit = self.proxy.estimate_gas(
            checking_block,
            'approve',
            to_checksum_address(allowed_address),
            allowance,
        )

        if gas_limit:
            error_prefix = 'Call to approve failed'
            log.debug('approve called', **log_details)
            transaction_hash = self.proxy.transact(
                'approve',
                safe_gas_limit(gas_limit),
                to_checksum_address(allowed_address),
                allowance,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='approve',
                transaction_executed=transaction_executed,
                required_gas=GAS_REQUIRED_FOR_APPROVE,
                block_identifier=block,
            )

            msg = self._check_why_approved_failed(allowance, block)
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('approve successful', **log_details)
Beispiel #17
0
    def unlock(
            self,
            channel_identifier: typing.ChannelID,
            partner: typing.Address,
            merkle_tree_leaves: typing.MerkleTreeLeaves,
    ):
        log_details = {
            'token_network': pex(self.address),
            'node': pex(self.node_address),
            'partner': pex(partner),
            'merkle_tree_leaves': merkle_tree_leaves,
        }

        if merkle_tree_leaves is None or not merkle_tree_leaves:
            log.info('skipping unlock, tree is empty', **log_details)
            return

        log.info('unlock called', **log_details)

        leaves_packed = b''.join(lock.encoded for lock in merkle_tree_leaves)

        gas_limit = self.proxy.estimate_gas(
            'unlock',
            channel_identifier,
            self.node_address,
            partner,
            leaves_packed,
        )
        gas_limit = safe_gas_limit(gas_limit, UNLOCK_TX_GAS_LIMIT)

        transaction_hash = self.proxy.transact(
            'unlock',
            gas_limit,
            channel_identifier,
            self.node_address,
            partner,
            leaves_packed,
        )

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client, transaction_hash)

        if receipt_or_none:
            channel_settled = self.channel_is_settled(
                participant1=self.node_address,
                participant2=partner,
                channel_identifier=channel_identifier,
            )

            if channel_settled is False:
                log.critical('unlock failed. Channel is not in a settled state', **log_details)
                raise RaidenUnrecoverableError(
                    'Channel is not in a settled state. An unlock cannot be made',
                )

            log.critical('unlock failed', **log_details)
            raise TransactionThrew('Unlock', receipt_or_none)

        log.info('unlock successful', **log_details)
Beispiel #18
0
    def approve(self, allowed_address: Address, allowance: TokenAmount):
        """ Aprove `allowed_address` to transfer up to `deposit` amount of token.

        Note:

            For channel deposit please use the channel proxy, since it does
            additional validations.
        """
        # Note that given_block_identifier is not used here as there
        # are no preconditions to check before sending the transaction

        log_details = {
            "node": pex(self.node_address),
            "contract": pex(self.address),
            "allowed_address": pex(allowed_address),
            "allowance": allowance,
        }

        checking_block = self.client.get_checking_block()
        error_prefix = "Call to approve will fail"
        gas_limit = self.proxy.estimate_gas(
            checking_block, "approve", to_checksum_address(allowed_address), allowance
        )

        if gas_limit:
            error_prefix = "Call to approve failed"
            log.debug("approve called", **log_details)
            transaction_hash = self.proxy.transact(
                "approve",
                safe_gas_limit(gas_limit),
                to_checksum_address(allowed_address),
                allowance,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client, transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none["blockNumber"]
            else:
                block = checking_block

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                address=self.node_address,
                transaction_name="approve",
                transaction_executed=transaction_executed,
                required_gas=GAS_REQUIRED_FOR_APPROVE,
                block_identifier=block,
            )

            msg = self._check_why_approved_failed(allowance, block)
            error_msg = f"{error_prefix}. {msg}"
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info("approve successful", **log_details)
Beispiel #19
0
    def approve(self, allowed_address: Address, allowance: TokenAmount):
        """ Aprove `allowed_address` to transfer up to `deposit` amount of token.

        Note:

            For channel deposit please use the channel proxy, since it does
            additional validations.
        """

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'allowed_address': pex(allowed_address),
            'allowance': allowance,
        }

        error_prefix = 'Call to approve will fail'
        gas_limit = self.proxy.estimate_gas(
            'pending',
            'approve',
            to_checksum_address(allowed_address),
            allowance,
        )

        if gas_limit:
            error_prefix = 'Call to approve failed'
            log.debug('approve called', **log_details)
            transaction_hash = self.proxy.transact(
                'approve',
                safe_gas_limit(gas_limit),
                to_checksum_address(allowed_address),
                allowance,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client, transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = 'pending'

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='approve',
                transaction_executed=transaction_executed,
                required_gas=GAS_REQUIRED_FOR_APPROVE,
                block_identifier=block,
            )

            msg = self._check_why_approved_failed(allowance, block)
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('approve successful', **log_details)
Beispiel #20
0
def test_transact_opcode_oog(deploy_client):
    """ The receipt status field of a transaction that did NOT throw is 0x0. """
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(to_checksum_address(address))) > 0

    # divide the estimate by 2 to run into out-of-gas
    startgas = safe_gas_limit(contract_proxy.estimate_gas('pending', 'loop', 1000)) // 2

    transaction = contract_proxy.transact('loop', startgas, 1000)
    deploy_client.poll(transaction)

    assert check_transaction_threw(deploy_client, transaction), 'must not be empty'
Beispiel #21
0
def test_transact_throws_opcode(deploy_client):
    """ The receipt status field of a transaction that threw is 0x0 """
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(to_checksum_address(address))) > 0

    # the gas estimation returns 0 here, so hardcode a value
    startgas = safe_gas_limit(22_000)

    transaction = contract_proxy.transact('fail', startgas)
    deploy_client.poll(transaction)

    assert check_transaction_threw(deploy_client, transaction), 'must not be empty'
    def _register_secret_batch(self, secrets):
        gas_limit = self.proxy.estimate_gas('registerSecretBatch', secrets)
        gas_limit = safe_gas_limit(
            gas_limit,
            len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH)
        transaction_hash = self.proxy.transact('registerSecretBatch',
                                               gas_limit, secrets)
        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)

        if receipt_or_none:
            raise TransactionThrew('registerSecretBatch', receipt_or_none)

        return transaction_hash
Beispiel #23
0
def test_endpointregistry_gas(endpoint_discovery_services):
    """ GAS_REQUIRED_FOR_ENDPOINT_REGISTER value must be equal to the gas required to call
    registerEndpoint.
    """
    contract_discovery = endpoint_discovery_services[0]
    discovery_proxy = contract_discovery.discovery_proxy
    endpoint = host_port_to_endpoint("127.0.0.1", 44444)

    transaction_hash = discovery_proxy.proxy.transact(
        "registerEndpoint", safe_gas_limit(GAS_REQUIRED_FOR_ENDPOINT_REGISTER),
        endpoint)
    discovery_proxy.client.poll(transaction_hash)

    receipt = discovery_proxy.client.get_transaction_receipt(transaction_hash)
    msg = "the transaction failed, check if it was because of the gas being too low"
    assert receipt["status"] != 0, msg
def test_transact_opcode_oog(deploy_client):
    """ The receipt status field of a transaction that did NOT throw is 0x0. """
    contract_proxy = deploy_rpc_test_contract(deploy_client)

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    # divide the estimate by 2 to run into out-of-gas
    startgas = safe_gas_limit(contract_proxy.estimate_gas('loop', 1000)) // 2

    transaction = contract_proxy.transact('loop', startgas, 1000)
    deploy_client.poll(transaction)

    assert check_transaction_threw(deploy_client,
                                   transaction), 'must not be empty'
Beispiel #25
0
    def add_token(self, token_address: typing.TokenAddress):
        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token')

        log_details = {
            'node': pex(self.node_address),
            'token_address': pex(token_address),
            'registry_address': pex(self.address),
        }
        log.debug('createERC20TokenNetwork called', **log_details)

        transaction_hash = self.proxy.transact(
            'createERC20TokenNetwork',
            safe_gas_limit(GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
            token_address,
        )

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            if self.get_token_network(token_address):
                msg = 'Token already registered'
                log.info(f'createERC20TokenNetwork failed, {msg}',
                         **log_details)
                raise RaidenRecoverableError(msg)

            log.critical(f'createERC20TokenNetwork failed', **log_details)
            raise TransactionThrew('createERC20TokenNetwork', receipt_or_none)

        token_network_address = self.get_token_network(token_address)

        if token_network_address is None:
            log.critical(
                'createERC20TokenNetwork failed and check_transaction_threw didnt detect it',
                **log_details,
            )

            raise RuntimeError('token_to_token_networks failed')

        log.info(
            'createERC20TokenNetwork successful',
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
def test_transact_opcode_oog(deploy_client: JSONRPCClient) -> None:
    """ The receipt status field of a transaction that did NOT throw is 0x0. """
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    # divide the estimate by 2 to run into out-of-gas
    check_block = deploy_client.get_checking_block()
    startgas = safe_gas_limit(
        contract_proxy.estimate_gas(check_block, "loop", 1000)) // 2

    transaction = contract_proxy.transact("loop", startgas, 1000)
    receipt = deploy_client.poll(transaction)

    assert check_transaction_threw(receipt=receipt), "must not be empty"
def test_endpointregistry_gas(endpoint_discovery_services):
    """ GAS_REQUIRED_FOR_ENDPOINT_REGISTER value must be equal to the gas required to call
    registerEndpoint.
    """
    contract_discovery = endpoint_discovery_services[0]
    discovery_proxy = contract_discovery.discovery_proxy
    endpoint = host_port_to_endpoint('127.0.0.1', 44444)

    transaction_hash = discovery_proxy.proxy.transact(
        'registerEndpoint',
        safe_gas_limit(GAS_REQUIRED_FOR_ENDPOINT_REGISTER),
        endpoint,
    )
    discovery_proxy.client.poll(transaction_hash)

    receipt = discovery_proxy.client.get_transaction_receipt(transaction_hash)
    msg = 'the transaction failed, check if it was because of the gas being too low'
    assert receipt['status'] != 0, msg
def test_transact_throws_opcode(deploy_client: JSONRPCClient) -> None:
    """ The receipt status field of a transaction that hit an assert or require is 0x0 """
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    # the gas estimation returns 0 here, so hardcode a value
    startgas = safe_gas_limit(22000)

    transaction = contract_proxy.transact("fail_assert", startgas)
    receipt = deploy_client.poll(transaction)

    assert check_transaction_threw(receipt=receipt), "must not be empty"

    transaction = contract_proxy.transact("fail_require", startgas)
    receipt = deploy_client.poll(transaction)

    assert check_transaction_threw(receipt=receipt), "must not be empty"
Beispiel #29
0
def test_duplicated_transaction_different_gas_price_raises(deploy_client: JSONRPCClient) -> None:
    """ If the same transaction is sent twice a JSON RPC error is raised. """
    gas_price = GasPrice(2000000000)
    deploy_client.web3.eth.setGasPriceStrategy(make_decreasing_gas_price_strategy(gas_price))
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(to_checksum_address(address))) > 0

    second_client = JSONRPCClient(web3=deploy_client.web3, privkey=deploy_client.privkey)

    second_proxy = second_client.new_contract_proxy(
        abi=contract_proxy.contract.abi, contract_address=contract_proxy.contract_address
    )

    check_block = deploy_client.get_checking_block()
    startgas = safe_gas_limit(contract_proxy.estimate_gas(check_block, "ret"))

    with pytest.raises(ReplacementTransactionUnderpriced):
        second_proxy.transact("ret", startgas)
        contract_proxy.transact("ret", startgas)
Beispiel #30
0
def test_resending_pending_transaction_raises(
        deploy_client: JSONRPCClient) -> None:
    """ If a pending transaction is re-sent the exception `EthereumNonceTooLow` is raised.

    This tests is only sufficient because of the companion test
    `test_resending_mined_transaction_raises` which shows that if the
    transaction has been mined a different exception is raised.
    """
    # Use a _fixed_ gas price strategy so that both transactions are identical.
    deploy_client.web3.eth.setGasPriceStrategy(
        make_fixed_gas_price_strategy(GasPrice(2000000000)))
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(
        to_checksum_address(address))) > 0

    # Create a new instance of the JSONRPCClient, this will store the current available nonce
    client_invalid_nonce = JSONRPCClient(web3=deploy_client.web3,
                                         privkey=deploy_client.privkey)

    check_block = deploy_client.get_checking_block()
    gas_estimate = contract_proxy.estimate_gas(check_block, "ret")
    assert gas_estimate, "Gas estimation should not fail here"
    startgas = safe_gas_limit(gas_estimate)

    # At this point `client_invalid_nonce` has a nonce that is `1` too low,
    # since a transaction was sent using `deploy_client` above and these two
    # instances share the same underlying private key.
    #
    # Note that it is assumed this runs fast enough so that the first transaction is not
    # mined before second is sent.
    contract_proxy.transact("ret", startgas)
    with pytest.raises(EthereumNonceTooLow):
        client_invalid_nonce.new_contract_proxy(
            abi=contract_proxy.contract.abi,
            contract_address=contract_proxy.contract_address).transact(
                "ret", startgas)
Beispiel #31
0
def test_duplicated_transaction_same_gas_price_raises(deploy_client: JSONRPCClient) -> None:
    """ If the same transaction is sent twice a JSON RPC error is raised. """
    gas_price = GasPrice(2000000000)
    gas_price_strategy = make_fixed_gas_price_strategy(gas_price)
    deploy_client.web3.eth.setGasPriceStrategy(gas_price_strategy)
    contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcTest")

    address = contract_proxy.contract_address
    assert len(deploy_client.web3.eth.getCode(to_checksum_address(address))) > 0

    second_client = JSONRPCClient(web3=deploy_client.web3, privkey=deploy_client.privkey)

    second_proxy = second_client.new_contract_proxy(
        abi=contract_proxy.contract.abi, contract_address=contract_proxy.contract_address
    )

    check_block = deploy_client.get_checking_block()
    gas_estimate = contract_proxy.estimate_gas(check_block, "ret")
    assert gas_estimate, "Gas estimation should not fail here"
    startgas = safe_gas_limit(gas_estimate)

    contract_proxy.transact("ret", startgas)
    with pytest.raises(TransactionAlreadyPending):
        second_proxy.transact("ret", startgas)
Beispiel #32
0
    def register_secret_batch(self, secrets: List[Secret]):
        """Register a batch of secrets. Check if they are already registered at
        the given block identifier."""
        secrets_to_register = list()
        secrethashes_to_register = list()
        secrethashes_not_sent = list()
        transaction_result = AsyncResult()
        wait_for = set()

        # secret registration has no preconditions:
        #
        # - The action does not depend on any state, it's always valid to call
        #   it.
        # - This action is always susceptible to race conditions.
        #
        # Therefore this proxy only needs to detect if the secret is already
        # registered, to avoid sending obviously unecessary transactions, and
        # it has to handle race conditions.

        with self._open_secret_transactions_lock:
            verification_block_hash = self.client.get_confirmed_blockhash()

            for secret in secrets:
                secrethash = sha3(secret)
                secrethash_hex = encode_hex(secrethash)

                # Do the local test on `open_secret_transactions` first, then
                # if necessary do an RPC call.
                #
                # The call to `is_secret_registered` has two conflicting
                # requirements:
                #
                # - Avoid sending duplicated transactions for the same lock
                # - Operating on a consistent/confirmed view of the blockchain
                #   (if a secret has been registered in a block that is not
                #   confirmed it doesn't count yet, an optimization would be to
                #   *not* send the transaction and wait for the confirmation)
                #
                # The code below respects the consistent blockchain view,
                # meaning that if this proxy method is called with an old
                # blockhash an unecessary transaction will be sent, and the
                # error will be treated as a race-condition.
                other_result = self.open_secret_transactions.get(secret)

                if other_result is not None:
                    wait_for.add(other_result)
                    secrethashes_not_sent.append(secrethash_hex)
                elif not self.is_secret_registered(secrethash,
                                                   verification_block_hash):
                    secrets_to_register.append(secret)
                    secrethashes_to_register.append(secrethash_hex)
                    self.open_secret_transactions[secret] = transaction_result

        # From here on the lock is not required. Context-switches will happen
        # for the gas estimation and the transaction, however the
        # synchronization data is limited to the open_secret_transactions
        log_details = {
            "node": pex(self.node_address),
            "contract": pex(self.address),
            "secrethashes": secrethashes_to_register,
            "secrethashes_not_sent": secrethashes_not_sent,
        }

        if not secrets_to_register:
            log.debug("registerSecretBatch skipped, waiting for transactions",
                      **log_details)

            gevent.joinall(wait_for, raise_error=True)

            log.info("registerSecretBatch successful", **log_details)
            return

        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "registerSecretBatch",
                                            secrets_to_register)
        receipt = None
        transaction_hash = None
        msg = None

        if gas_limit:
            gas_limit = safe_gas_limit(
                gas_limit,
                len(secrets_to_register) * GAS_REQUIRED_PER_SECRET_IN_BATCH)

            log.debug("registerSecretBatch called", **log_details)

            try:
                transaction_hash = self.proxy.transact("registerSecretBatch",
                                                       gas_limit,
                                                       secrets_to_register)
                self.client.poll(transaction_hash)
                receipt = self.client.get_transaction_receipt(transaction_hash)
            except Exception as e:  # pylint: disable=broad-except
                msg = f"Unexpected exception {e} at sending registerSecretBatch transaction."

        # Clear `open_secret_transactions` regardless of the transaction being
        # successfully executed or not.
        with self._open_secret_transactions_lock:
            for secret in secrets_to_register:
                self.open_secret_transactions.pop(secret)

        # As of version `0.4.0` of the contract has *no* asserts or requires.
        # Therefore the only reason for the transaction to fail is if there is
        # a bug.
        unrecoverable_error = (gas_limit is None or receipt is None
                               or receipt["status"] == RECEIPT_FAILURE_CODE)

        exception: Union[RaidenRecoverableError, RaidenUnrecoverableError]
        if unrecoverable_error:
            # If the transaction was sent it must not fail. If this happened
            # some of our assumptions is broken therefore the error is
            # unrecoverable
            if receipt is not None:
                if receipt["gasUsed"] == gas_limit:
                    # The transaction failed and all gas was used. This can
                    # happen because of:
                    #
                    # - A compiler bug if an invalid opcode was executed.
                    # - A configuration bug if an assert was executed,
                    # because version 0.4.0 of the secret registry does not have an
                    # assert.
                    # - An ethereum client bug if the gas_limit was
                    # underestimated.
                    #
                    # Safety cannot be guaranteed under any of these cases,
                    # this error is unrecoverable.
                    error = (
                        "Secret registration failed because of a bug in either "
                        "the solidity compiler, the running ethereum client, or "
                        "a configuration error in Raiden.")
                else:
                    # The transaction failed and *not* all gas was used. This
                    # can happen because of:
                    #
                    # - A compiler bug if a revert was introduced.
                    # - A configuration bug, because for 0.4.0 the secret
                    # registry does not have a revert.
                    error = (
                        "Secret registration failed because of a configuration "
                        "bug or compiler bug. Please double check the secret "
                        "smart contract is at version 0.4.0, if it is then a "
                        "compiler bug was hit.")

                log.critical(error, **log_details)
                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # If gas_limit is set and there is no receipt then an exception was
            # raised while sending the transaction. This should only happen if
            # the account is being used concurrently, which is not supported.
            # This can happen because:
            #
            # - The nonce of the transaction was already used
            # - The nonce was reused *and* the account didn't have enough ether
            # to pay for the gas
            #
            # Safety cannot be guaranteed under any of these cases, this error
            # is unrecoverable. *Note*: This assumes the ethereum client
            # takes into account the current transactions in the pool.
            if gas_limit:
                assert msg, "Unexpected control flow, an exception should have been raised."
                error = (
                    f"Sending the the transaction for registerSecretBatch failed with: `{msg}`. "
                    f"This happens if the same ethereum account is being used by more than one "
                    f"program which is not supported.")

                log.critical(error, **log_details)
                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # gas_limit can fail because:
            #
            # - The Ethereum client detected the transaction could not
            # successfully execute, this happens if an assert/revert is hit.
            # - The account is lacking funds to pay for the gas.
            #
            # Either of these is a bug. The contract does not use
            # assert/revert, and the account should always be funded
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="registerSecretBatch",
                transaction_executed=True,
                required_gas=gas_limit,
                block_identifier=checking_block,
            )
            error = "Call to registerSecretBatch couldn't be done"

            log.critical(error, **log_details)
            exception = RaidenRecoverableError(error)
            transaction_result.set_exception(exception)
            raise exception

        # The local **MUST** transaction_result be set before waiting for the
        # other results, otherwise we have a dead-lock
        transaction_result.set(transaction_hash)

        if wait_for:
            log.info("registerSecretBatch waiting for pending", **log_details)
            gevent.joinall(wait_for, raise_error=True)

        log.info("registerSecretBatch successful", **log_details)
Beispiel #33
0
    def _register_secret_batch(
        self,
        secrets_to_register: List[Secret],
        transaction_result: AsyncResult,
        log_details: Dict[Any, Any],
    ) -> None:
        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "registerSecretBatch",
                                            secrets_to_register)
        receipt = None
        transaction_hash = None
        msg = None

        if gas_limit:
            gas_limit = safe_gas_limit(
                gas_limit,
                len(secrets_to_register) * GAS_REQUIRED_PER_SECRET_IN_BATCH)
            log_details["gas_limit"] = gas_limit

            try:
                transaction_hash = self.proxy.transact("registerSecretBatch",
                                                       gas_limit,
                                                       secrets_to_register)
                receipt = self.client.poll(transaction_hash)
            except Exception as e:  # pylint: disable=broad-except
                msg = f"Unexpected exception {e} at sending registerSecretBatch transaction."

        # Clear `open_secret_transactions` regardless of the transaction being
        # successfully executed or not.
        with self._open_secret_transactions_lock:
            for secret in secrets_to_register:
                self.open_secret_transactions.pop(secret)

        # As of version `0.4.0` of the contract has *no* asserts or requires.
        # Therefore the only reason for the transaction to fail is if there is
        # a bug.
        unrecoverable_error = (gas_limit is None or receipt is None
                               or receipt["status"] == RECEIPT_FAILURE_CODE)

        exception: Union[RaidenRecoverableError, RaidenUnrecoverableError]
        if unrecoverable_error:
            # If the transaction was sent it must not fail. If this happened
            # some of our assumptions is broken therefore the error is
            # unrecoverable
            if receipt is not None:
                if receipt["gasUsed"] == gas_limit:
                    # The transaction failed and all gas was used. This can
                    # happen because of:
                    #
                    # - A compiler bug if an invalid opcode was executed.
                    # - A configuration bug if an assert was executed,
                    # because version 0.4.0 of the secret registry does not have an
                    # assert.
                    # - An ethereum client bug if the gas_limit was
                    # underestimated.
                    #
                    # Safety cannot be guaranteed under any of these cases,
                    # this error is unrecoverable.
                    error = (
                        "Secret registration failed because of a bug in either "
                        "the solidity compiler, the running ethereum client, or "
                        "a configuration error in Raiden.")
                else:
                    # The transaction failed and *not* all gas was used. This
                    # can happen because of:
                    #
                    # - A compiler bug if a revert was introduced.
                    # - A configuration bug, because for 0.4.0 the secret
                    # registry does not have a revert.
                    error = (
                        "Secret registration failed because of a configuration "
                        "bug or compiler bug. Please double check the secret "
                        "smart contract is at version 0.4.0, if it is then a "
                        "compiler bug was hit.")

                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # If gas_limit is set and there is no receipt then an exception was
            # raised while sending the transaction. This should only happen if
            # the account is being used concurrently, which is not supported.
            # This can happen because:
            #
            # - The nonce of the transaction was already used
            # - The nonce was reused *and* the account didn't have enough ether
            # to pay for the gas
            #
            # Safety cannot be guaranteed under any of these cases, this error
            # is unrecoverable. *Note*: This assumes the ethereum client
            # takes into account the current transactions in the pool.
            if gas_limit:
                assert msg, "Unexpected control flow, an exception should have been raised."
                error = (
                    f"Sending the the transaction for registerSecretBatch "
                    f"failed with: `{msg}`.  This happens if the same ethereum "
                    f"account is being used by more than one program which is not "
                    f"supported.")

                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # gas_limit can fail because:
            #
            # - The Ethereum client detected the transaction could not
            # successfully execute, this happens if an assert/revert is hit.
            # - The account is lacking funds to pay for the gas.
            #
            # Either of these is a bug. The contract does not use
            # assert/revert, and the account should always be funded
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="registerSecretBatch",
                transaction_executed=True,
                required_gas=gas_limit,
                block_identifier=checking_block,
            )
            error = "Call to registerSecretBatch couldn't be done"

            exception = RaidenRecoverableError(error)
            transaction_result.set_exception(exception)
            raise exception

        # The local **MUST** transaction_result be set before waiting for the
        # other results, otherwise we have a dead-lock
        transaction_result.set(transaction_hash)
Beispiel #34
0
    def _add_token(
        self,
        token_address: TokenAddress,
        additional_arguments: Dict,
    ) -> Address:
        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token')

        token_proxy = Token(
            jsonrpc_client=self.client,
            token_address=token_address,
            contract_manager=self.contract_manager,
        )

        if token_proxy.total_supply() == '':
            raise InvalidToken(
                'Given token address does not follow the ERC20 standard (missing totalSupply()',
            )

        log_details = {
            'node': pex(self.node_address),
            'token_address': pex(token_address),
            'registry_address': pex(self.address),
        }
        log.debug('createERC20TokenNetwork called', **log_details)

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to createERC20TokenNetwork will fail'

        kwarguments = {'_token_address': token_address}
        kwarguments.update(additional_arguments)
        gas_limit = self.proxy.estimate_gas(
            checking_block,
            'createERC20TokenNetwork',
            **kwarguments,
        )

        if gas_limit:
            error_prefix = 'Call to createERC20TokenNetwork failed'
            transaction_hash = self.proxy.transact(
                'createERC20TokenNetwork',
                safe_gas_limit(gas_limit,
                               GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
                **kwarguments,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            error_type = RaidenUnrecoverableError
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            required_gas = gas_limit if gas_limit else GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='createERC20TokenNetwork',
                transaction_executed=transaction_executed,
                required_gas=required_gas,
                block_identifier=block,
            )

            msg = ''
            if self.get_token_network(token_address, block):
                msg = 'Token already registered'
                error_type = RaidenRecoverableError

            error_msg = f'{error_prefix}. {msg}'
            if error_type == RaidenRecoverableError:
                log.warning(error_msg, **log_details)
            else:
                log.critical(error_msg, **log_details)
            raise error_type(error_msg)

        token_network_address = self.get_token_network(token_address, 'latest')
        if token_network_address is None:
            msg = 'createERC20TokenNetwork succeeded but token network address is Null'
            log.critical(msg, **log_details)
            raise RuntimeError(msg)

        log.info(
            'createERC20TokenNetwork successful',
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
    def add_token(self, token_address: TokenAddress):
        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token')

        log_details = {
            'node': pex(self.node_address),
            'token_address': pex(token_address),
            'registry_address': pex(self.address),
        }
        log.debug('createERC20TokenNetwork called', **log_details)

        error_prefix = 'Call to createERC20TokenNetwork will fail'
        gas_limit = self.proxy.estimate_gas(
            'pending',
            'createERC20TokenNetwork',
            token_address,
        )

        if gas_limit:
            error_prefix = 'Call to createERC20TokenNetwork failed'
            transaction_hash = self.proxy.transact(
                'createERC20TokenNetwork',
                safe_gas_limit(gas_limit, GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
                token_address,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client, transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            error_type = RaidenUnrecoverableError
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = 'pending'

            required_gas = gas_limit if gas_limit else GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='createERC20TokenNetwork',
                transaction_executed=transaction_executed,
                required_gas=required_gas,
                block_identifier=block,
            )

            msg = ''
            if self.get_token_network(token_address, block):
                msg = 'Token already registered'
                error_type = RaidenRecoverableError

            error_msg = f'{error_prefix}. {msg}'
            if error_type == RaidenRecoverableError:
                log.warning(error_msg, **log_details)
            else:
                log.critical(error_msg, **log_details)
            raise error_type(error_msg)

        token_network_address = self.get_token_network(token_address, 'latest')
        if token_network_address is None:
            msg = 'createERC20TokenNetwork succeeded but token network address is Null'
            log.critical(msg, **log_details)
            raise RuntimeError(msg)

        log.info(
            'createERC20TokenNetwork successful',
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
    def settle(
        self,
        channel_identifier: typing.ChannelID,
        transferred_amount: int,
        locked_amount: int,
        locksroot: typing.Locksroot,
        partner: typing.Address,
        partner_transferred_amount: int,
        partner_locked_amount: int,
        partner_locksroot: typing.Locksroot,
    ):
        """ Settle the channel.

        Raises:
            ChannelBusyError: If the channel is busy with another operation
        """
        log_details = {
            'channel_identifier': channel_identifier,
            'token_network': pex(self.address),
            'node': pex(self.node_address),
            'partner': pex(partner),
            'transferred_amount': transferred_amount,
            'locked_amount': locked_amount,
            'locksroot': encode_hex(locksroot),
            'partner_transferred_amount': partner_transferred_amount,
            'partner_locked_amount': partner_locked_amount,
            'partner_locksroot': encode_hex(partner_locksroot),
        }
        log.debug('settle called', **log_details)

        self._check_for_outdated_channel(
            self.node_address,
            partner,
            channel_identifier,
        )

        with self.channel_operations_lock[partner]:
            our_maximum = transferred_amount + locked_amount
            partner_maximum = partner_transferred_amount + partner_locked_amount

            # The second participant transferred + locked amount must be higher
            our_bp_is_larger = our_maximum > partner_maximum

            if our_bp_is_larger:
                gas_limit = self.proxy.estimate_gas(
                    'settleChannel',
                    channel_identifier,
                    partner,
                    partner_transferred_amount,
                    partner_locked_amount,
                    partner_locksroot,
                    self.node_address,
                    transferred_amount,
                    locked_amount,
                    locksroot,
                )
                gas_limit = safe_gas_limit(gas_limit,
                                           GAS_REQUIRED_FOR_SETTLE_CHANNEL)

                transaction_hash = self.proxy.transact(
                    'settleChannel',
                    gas_limit,
                    channel_identifier,
                    partner,
                    partner_transferred_amount,
                    partner_locked_amount,
                    partner_locksroot,
                    self.node_address,
                    transferred_amount,
                    locked_amount,
                    locksroot,
                )
            else:
                gas_limit = self.proxy.estimate_gas(
                    'settleChannel',
                    channel_identifier,
                    self.node_address,
                    transferred_amount,
                    locked_amount,
                    locksroot,
                    partner,
                    partner_transferred_amount,
                    partner_locked_amount,
                    partner_locksroot,
                )
                gas_limit = safe_gas_limit(gas_limit,
                                           GAS_REQUIRED_FOR_SETTLE_CHANNEL)

                transaction_hash = self.proxy.transact(
                    'settleChannel',
                    gas_limit,
                    channel_identifier,
                    self.node_address,
                    transferred_amount,
                    locked_amount,
                    locksroot,
                    partner,
                    partner_transferred_amount,
                    partner_locked_amount,
                    partner_locksroot,
                )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)
            if receipt_or_none:
                log.critical('settle failed', **log_details)
                self._check_channel_state_for_settle(
                    self.node_address,
                    partner,
                    channel_identifier,
                )
                raise TransactionThrew('Settle', receipt_or_none)

            log.info('settle successful', **log_details)
    def _add_token(self, token_address: TokenAddress,
                   additional_arguments: Dict) -> Address:
        if not is_binary_address(token_address):
            raise InvalidAddress("Expected binary address format for token")

        token_proxy = Token(
            jsonrpc_client=self.client,
            token_address=token_address,
            contract_manager=self.contract_manager,
        )

        if token_proxy.total_supply() == "":
            raise InvalidToken(
                "Given token address does not follow the ERC20 standard (missing totalSupply()"
            )

        log_details = {
            "node": pex(self.node_address),
            "token_address": pex(token_address),
            "registry_address": pex(self.address),
        }
        log.debug("createERC20TokenNetwork called", **log_details)

        checking_block = self.client.get_checking_block()
        error_prefix = "Call to createERC20TokenNetwork will fail"

        kwarguments = {"_token_address": token_address}
        kwarguments.update(additional_arguments)
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "createERC20TokenNetwork",
                                            **kwarguments)

        if gas_limit:
            error_prefix = "Call to createERC20TokenNetwork failed"
            transaction_hash = self.proxy.transact(
                "createERC20TokenNetwork",
                safe_gas_limit(gas_limit,
                               GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
                **kwarguments,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none["blockNumber"]
            else:
                block = checking_block

            required_gas = gas_limit if gas_limit else GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="createERC20TokenNetwork",
                transaction_executed=transaction_executed,
                required_gas=required_gas,
                block_identifier=block,
            )

            if self.get_token_network(token_address, block):
                error_msg = f"{error_prefix}. Token already registered"
                log.warning(error_msg, **log_details)
                raise RaidenRecoverableError(error_msg)

            error_msg = f"{error_prefix}"
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        token_network_address = self.get_token_network(token_address, "latest")
        if token_network_address is None:
            msg = "createERC20TokenNetwork succeeded but token network address is Null"
            log.critical(msg, **log_details)
            raise RuntimeError(msg)

        log.info(
            "createERC20TokenNetwork successful",
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
Beispiel #38
0
    def _deposit(
        self,
        beneficiary: Address,
        token: Token,
        total_deposit: TokenAmount,
        amount_to_deposit: TokenAmount,
        log_details: Dict[str, Any],
    ) -> None:
        token.approve(allowed_address=Address(self.address),
                      allowance=amount_to_deposit)

        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(checking_block, "deposit",
                                            to_checksum_address(beneficiary),
                                            total_deposit)

        if not gas_limit:
            failed_at = self.proxy.rpc_client.get_block("latest")
            failed_at_blocknumber = failed_at["number"]

            self.proxy.rpc_client.check_for_insufficient_eth(
                transaction_name="deposit",
                transaction_executed=False,
                required_gas=self.gas_measurements["UserDeposit.deposit"],
                block_identifier=failed_at_blocknumber,
            )

            latest_deposit = self.get_total_deposit(
                address=self.node_address,
                block_identifier=failed_at_blocknumber)
            amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

            allowance = token.allowance(
                owner=self.node_address,
                spender=Address(self.address),
                block_identifier=failed_at_blocknumber,
            )
            whole_balance = self.whole_balance(
                block_identifier=failed_at_blocknumber)
            whole_balance_limit = self.whole_balance_limit(
                block_identifier=failed_at_blocknumber)

            if allowance < amount_to_deposit:
                msg = (
                    "The allowance is insufficient. Check concurrent deposits "
                    "for the same user deposit but different proxies.")
                raise RaidenRecoverableError(msg)

            if token.balance_of(self.node_address,
                                failed_at_blocknumber) < amount_to_deposit:
                msg = "The address doesnt have enough tokens"
                raise RaidenRecoverableError(msg)

            if latest_deposit < total_deposit:
                msg = "Deposit amount did not increase after deposit transaction"
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > UINT256_MAX:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"The new deposit of {amount_to_deposit} would lead to an overflow."
                )
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > whole_balance_limit:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"With the new deposit of {amount_to_deposit}, the deposit "
                    f"limit of {whole_balance_limit} would be exceeded.")
                raise RaidenRecoverableError(msg)

            raise RaidenRecoverableError("Deposit failed of unknown reason")

        else:
            gas_limit = safe_gas_limit(gas_limit)
            log_details["gas_limit"] = gas_limit

            transaction_hash = self.proxy.transact(
                "deposit", gas_limit, to_checksum_address(beneficiary),
                total_deposit)

            receipt = self.client.poll(transaction_hash)
            failed_receipt = check_transaction_threw(receipt=receipt)

            if failed_receipt:
                failed_at_blocknumber = failed_receipt["blockNumber"]

                latest_deposit = self.get_total_deposit(
                    address=self.node_address,
                    block_identifier=failed_at_blocknumber)
                amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

                allowance = token.allowance(
                    owner=self.node_address,
                    spender=Address(self.address),
                    block_identifier=failed_at_blocknumber,
                )

                whole_balance = self.whole_balance(
                    block_identifier=failed_at_blocknumber)
                whole_balance_limit = self.whole_balance_limit(
                    block_identifier=failed_at_blocknumber)

                if latest_deposit >= total_deposit:
                    msg = "Deposit amount already increased after another transaction"
                    raise RaidenRecoverableError(msg)

                if allowance < amount_to_deposit:
                    msg = (
                        "The allowance is insufficient. Check concurrent deposits "
                        "for the same token network but different proxies.")
                    raise RaidenRecoverableError(msg)

                # Because we acquired the lock for the token, and the gas estimation succeeded,
                # We know that the account had enough balance for the deposit transaction.
                if token.balance_of(self.node_address,
                                    failed_at_blocknumber) < amount_to_deposit:
                    msg = (
                        f"Transaction failed and balance decreased unexpectedly. "
                        f"This could be a bug in Raiden or a mallicious "
                        f"ERC20 Token.")
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > UINT256_MAX:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"The new deposit of {amount_to_deposit} caused an overflow."
                    )
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > whole_balance_limit:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"With the new deposit of {amount_to_deposit}, the deposit "
                        f"limit of {whole_balance_limit} was exceeded.")
                    raise RaidenRecoverableError(msg)

                if latest_deposit < total_deposit:
                    msg = "Deposit amount did not increase after deposit transaction"
                    raise RaidenRecoverableError(msg)

                raise RaidenRecoverableError(
                    "Deposit failed of unknown reason")
Beispiel #39
0
    def register_secret_batch(self, secrets: List[Secret]):
        secrets_to_register = list()
        secrethashes_to_register = list()
        secrethashes_not_sent = list()
        secret_registry_transaction = AsyncResult()

        for secret in secrets:
            secrethash = sha3(secret)
            secrethash_hex = encode_hex(secrethash)

            is_register_needed = (
                not self.check_registered(secrethash) and
                secret not in self.open_secret_transactions
            )
            if is_register_needed:
                secrets_to_register.append(secret)
                secrethashes_to_register.append(secrethash_hex)
                self.open_secret_transactions[secret] = secret_registry_transaction
            else:
                secrethashes_not_sent.append(secrethash_hex)

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'secrethashes': secrethashes_to_register,
            'secrethashes_not_sent': secrethashes_not_sent,
        }

        if not secrets_to_register:
            log.debug('registerSecretBatch skipped', **log_details)
            return

        error_prefix = 'Call to registerSecretBatch will fail'
        gas_limit = self.proxy.estimate_gas('pending', 'registerSecretBatch', secrets)
        if gas_limit:
            error_prefix = 'Call to registerSecretBatch failed'
            try:
                gas_limit = safe_gas_limit(
                    gas_limit,
                    len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                )
                transaction_hash = self.proxy.transact('registerSecretBatch', gas_limit, secrets)
                self.client.poll(transaction_hash)
                receipt_or_none = check_transaction_threw(self.client, transaction_hash)
            except Exception as e:
                secret_registry_transaction.set_exception(e)
                msg = 'Unexpected exception at sending registerSecretBatch transaction'
            else:
                secret_registry_transaction.set(transaction_hash)
            finally:
                for secret in secrets_to_register:
                    self.open_secret_transactions.pop(secret, None)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = 'pending'

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='registerSecretBatch',
                transaction_executed=transaction_executed,
                required_gas=len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                block_identifier=block,
            )
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('registerSecretBatch successful', **log_details)
Beispiel #40
0
    def register_secret_batch(
        self,
        secrets: List[Secret],
        given_block_identifier: BlockSpecification,
    ):
        secrets_to_register = list()
        secrethashes_to_register = list()
        secrethashes_not_sent = list()
        secret_registry_transaction = AsyncResult()

        for secret in secrets:
            secrethash = sha3(secret)
            secrethash_hex = encode_hex(secrethash)

            is_register_needed = (
                not self.check_registered(secrethash, given_block_identifier)
                and secret not in self.open_secret_transactions)
            if is_register_needed:
                secrets_to_register.append(secret)
                secrethashes_to_register.append(secrethash_hex)
                self.open_secret_transactions[
                    secret] = secret_registry_transaction
            else:
                secrethashes_not_sent.append(secrethash_hex)

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'secrethashes': secrethashes_to_register,
            'secrethashes_not_sent': secrethashes_not_sent,
        }

        if not secrets_to_register:
            log.debug('registerSecretBatch skipped', **log_details)
            return

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to registerSecretBatch will fail'
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            'registerSecretBatch', secrets)
        if gas_limit:
            error_prefix = 'Call to registerSecretBatch failed'
            try:
                gas_limit = safe_gas_limit(
                    gas_limit,
                    len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                )
                transaction_hash = self.proxy.transact('registerSecretBatch',
                                                       gas_limit, secrets)
                self.client.poll(transaction_hash)
                receipt_or_none = check_transaction_threw(
                    self.client, transaction_hash)
            except Exception as e:
                secret_registry_transaction.set_exception(e)
                msg = 'Unexpected exception at sending registerSecretBatch transaction'
            else:
                secret_registry_transaction.set(transaction_hash)
            finally:
                for secret in secrets_to_register:
                    self.open_secret_transactions.pop(secret, None)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='registerSecretBatch',
                transaction_executed=transaction_executed,
                required_gas=len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                block_identifier=block,
            )
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('registerSecretBatch successful', **log_details)