예제 #1
0
class Channel:
    # pylint: disable=too-many-instance-attributes
    token_network_address: TokenNetworkAddress = field(
        metadata={"marshmallow_field": ChecksumAddress(required=True)})
    channel_id: ChannelID
    participant1: Address = field(
        metadata={"marshmallow_field": ChecksumAddress(required=True)})
    participant2: Address = field(
        metadata={"marshmallow_field": ChecksumAddress(required=True)})
    fee_schedule1: FeeSchedule = field(default_factory=FeeSchedule)
    fee_schedule2: FeeSchedule = field(default_factory=FeeSchedule)

    # Set by PFSCapacityUpdate
    capacity1: TokenAmount = TokenAmount(0)
    capacity2: TokenAmount = TokenAmount(0)
    update_nonce1: Nonce = Nonce(0)
    update_nonce2: Nonce = Nonce(0)
    reveal_timeout1: BlockTimeout = DEFAULT_REVEAL_TIMEOUT
    reveal_timeout2: BlockTimeout = DEFAULT_REVEAL_TIMEOUT

    Schema: ClassVar[Type[marshmallow.Schema]]

    @property
    def views(self) -> Tuple["ChannelView", "ChannelView"]:
        return ChannelView(channel=self), ChannelView(channel=self,
                                                      reverse=True)
예제 #2
0
def calc_claim_cost_rdn(web3: Web3, rdn_per_eth: float) -> TokenAmount:
    web3.eth.setGasPriceStrategy(rpc_gas_price_strategy)
    claim_cost_gas = gas_measurements()["OneToN.claim"]

    gas_price = web3.eth.generateGasPrice()
    assert gas_price is not None, "Could not generate gas price"

    claim_cost_eth = claim_cost_gas * gas_price * GAS_COST_SAFETY_MARGIN
    return TokenAmount(int(claim_cost_eth / rdn_per_eth))
예제 #3
0
def maybe_create_token_network(
        token_network_proxy: TokenNetworkRegistry,
        token_proxy: CustomToken) -> TokenNetworkAddress:
    """ Make sure the token is registered with the node's network registry. """
    block_identifier = token_network_proxy.rpc_client.get_confirmed_blockhash()
    token_address = token_proxy.address

    token_network_address = token_network_proxy.get_token_network(
        token_address=token_address, block_identifier=block_identifier)

    if token_network_address is None:
        _, new_token_network_address = token_network_proxy.add_token(
            token_address=token_address,
            channel_participant_deposit_limit=TokenAmount(UINT256_MAX),
            token_network_deposit_limit=TokenAmount(UINT256_MAX),
            given_block_identifier=block_identifier,
        )
        return new_token_network_address
    else:
        return token_network_address
예제 #4
0
    def get(
        channel_identifier: ChannelID,
        participant: HexAddress,
        transferred_amount: TokenAmount = TokenAmount(0),  # noqa
        locked_amount: TokenAmount = TokenAmount(0),  # noqa
        nonce: Nonce = Nonce(0),  # noqa
        locksroot: Optional[Locksroot] = None,
        additional_hash: Optional[AdditionalHash] = None,
        v: int = 27,
        signer: Optional[HexAddress] = None,
        other_token_network: Optional[Contract] = None,
    ) -> OnchainBalanceProof:
        _token_network = other_token_network or token_network
        private_key = get_private_key(signer or participant)
        locksroot = locksroot or LOCKSROOT_OF_NO_LOCKS
        additional_hash = additional_hash or AdditionalHash(b"\x00" * 32)

        balance_hash = hash_balance_data(transferred_amount, locked_amount,
                                         locksroot)

        signature = sign_balance_proof(
            private_key,
            _token_network.address,
            _token_network.functions.chain_id().call(),
            channel_identifier,
            MessageTypeId.BALANCE_PROOF,
            balance_hash,
            nonce,
            additional_hash,
            v,
        )
        # The keys of the dictionary correspond to the parameters of
        # create_balance_proof_countersignature.
        return OnchainBalanceProof(
            balance_hash=balance_hash,
            nonce=nonce,
            additional_hash=additional_hash,
            original_signature=signature,
        )
예제 #5
0
 def get_capacity_updates(
     self,
     updating_participant: Address,
     token_network_address: TokenNetworkAddress,
     channel_id: int,
 ) -> Tuple[TokenAmount, TokenAmount]:
     with self._cursor() as cursor:
         capacity_list = cursor.execute(
             """
             SELECT updating_capacity, other_capacity
             FROM capacity_update WHERE updating_participant=?
             AND token_network_address=?
             AND channel_id=?
         """,
             [
                 to_checksum_address(updating_participant),
                 to_checksum_address(token_network_address),
                 hex256(channel_id),
             ],
         )
         try:
             return next(capacity_list)
         except StopIteration:
             return TokenAmount(0), TokenAmount(0)
예제 #6
0
def test_claim_by_unregistered_service(
    one_to_n_contract: Contract,
    deposit_to_udc: Callable,
    get_accounts: Callable,
    get_private_key: Callable,
    web3: Web3,
) -> None:
    """OneToN contract should not work for an unregistered service provider."""
    (A, B) = get_accounts(2)
    deposit_to_udc(A, 30)

    amount = TokenAmount(10)
    expiration = BlockExpiration(web3.eth.block_number + 2)
    chain_id = web3.eth.chain_id

    signature = sign_one_to_n_iou(
        get_private_key(A),
        sender=A,
        receiver=B,
        amount=amount,
        expiration_block=expiration,
        one_to_n_address=one_to_n_contract.address,
        chain_id=ChainID(chain_id),
    )

    # Doesn't work because B is not registered
    with pytest.raises(TransactionFailed, match="receiver not registered"):
        call_and_transact(
            one_to_n_contract.functions.claim(
                sender=A,
                receiver=B,
                amount=amount,
                expiration_block=expiration,
                one_to_n_address=one_to_n_contract.address,
                signature=signature,
            ),
            {"from": A},
        )
예제 #7
0
def print_gas_one_to_n(
    one_to_n_contract: Contract,
    deposit_to_udc: Callable,
    print_gas: Callable,
    make_iou: Callable,
    web3: Web3,
    get_private_key: Callable,
    create_service_account: Callable,
    create_account: Callable,
) -> None:
    """Abusing pytest to print gas cost of OneToN functions"""
    A = create_account()
    B = create_service_account()
    deposit_to_udc(A, 30)

    # happy case
    chain_id = web3.eth.chain_id
    amount = TokenAmount(10)
    expiration = BlockExpiration(web3.eth.block_number + 2)
    signature = sign_one_to_n_iou(
        get_private_key(A),
        sender=A,
        receiver=B,
        amount=amount,
        expiration_block=expiration,
        one_to_n_address=one_to_n_contract.address,
        chain_id=ChainID(chain_id),
    )
    txn_hash = call_and_transact(
        one_to_n_contract.functions.claim(A, B, amount, expiration,
                                          one_to_n_contract.address,
                                          signature),
        {"from": A},
    )

    print_gas(txn_hash, CONTRACT_ONE_TO_N + ".claim")

    # bulk claims gas prices
    def concat_iou_data(ious: List[Dict], key: str) -> List:
        return [iou[key] for iou in ious]

    def concat_iou_signatures(ious: List[Dict]) -> bytes:
        result = b""
        for iou in ious:
            result += iou["signature"]

        return result

    for num_ious in (1, 6):
        receivers = [create_service_account() for i in range(num_ious)]
        ious = [make_iou(A, r) for r in receivers]

        txn_hash = call_and_transact(
            one_to_n_contract.functions.bulkClaim(
                concat_iou_data(ious, "sender"),
                concat_iou_data(ious, "receiver"),
                concat_iou_data(ious, "amount"),
                concat_iou_data(ious, "expiration_block"),
                one_to_n_contract.address,
                concat_iou_signatures(ious),
            ),
            {"from": A},
        )
        print_gas(txn_hash, CONTRACT_ONE_TO_N + f".bulkClaim {num_ious} ious")
예제 #8
0
def print_gas_monitoring_service(
    token_network: Contract,
    monitoring_service_external: Contract,
    get_accounts: Callable,
    create_channel: Callable,
    create_balance_proof: Callable,
    create_balance_proof_countersignature: Callable,
    service_registry: Contract,
    custom_token: Contract,
    deposit_to_udc: Callable,
    print_gas: Callable,
    get_private_key: Callable,
    create_service_account: Callable,
    web3: Web3,
) -> None:
    """Abusing pytest to print gas cost of MonitoringService functions"""
    # setup: two parties + MS
    (A, MS) = get_accounts(2)
    B = create_service_account()
    reward_amount = TokenAmount(10)
    deposit_to_udc(B, reward_amount)

    # register MS in the ServiceRegistry contract
    call_and_transact(custom_token.functions.mint(SERVICE_DEPOSIT * 2),
                      {"from": MS})
    call_and_transact(
        custom_token.functions.approve(service_registry.address,
                                       SERVICE_DEPOSIT),
        {"from": MS},
    )
    call_and_transact(service_registry.functions.deposit(SERVICE_DEPOSIT),
                      {"from": MS})

    # open a channel (c1, c2)
    channel_identifier = create_channel(A, B)[0]

    # create balance and reward proofs
    balance_proof_A = create_balance_proof(channel_identifier,
                                           B,
                                           transferred_amount=10,
                                           nonce=1)
    closing_sig_A = create_balance_proof_countersignature(
        participant=A,
        channel_identifier=channel_identifier,
        msg_type=MessageTypeId.BALANCE_PROOF,
        **balance_proof_A._asdict(),
    )
    balance_proof_B = create_balance_proof(channel_identifier,
                                           A,
                                           transferred_amount=20,
                                           nonce=2)
    non_closing_signature_B = create_balance_proof_countersignature(
        participant=B,
        channel_identifier=channel_identifier,
        msg_type=MessageTypeId.BALANCE_PROOF_UPDATE,
        **balance_proof_B._asdict(),
    )
    reward_proof_signature = sign_reward_proof(
        privatekey=get_private_key(B),
        monitoring_service_contract_address=monitoring_service_external.
        address,
        chain_id=token_network.functions.chain_id().call(),
        token_network_address=token_network.address,
        non_closing_participant=B,
        reward_amount=reward_amount,
        non_closing_signature=non_closing_signature_B,
    )

    # c1 closes channel
    call_and_transact(
        token_network.functions.closeChannel(
            channel_identifier, B, A,
            *balance_proof_A._asdict().values(), closing_sig_A),
        {"from": A},
    )
    mine_blocks(web3, 4)

    # MS calls `MSC::monitor()` using c1's BP and reward proof
    txn_hash = call_and_transact(
        monitoring_service_external.functions.monitor(
            A,
            B,
            balance_proof_B.balance_hash,
            balance_proof_B.nonce,
            balance_proof_B.additional_hash,
            balance_proof_B.original_signature,
            non_closing_signature_B,  # non-closing signature
            reward_amount,
            token_network.address,  # token network address
            reward_proof_signature,
        ),
        {"from": MS},
    )
    print_gas(txn_hash, CONTRACT_MONITORING_SERVICE + ".monitor")

    mine_blocks(web3, 1)

    # MS claims the reward
    txn_hash = call_and_transact(
        monitoring_service_external.functions.claimReward(
            channel_identifier, token_network.address, A, B),
        {"from": MS},
    )
    print_gas(txn_hash, CONTRACT_MONITORING_SERVICE + ".claimReward")
예제 #9
0
    CONTRACT_MONITORING_SERVICE,
    LOCKSROOT_OF_NO_LOCKS,
    MessageTypeId,
    MonitoringServiceEvent,
)
from raiden_contracts.tests.utils import (
    DEPLOYER_ADDRESS,
    EMPTY_HEXADDRESS,
    SERVICE_DEPOSIT,
    call_and_transact,
)
from raiden_contracts.tests.utils.blockchain import mine_blocks
from raiden_contracts.utils.proofs import sign_reward_proof
from raiden_contracts.utils.type_aliases import TokenAmount

REWARD_AMOUNT = TokenAmount(10)


@pytest.fixture
def ms_address(
    get_accounts: Callable, custom_token: Contract, service_registry: Contract
) -> HexAddress:
    (ms,) = get_accounts(1)

    # register MS in the ServiceRegistry contract
    call_and_transact(custom_token.functions.mint(2 * SERVICE_DEPOSIT), {"from": ms})
    call_and_transact(
        custom_token.functions.approve(service_registry.address, SERVICE_DEPOSIT),
        {"from": ms},
    )
    call_and_transact(service_registry.functions.deposit(SERVICE_DEPOSIT), {"from": ms})
예제 #10
0
def test_claim_with_insufficient_deposit(
    user_deposit_contract: Contract,
    one_to_n_contract: Contract,
    deposit_to_udc: Callable,
    get_private_key: Callable,
    web3: Web3,
    event_handler: Callable,
    create_account: Callable,
    create_service_account: Callable,
) -> None:
    ev_handler = event_handler(one_to_n_contract)
    A = create_account()
    B = create_service_account()
    deposit_to_udc(A, 6)
    chain_id = web3.eth.chain_id

    amount = TokenAmount(10)
    expiration = BlockExpiration(web3.eth.block_number + 1)
    signature = sign_one_to_n_iou(
        get_private_key(A),
        sender=A,
        receiver=B,
        amount=amount,
        expiration_block=expiration,
        one_to_n_address=one_to_n_contract.address,
        chain_id=ChainID(chain_id),
    )

    # amount is 10, but only 6 are in deposit
    # check return value (transactions don't give back return values, so use call)
    assert (one_to_n_contract.functions.claim(A, B, amount, expiration,
                                              one_to_n_contract.address,
                                              signature).call({"from":
                                                               A}) == 6)
    # check that transaction succeeds
    call_and_transact(
        one_to_n_contract.functions.claim(A, B, amount, expiration,
                                          one_to_n_contract.address,
                                          signature),
        {"from": A},
    )

    assert user_deposit_contract.functions.balances(A).call() == 0
    assert user_deposit_contract.functions.balances(B).call() == 6

    # claim can be retried when transferred amount was 0
    expiration = BlockExpiration(web3.eth.block_number + 10)
    signature = sign_one_to_n_iou(
        get_private_key(A),
        sender=A,
        receiver=B,
        amount=amount,
        expiration_block=expiration,
        one_to_n_address=one_to_n_contract.address,
        chain_id=ChainID(chain_id),
    )
    call_and_transact(
        one_to_n_contract.functions.claim(A, B, amount, expiration,
                                          one_to_n_contract.address,
                                          signature),
        {"from": A},
    )
    deposit_to_udc(A, 6 + 4)
    tx_hash = call_and_transact(
        one_to_n_contract.functions.claim(A, B, amount, expiration,
                                          one_to_n_contract.address,
                                          signature),
        {"from": A},
    )
    ev_handler.assert_event(
        tx_hash,
        OneToNEvent.CLAIMED,
        dict(sender=A, receiver=B, expiration_block=expiration, transferred=4),
    )
예제 #11
0
    def __init__(
        self,
        pathfinding_service: PathfindingService,
        one_to_n_address: Address,
        operator: str,
        info_message: str = DEFAULT_INFO_MESSAGE,
        service_fee: TokenAmount = TokenAmount(0),
        debug_mode: bool = False,
        enable_tracing: bool = False,
    ) -> None:
        flask_app = Flask(__name__)

        if enable_tracing:
            FlaskTracing(opentracing.tracer, trace_all_requests=True, app=flask_app)

        self.flask_app = DispatcherMiddleware(
            NotFound(),
            {
                "/metrics": make_wsgi_app(registry=metrics.REGISTRY),
                API_PATH: flask_app.wsgi_app,
            },
        )

        self.api = ApiWithErrorHandler(flask_app)
        self.rest_server: Optional[WSGIServer] = None
        self.one_to_n_address = one_to_n_address
        self.pathfinding_service = pathfinding_service
        self.service_fee = service_fee
        self.operator = operator
        self.info_message = info_message

        # Enable cross origin requests
        @flask_app.after_request
        def after_request(response: Response) -> Response:  # pylint: disable=unused-variable
            response.headers.add("Access-Control-Allow-Origin", "*")
            response.headers.add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept")
            response.headers.add("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
            return response

        resources: List[Tuple[str, Resource, Dict, str]] = [
            (
                "/v1/<token_network_address>/paths",
                PathsResource,
                dict(debug_mode=debug_mode),
                "paths",
            ),
            ("/v1/<token_network_address>/payment/iou", IOUResource, {}, "payments"),
            ("/v1/<token_network_address>/feedback", FeedbackResource, {}, "feedback"),
            (
                "/v1/<token_network_address>/suggest_partner",
                SuggestPartnerResource,
                {},
                "suggest_partner",
            ),
            ("/v1/online_addresses", OnlineAddressesResource, {}, "online_addresses"),
            ("/v1/info", InfoResource, {}, "info"),
            ("/v2/info", InfoResource2, {}, "info2"),
            (
                "/v1/address/<checksummed_address>/metadata",
                AddressMetadataResource,
                {},
                "address_metadata",
            ),
        ]

        if debug_mode:
            log.warning("The debug REST API is enabled. Don't do this on public nodes.")
            resources.extend(
                [
                    (
                        "/v1/_debug/routes/<token_network_address>/<source_address>",
                        cast(Resource, DebugPathResource),
                        {},
                        "debug1",
                    ),
                    (
                        "/v1/_debug/routes/<token_network_address>/<source_address>/<target_address>",  # noqa
                        DebugPathResource,
                        {},
                        "debug2",
                    ),
                    ("/v1/_debug/ious/<source_address>", DebugIOUResource, {}, "debug3"),
                    ("/v1/_debug/stats", DebugStatsResource, {}, "debug4"),
                ]
            )

        for endpoint_url, resource, kwargs, endpoint in resources:
            kwargs.update({"pathfinding_service": pathfinding_service, "api": self})
            self.api.add_resource(
                resource, endpoint_url, resource_class_kwargs=kwargs, endpoint=f"rest-{endpoint}"
            )