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)
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))
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
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, )
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)
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}, )
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")
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")
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})
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), )
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}" )