def update_iou( iou: Dict[str, Any], privkey: bytes, added_amount: TokenAmount = ZERO_TOKENS, expiration_block: Optional[BlockNumber] = None, ) -> Dict[str, Any]: expected_signature = to_hex( sign_one_to_n_iou( privatekey=to_hex(privkey), expiration=iou["expiration_block"], sender=iou["sender"], receiver=iou["receiver"], amount=iou["amount"], )) if iou.get("signature") != expected_signature: raise ServiceRequestFailed( "Last IOU as given by the pathfinding service is invalid (signature does not match)" ) iou["amount"] += added_amount if expiration_block: iou["expiration_block"] = expiration_block iou["signature"] = to_hex( sign_one_to_n_iou( privatekey=to_hex(privkey), expiration=iou["expiration_block"], sender=iou["sender"], receiver=iou["receiver"], amount=iou["amount"], )) return iou
def update_iou( iou: typing.Dict[str, typing.Any], privkey: bytes, added_amount: typing.TokenAmount = 0, expiration_block: typing.Optional[typing.BlockNumber] = None, ) -> typing.Dict[str, typing.Any]: expected_signature = to_hex( sign_one_to_n_iou( privatekey=to_hex(privkey), expiration=iou['expiration_block'], sender=iou['sender'], receiver=iou['receiver'], amount=iou['amount'], )) if iou.get('signature') != expected_signature: raise ServiceRequestFailed( 'Last IOU as given by the pathfinding service is invalid (signature does not match)', ) iou['amount'] += added_amount if expiration_block: iou['expiration_block'] = expiration_block iou['signature'] = sign_one_to_n_iou( privatekey=to_hex(privkey), expiration=iou['expiration_block'], sender=iou['sender'], receiver=iou['receiver'], amount=iou['amount'], ) return iou
def test_get_pfs_iou(): token_network_address = TokenNetworkAddress(bytes([1] * 20)) privkey = bytes([2] * 32) sender = to_checksum_address(privatekey_to_address(privkey)) receiver = factories.make_checksum_address() with patch('raiden.network.pathfinding.requests.get') as get_mock: # No previous IOU get_mock.return_value.json.return_value = {'last_iou': None} assert get_last_iou('http://example.com', token_network_address, sender, receiver) is None # Previous IOU iou = dict(sender=sender, receiver=receiver, amount=10, expiration_block=1000) iou['signature'] = sign_one_to_n_iou( privatekey=encode_hex(privkey), sender=sender, receiver=receiver, amount=iou['amount'], expiration=iou['expiration_block'], ) get_mock.return_value.json.return_value = {'last_iou': iou} assert get_last_iou('http://example.com', token_network_address, sender, receiver) == iou
def test_get_pfs_iou(): token_network_address = TokenNetworkAddress(bytes([1] * 20)) privkey = bytes([2] * 32) sender = privatekey_to_address(privkey) receiver = factories.make_address() with patch("raiden.network.pathfinding.requests.get") as get_mock: # No previous IOU get_mock.return_value.json.return_value = {"last_iou": None} assert (get_last_iou("http://example.com", token_network_address, sender, receiver, PRIVKEY) is None) # Previous IOU iou = dict(sender=sender, receiver=receiver, amount=10, expiration_block=1000) iou["signature"] = sign_one_to_n_iou( privatekey=encode_hex(privkey), sender=to_checksum_address(sender), receiver=to_checksum_address(receiver), amount=iou["amount"], expiration=iou["expiration_block"], ) get_mock.return_value.json.return_value = {"last_iou": iou} assert (get_last_iou("http://example.com", token_network_address, sender, receiver, PRIVKEY) == iou)
def print_gas_one_to_n( one_to_n_contract: Contract, deposit_to_udc: Callable, get_accounts: Callable, get_private_key: Callable, web3: Web3, print_gas: Callable, ) -> None: """ Abusing pytest to print gas cost of OneToN functions """ (A, B) = get_accounts(2) deposit_to_udc(A, 30) # happy case chain_id = int(web3.version.network) amount = 10 expiration = web3.eth.blockNumber + 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=chain_id, ) txn_hash = one_to_n_contract.functions.claim( A, B, amount, expiration, one_to_n_contract.address, signature ).call_and_transact({"from": A}) print_gas(txn_hash, CONTRACT_ONE_TO_N + ".claim")
def print_gas_one_to_n( one_to_n_contract, deposit_to_udc, get_accounts, get_private_key, web3, print_gas, ): """ Abusing pytest to print gas cost of OneToN functions """ (A, B) = get_accounts(2) deposit_to_udc(A, 30) # happy case amount = 10 expiration = web3.eth.blockNumber + 2 signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount, expiration=expiration, ) txn_hash = one_to_n_contract.functions.claim( A, B, amount, expiration, signature, ).transact({'from': A}) print_gas(txn_hash, CONTRACT_ONE_TO_N + '.claim')
def update_iou( iou: IOU, privkey: bytes, added_amount: TokenAmount = ZERO_TOKENS, expiration_block: Optional[BlockNumber] = None, ) -> IOU: expected_signature = Signature( sign_one_to_n_iou( privatekey=to_hex(privkey), sender=to_checksum_address(iou.sender), receiver=to_checksum_address(iou.receiver), amount=iou.amount, expiration_block=iou.expiration_block, one_to_n_address=to_checksum_address(iou.one_to_n_address), chain_id=iou.chain_id, )) if iou.signature != expected_signature: raise ServiceRequestFailed( "Last IOU as given by the Pathfinding Service is invalid (signature does not match)" ) iou.amount = TokenAmount(iou.amount + added_amount) if expiration_block: iou.expiration_block = expiration_block iou.sign(privkey) return iou
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 = 10 expiration = web3.eth.blockNumber + 2 chain_id = int(web3.version.network) 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=chain_id, ) # Doesn't work because B is not registered with pytest.raises(TransactionFailed, match="receiver not registered"): 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, ).call_and_transact({"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 = int(web3.version.network) amount = 10 expiration = web3.eth.blockNumber + 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=chain_id, ) txn_hash = one_to_n_contract.functions.claim( A, B, amount, expiration, one_to_n_contract.address, signature).call_and_transact({"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 = 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), ).call_and_transact({"from": A}) print_gas(txn_hash, CONTRACT_ONE_TO_N + f".bulkClaim {num_ious} ious")
def sign(self, privkey: bytes) -> None: self.signature = Signature( sign_one_to_n_iou( privatekey=encode_hex(privkey), sender=to_checksum_address(self.sender), receiver=to_checksum_address(self.receiver), amount=self.amount, expiration_block=self.expiration_block, one_to_n_address=to_checksum_address(self.one_to_n_address), chain_id=self.chain_id, ))
def f( sender: HexAddress, receiver: HexAddress, amount: int = 10, expiration_block: int = None, chain_id: int = chain_id, one_to_n_address: HexAddress = one_to_n_contract.address, ) -> dict: if expiration_block is None: expiration_block = web3.eth.blockNumber + 10 iou = dict( sender=sender, receiver=receiver, amount=amount, expiration_block=expiration_block, one_to_n_address=one_to_n_address, chain_id=chain_id, ) iou["signature"] = sign_one_to_n_iou(get_private_key(sender), **iou) del iou["chain_id"] return iou
def test_update_iou(): privkey = bytes([2] * 32) sender = Address(privatekey_to_address(privkey)) receiver = Address(bytes([1] * 20)) # prepare iou iou = { "sender": encode_hex(sender), "receiver": encode_hex(receiver), "amount": 10, "expiration_block": 1000, } iou["signature"] = encode_hex( sign_one_to_n_iou( privatekey=encode_hex(privkey), sender=iou["sender"], receiver=iou["receiver"], amount=iou["amount"], expiration=iou["expiration_block"], )) # update and compare added_amount = 10 new_iou = update_iou(iou=iou.copy(), privkey=privkey, added_amount=added_amount) assert new_iou["amount"] == iou["amount"] + added_amount assert new_iou["sender"] == iou["sender"] assert new_iou["receiver"] == iou["receiver"] assert new_iou["signature"] != iou["signature"] # Previous IOU with increased amount by evil PFS tampered_iou = new_iou.copy() tampered_iou["amount"] += 10 with pytest.raises(ServiceRequestFailed): update_iou(iou=tampered_iou, privkey=privkey, added_amount=added_amount)
def f( sender_priv_key, receiver: Address, amount=1, expiration_block=MIN_IOU_EXPIRY + 100, one_to_n_address=one_to_n_contract.address, chain_id=1, ) -> IOU: receiver_hex: str = to_checksum_address(receiver) iou_dict = { "sender": to_checksum_address(private_key_to_address(sender_priv_key)), "receiver": receiver_hex, "amount": amount, "expiration_block": expiration_block, "one_to_n_address": to_checksum_address(one_to_n_address), "chain_id": chain_id, } iou_dict["signature"] = encode_hex( sign_one_to_n_iou(privatekey=sender_priv_key, **iou_dict)) iou = IOU.Schema().load(iou_dict) iou.claimed = False return iou
def f( sender_priv_key: PrivateKey, receiver: Address, amount=1, claimable_until=1000000000 * 15 + MIN_IOU_EXPIRY, one_to_n_address: Address = one_to_n_contract_address, chain_id: ChainID = ChainID(61), ) -> IOU: receiver_hex: str = to_checksum_address(receiver) iou_dict = { "sender": to_checksum_address(private_key_to_address(sender_priv_key)), "receiver": receiver_hex, "amount": amount, "claimable_until": claimable_until, "one_to_n_address": to_checksum_address(one_to_n_address), "chain_id": chain_id, } iou_dict["signature"] = encode_hex( sign_one_to_n_iou(privatekey=sender_priv_key, **iou_dict)) iou = IOU.Schema().load(iou_dict) iou.claimed = False return iou
def make_iou( config: Dict[str, Any], our_address: Address, one_to_n_address: Address, privkey: bytes, block_number: BlockNumber, chain_id: int, offered_fee: TokenAmount = None, ) -> Dict: expiration = block_number + config["pathfinding_iou_timeout"] iou = dict( sender=to_checksum_address(our_address), receiver=config["pathfinding_eth_address"], amount=offered_fee or config["pathfinding_max_fee"], expiration_block=expiration, one_to_n_address=to_checksum_address(one_to_n_address), chain_id=chain_id, ) iou.update( signature=to_hex(sign_one_to_n_iou(privatekey=to_hex(privkey), **iou))) return iou
def make_iou( config: typing.Dict[str, typing.Any], our_address: typing.Address, privkey: bytes, block_number: typing.BlockNumber, offered_fee: typing.TokenAmount = None, ) -> typing.Dict: expiration = block_number + config['pathfinding_iou_timeout'] iou = dict( sender=to_checksum_address(our_address), receiver=config['pathfinding_eth_address'], amount=offered_fee or config['pathfinding_max_fee'], ) iou.update( expiration_block=expiration, signature=to_hex( sign_one_to_n_iou(privatekey=to_hex(privkey), expiration=expiration, **iou), ), ) return iou
def test_claim( user_deposit_contract, one_to_n_contract, deposit_to_udc, get_accounts, get_private_key, web3, event_handler, ): ev_handler = event_handler(one_to_n_contract) (A, B) = get_accounts(2) deposit_to_udc(A, 30) # happy case amount = 10 expiration = web3.eth.blockNumber + 2 signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount, expiration=expiration, ) tx_hash = one_to_n_contract.functions.claim( A, B, amount, expiration, signature, ).transact({'from': A}) ev_handler.assert_event( tx_hash, OneToNEvent.CLAIMED, dict(sender=A, receiver=B, expiration_block=expiration, transferred=amount), ) assert user_deposit_contract.functions.balances(A).call() == 20 assert user_deposit_contract.functions.balances(B).call() == 10 # can't be claimed twice with pytest.raises(TransactionFailed): one_to_n_contract.functions.claim( A, B, amount, expiration, signature, ).transact({'from': A}) # IOU expired with pytest.raises(TransactionFailed): bad_expiration = web3.eth.blockNumber signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount, expiration=bad_expiration, ) one_to_n_contract.functions.claim( A, B, amount, bad_expiration, signature, ).transact({'from': A}) # bad signature with pytest.raises(TransactionFailed): expiration = web3.eth.blockNumber + 1 signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount + 1, # this does not match amount below expiration=expiration, ) one_to_n_contract.functions.claim( A, B, amount, expiration, signature, ).transact({'from': A})
def test_claim( user_deposit_contract, one_to_n_contract, deposit_to_udc, get_accounts, get_private_key, web3, event_handler, ): ev_handler = event_handler(one_to_n_contract) (A, B) = get_accounts(2) deposit_to_udc(A, 30) # happy case amount = 10 expiration = web3.eth.blockNumber + 2 chain_id = int(web3.version.network) # IOU expired with pytest.raises(TransactionFailed): bad_expiration = web3.eth.blockNumber - 1 signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount, expiration_block=bad_expiration, one_to_n_address=one_to_n_contract.address, chain_id=chain_id, ) one_to_n_contract.functions.claim(A, B, amount, bad_expiration, one_to_n_contract.address, signature).call({"from": A}) # Wrong OneToN address with pytest.raises(TransactionFailed): signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount, expiration_block=expiration, one_to_n_address=A, # Inject an error chain_id=chain_id, ) one_to_n_contract.functions.claim(A, B, amount, expiration, A, signature).call({"from": A}) # Wrong chain_id with pytest.raises(TransactionFailed): 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=chain_id + 2, # Inject an error ) one_to_n_contract.functions.claim(A, B, amount, expiration, A, signature).call({"from": A}) # bad signature with pytest.raises(TransactionFailed): expiration = web3.eth.blockNumber + 1 signature = sign_one_to_n_iou( get_private_key(A), sender=A, receiver=B, amount=amount + 1, # this does not match amount below expiration_block=expiration, one_to_n_address=one_to_n_contract.address, chain_id=chain_id, ) one_to_n_contract.functions.claim(A, B, amount, expiration, one_to_n_contract.address, signature).call({"from": A}) 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=chain_id, ) tx_hash = 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, ).call_and_transact({"from": A}) ev_handler.assert_event( tx_hash, OneToNEvent.CLAIMED, dict(sender=A, receiver=B, expiration_block=expiration, transferred=amount), ) assert user_deposit_contract.functions.balances(A).call() == 20 assert user_deposit_contract.functions.balances(B).call() == 10 # can't be claimed twice with pytest.raises(TransactionFailed): one_to_n_contract.functions.claim(A, B, amount, expiration, one_to_n_contract.address, signature).call({"from": A})
def test_claim_with_insufficient_deposit( user_deposit_contract, one_to_n_contract, deposit_to_udc, get_accounts, get_private_key, web3, event_handler, ): ev_handler = event_handler(one_to_n_contract) (A, B) = get_accounts(2) deposit_to_udc(A, 6) chain_id = int(web3.version.network) amount = 10 expiration = web3.eth.blockNumber + 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=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 one_to_n_contract.functions.claim(A, B, amount, expiration, one_to_n_contract.address, signature).call_and_transact({"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 = web3.eth.blockNumber + 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=chain_id, ) one_to_n_contract.functions.claim(A, B, amount, expiration, one_to_n_contract.address, signature).call_and_transact({"from": A}) deposit_to_udc(A, 6 + 4) tx_hash = one_to_n_contract.functions.claim( A, B, amount, expiration, one_to_n_contract.address, signature).call_and_transact({"from": A}) ev_handler.assert_event( tx_hash, OneToNEvent.CLAIMED, dict(sender=A, receiver=B, expiration_block=expiration, transferred=4), )
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), )