def test_sha256_secrethash(): assert sha256_secrethash(Secret(b"")) == ( b"\xe3\xb0\xc4B\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99o\xb9$" b"'\xaeA\xe4d\x9b\x93L\xa4\x95\x99\x1bxR\xb8U" ) assert sha256_secrethash(Secret(b"a")) == ( b"\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc21\xb3\x9a#\xdcM" b"\xa7\x86\xef\xf8\x14|Nr\xb9\x80w\x85\xaf\xeeH\xbb" ) secret = Secret(b"secretsecretsecretsecretsecretse") assert sha256_secrethash(secret) == ( b'\xd4h:"\xc1\xce9\x82M\x93\x1e\xed\xc6\x8e\xa8\xfa' b"RY\xce\xb05(\xb1\xa2/pu\x86>\xf8\xba\xf0" )
def from_dict(cls, data: Dict[str, Any]) -> 'ContractSendSecretReveal': restored = cls( expiration=BlockExpiration(int(data['expiration'])), secret=Secret(serialization.deserialize_bytes(data['secret'])), ) return restored
def from_dict(cls, data: Dict[str, Any]) -> "ReceiveSecretReveal": instance = cls( secret=Secret(deserialize_bytes(data["secret"])), sender=to_canonical_address(data["sender"]), ) instance.secrethash = deserialize_bytes(data["secrethash"]) return instance
def from_dict(cls, data: Dict[str, Any]) -> "ReceiveTransferRefundCancelRoute": instance = cls( routes=data["routes"], transfer=data["transfer"], secret=Secret(deserialize_bytes(data["secret"])), ) return instance
def from_dict(cls, data: Dict[str, Any]) -> 'ReceiveTransferRefundCancelRoute': instance = cls( routes=data['routes'], transfer=data['transfer'], secret=Secret(deserialize_bytes(data['secret'])), ) return instance
def random_secret() -> Secret: """ Return a random 32 byte secret except the 0 secret since it's not accepted in the contracts """ while True: secret = os.urandom(constants.SECRET_LENGTH) if secret != constants.EMPTY_HASH: return Secret(secret)
def random_payment_hash_invoice() -> PaymentHashInvoice: """ Return a random 32 byte secret except the 0 secret since it's not accepted in the contracts """ while True: secret = os.urandom(constants.SECRETHASH_LENGTH) if secret != constants.EMPTY_HASH: secret = Secret(secret) return sha3(secret)
def from_dict(cls, data: Dict[str, Any]) -> "ContractSendSecretReveal": restored = cls( expiration=BlockExpiration(int(data["expiration"])), secret=Secret(serialization.deserialize_bytes(data["secret"])), triggered_by_block_hash=BlockHash( deserialize_bytes(data["triggered_by_block_hash"])), ) return restored
def from_dict(cls, data: Dict[str, Any]) -> 'SendSecretReveal': restored = cls( recipient=to_canonical_address(data['recipient']), channel_identifier=ChannelID(int(data['channel_identifier'])), message_identifier=MessageID(int(data['message_identifier'])), secret=Secret(serialization.deserialize_bytes(data['secret'])), ) return restored
def test_automatic_secret_registration(raiden_chain, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) hold_event_handler = app1.raiden.raiden_event_handler amount = 100 identifier = 1 message_handler = WaitForMessage() app1.raiden.message_handler = message_handler target = app1.raiden.address secret = Secret(sha3(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) locked_transfer_received = message_handler.wait_for_message( LockedTransfer, {}) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) # Wait for app1 to receive the locked transfer. locked_transfer_received.wait() # Stop app0 to avoid sending the unlock, this must be done after the locked # transfer is sent. app0.raiden.transport.stop() reveal_secret = RevealSecret( message_identifier=MessageID(random.randint(0, UINT64_MAX)), secret=secret, signature=EMPTY_SIGNATURE, ) app0.raiden.sign(reveal_secret) message_handler.on_message(app1.raiden, reveal_secret) chain_state = views.state_from_app(app1) secrethash = sha256_secrethash(secret) target_task = chain_state.payment_mapping.secrethashes_to_task[secrethash] lock_expiration = target_task.target_state.transfer.lock.expiration # type: ignore app1.raiden.proxy_manager.wait_until_block( target_block_number=lock_expiration) assert app1.raiden.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier="latest")
def assert_payment_secret_and_hash(response, payment): # make sure that payment key/values are part of the response. assert len(response) == 7 assert "secret" in response assert "secret_hash" in response secret = Secret(to_bytes(hexstr=response["secret"])) assert len(secret) == SECRET_LENGTH assert payment["amount"] == response["amount"] assert to_bytes( hexstr=response["secret_hash"]) == sha256_secrethash(secret)
def from_dict(cls, data: Dict[str, Any]) -> 'SendBalanceProof': restored = cls( recipient=to_canonical_address(data['recipient']), channel_identifier=ChannelID(int(data['channel_identifier'])), message_identifier=MessageID(int(data['message_identifier'])), payment_identifier=PaymentID(int(data['payment_identifier'])), token_address=to_canonical_address(data['token_address']), secret=Secret(serialization.deserialize_bytes(data['secret'])), balance_proof=data['balance_proof'], ) return restored
def message_from_sendevent(send_event, our_address): if type(send_event) == SendLockedTransfer: message = LockedTransfer.from_event(send_event) elif type(send_event) == SendDirectTransfer: message = DirectTransfer.from_event(send_event) elif type(send_event) == SendRevealSecret: message = RevealSecret.from_event(send_event) elif type(send_event) == SendBalanceProof: message = Secret.from_event(send_event) elif type(send_event) == SendSecretRequest: message = SecretRequest.from_event(send_event) elif type(send_event) == SendRefundTransfer: message = RefundTransfer.from_event(send_event) elif type(send_event) == SendProcessed: message = Processed.from_event(send_event) else: raise ValueError(f'Unknown event type {send_event}') return message
def message_from_sendevent(send_event, our_address): if type(send_event) == SendLockedTransfer: message = LockedTransfer.from_event(send_event) elif type(send_event) == SendDirectTransfer: message = DirectTransfer.from_event(send_event) elif type(send_event) == SendRevealSecret: message = RevealSecret.from_event(send_event) elif type(send_event) == SendBalanceProof: message = Secret.from_event(send_event) elif type(send_event) == SendSecretRequest: message = SecretRequest.from_event(send_event) elif type(send_event) == SendRefundTransfer: message = RefundTransfer.from_event(send_event) elif type(send_event) == SendProcessed: message = Processed.from_event(send_event) else: raise ValueError(f'Unknown event type {send_event}') return message
def test_start_end_attack(token_addresses, raiden_chain, deposit): """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" amount = 30 token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token ) assert token_network_address hold_event_handler = app2.raiden.raiden_event_handler # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 target = app2.raiden.address secret = Secret(sha3(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) attack_channel = get_channelstate(app2, app1, token_network_address) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address # type: ignore hub_contract = get_channelstate( # type: ignore app1, app0, token_network_address ).external_state.netting_channel.address # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() # type: ignore attack_channel.netting_channel.channel_close(attack_balance_proof) # type: ignore # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test assert attack_transfer app2.raiden.proxy_manager.wait_until_block( target_block_number=attack_transfer.lock.expiration - 1 ) # since the attacker knows the secret he can net the lock # <the commented code below is left for documentation purposes> # attack_channel.netting_channel.unlock( # UnlockProofState(unlock_proof, attack_transfer.lock, secret) # ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2proxymanager = app2.raiden.proxy_manager app2proxymanager.wait_until_block( target_block_number=app2proxymanager.client.block_number() + 1 ) attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.proxy_manager.token_hashchannel[token][attack_contract] assert attack_contract.participants[app2.raiden.address]["netted"] == deposit + amount assert attack_contract.participants[app1.raiden.address]["netted"] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]["netted"] == deposit assert hub_contract.participants[app1.raiden.address]["netted"] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1proxymanager = app1.raiden.proxy_manager app1proxymanager.wait_until_block( target_block_number=app1proxymanager.client.block_number() + 1 ) # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]["netted"] == deposit + amount assert hub_contract.participants[app1.raiden.address]["netted"] == deposit - amount
# Set at 64 since parity's default is 64 and Geth's default is 128 # TODO: Make this configurable. Since in parity this is also a configurable value STATE_PRUNING_AFTER_BLOCKS = 64 STATE_PRUNING_SAFETY_MARGIN = 8 NO_STATE_QUERY_AFTER_BLOCKS = STATE_PRUNING_AFTER_BLOCKS - STATE_PRUNING_SAFETY_MARGIN NULL_ADDRESS_BYTES = bytes(20) NULL_ADDRESS_HEX = to_hex_address(Address(NULL_ADDRESS_BYTES)) NULL_ADDRESS_CHECKSUM = to_checksum_address(Address(NULL_ADDRESS_BYTES)) EMPTY_HASH = BlockHash(bytes(32)) EMPTY_BALANCE_HASH = BalanceHash(bytes(32)) EMPTY_MESSAGE_HASH = AdditionalHash(bytes(32)) EMPTY_SIGNATURE = Signature(bytes(65)) EMPTY_SECRET = Secret(bytes(32)) EMPTY_SECRETHASH = SecretHash(bytes(32)) EMPTY_SECRET_SHA256 = sha256_secrethash(EMPTY_SECRET) LOCKSROOT_OF_NO_LOCKS = Locksroot(keccak(b"")) EMPTY_LOCKSROOT = Locksroot(bytes(32)) ZERO_TOKENS = TokenAmount(0) ABSENT_SECRET = Secret(b"") SECRET_LENGTH = 32 SECRETHASH_LENGTH = 32 RECEIPT_FAILURE_CODE = 0 class EthClient(Enum):
def test_clear_closed_queue(raiden_network: List[App], token_addresses, network_wait): """ Closing a channel clears the respective message queue. """ app0, app1 = raiden_network hold_event_handler = app1.raiden.raiden_event_handler assert isinstance(hold_event_handler, HoldRaidenEventHandler) registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] chain_state0 = views.state_from_app(app0) token_network_address = views.get_token_network_address_by_token_address( chain_state0, app0.raiden.default_registry.address, token_address) assert token_network_address token_network = views.get_token_network_by_address(chain_state0, token_network_address) assert token_network channel_identifier = get_channelstate(app0, app1, token_network_address).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ app1.raiden.address]) target = app1.raiden.address secret = Secret(keccak(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) # make an unconfirmed transfer to ensure the nodes have communicated amount = PaymentAmount(10) payment_identifier = PaymentID(1337) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(target), identifier=payment_identifier, secret=secret, ) app1.raiden.transport.stop() app1.raiden.transport.greenlet.get() # make sure to wait until the queue is created def has_initiator_events(): assert app0.raiden.wal, "raiden server must have been started" initiator_events = app0.raiden.wal.storage.get_events() return search_for_item(initiator_events, SendLockedTransfer, {}) assert wait_until(has_initiator_events, network_wait) # assert the specific queue is present chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert [ (queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue_id. canonical_identifier.channel_identifier == channel_identifier and queue ] # A ChannelClose event will be generated, this will be polled by both apps RaidenAPI(app0.raiden).channel_close(registry_address, token_address, app1.raiden.address) with block_offset_timeout(app0.raiden, "Could not get close event"): waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) # assert all queues with this partner are gone or empty chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert not [(queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue] chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) assert not [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue]
def reveal_secret_with_resolver( raiden: "RaidenService", chain_state: ChainState, secret_request_event: SendSecretRequest) -> bool: resolver_endpoint = raiden.config.resolver_endpoint if not resolver_endpoint: return False log.debug("Using resolver to fetch secret", resolver_endpoint=resolver_endpoint) assert isinstance(raiden.wal, WriteAheadLog), "RaidenService has not been started" current_state = raiden.wal.state_manager.current_state if current_state is None: return False task = current_state.payment_mapping.secrethashes_to_task[ secret_request_event.secrethash] token = task.target_state.transfer.token request = { "token": to_hex(token), "secrethash": to_hex(secret_request_event.secrethash), "amount": secret_request_event.amount, "payment_identifier": secret_request_event.payment_identifier, "payment_sender": to_hex(secret_request_event.recipient), "expiration": secret_request_event.expiration, "payment_recipient": to_hex(raiden.address), "chain_id": chain_state.chain_id, } # loop until we get a valid response from the resolver or until timeout while True: current_state = views.state_from_raiden(raiden) if secret_request_event.expiration < current_state.block_number: log.debug("Stopped using resolver, transfer expired", resolver_endpoint=resolver_endpoint) # The locked transfer is expired now, so it makes no sense to continue processing it. # When returning `False`, the event handler would contiue processing the events, which # is not what is wanted here, so we return `True` to cancel return True response = None try: # before calling resolver, update block height request["chain_height"] = chain_state.block_number response = requests.post(resolver_endpoint, json=request) except requests.exceptions.RequestException: pass # no response means the resolver could not be reached and we should try again if response is not None: if response.status_code == HTTPStatus.OK: # request succeeded so we can break the loop and use the secret break elif response.status_code == HTTPStatus.SERVICE_UNAVAILABLE: # treat SERVICE UNAVAILABLE as if the resolver could not be reached and try again pass else: # on any other status code, treat the request as having failed and return False return False gevent.sleep(5) log.debug("Got secret from resolver, dispatching secret reveal", resolver_endpoint=resolver_endpoint) state_change = ReceiveSecretReveal( sender=secret_request_event.recipient, secret=Secret(to_bytes(hexstr=json.loads(response.content)["secret"])), ) raiden.handle_and_track_state_changes([state_change]) return True
def test_mediated_transfer_calls_pfs(raiden_chain: List[App], token_addresses: List[TokenAddress]): app0, app1, app2 = raiden_chain token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address ) assert token_network_address, "Fixture token_addresses don't have correspoding token_network" with patch("raiden.routing.query_paths", return_value=([], None)) as patched: app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=PaymentAmount(10), target=TargetAddress(app1.raiden.address), identifier=PaymentID(1), secret=Secret(b"1" * 32), ) assert not patched.called # Setup PFS config app0.raiden.config.pfs_config = PFSConfig( info=PFSInfo( url="mock-address", chain_id=app0.raiden.rpc_client.chain_id, token_network_registry_address=token_network_registry_address, user_deposit_address=factories.make_address(), payment_address=factories.make_address(), confirmed_block_number=chain_state.block_number, message="", operator="", version="", price=TokenAmount(0), ), maximum_fee=TokenAmount(100), iou_timeout=BlockTimeout(100), max_paths=5, ) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=PaymentAmount(11), target=TargetAddress(app2.raiden.address), identifier=PaymentID(2), secret=Secret(b"2" * 32), ) assert patched.call_count == 1 # Mediator should not re-query PFS locked_transfer = factories.create( factories.LockedTransferProperties( amount=TokenAmount(5), initiator=factories.HOP1, target=TargetAddress(app2.raiden.address), sender=factories.HOP1, pkey=factories.HOP1_KEY, token=token_address, canonical_identifier=factories.make_canonical_identifier( token_network_address=token_network_address ), ) ) app0.raiden.on_messages([locked_transfer]) assert patched.call_count == 1
# Set at 64 since parity's default is 64 and Geth's default is 128 # TODO: Make this configurable. Since in parity this is also a configurable value STATE_PRUNING_AFTER_BLOCKS = 64 STATE_PRUNING_SAFETY_MARGIN = 8 NO_STATE_QUERY_AFTER_BLOCKS = STATE_PRUNING_AFTER_BLOCKS - STATE_PRUNING_SAFETY_MARGIN NULL_ADDRESS_BYTES = bytes(20) NULL_ADDRESS = to_checksum_address(NULL_ADDRESS_BYTES) EMPTY_HASH = BlockHash(bytes(32)) EMPTY_BALANCE_HASH = BalanceHash(bytes(32)) EMPTY_MESSAGE_HASH = AdditionalHash(bytes(32)) EMPTY_HASH_KECCAK = keccak(EMPTY_HASH) EMPTY_SIGNATURE = Signature(bytes(65)) EMPTY_MERKLE_ROOT = Locksroot(bytes(32)) EMPTY_SECRET = Secret(b"") ZERO_TOKENS = TokenAmount(0) SECRET_HEXSTRING_LENGTH = len(to_hex(EMPTY_HASH)) SECRETHASH_HEXSTRING_LENGTH = SECRET_HEXSTRING_LENGTH RECEIPT_FAILURE_CODE = 0 class EthClient(Enum): GETH = "geth" PARITY = "parity" ETH_RPC_DEFAULT_PORT = 8545 HTTP_PORT = 80
def test_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret reveal must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] amount = PaymentAmount(30) token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address ) assert token_network_address hold_event_handler = app1.raiden.raiden_event_handler address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.proxy_manager.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) identifier = 1 target = app1.raiden.address secret = Secret(sha3(target)) secrethash = sha256_secrethash(secret) secret_available = hold_event_handler.hold_secretrequest_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) secret_available.wait() # wait for the messages to be exchanged # Save the pending locks from the pending transfer, used to test the unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_address) batch_unlock = channel.get_batch_unlock(channelstate_0_1.our_state) assert batch_unlock hold_event_handler.release_secretrequest_for(app1.raiden, secrethash) transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=amount, identifier=PaymentID(2), ) RaidenAPI(app1.raiden).channel_close(registry_address, token_address, app0.raiden.address) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channelstate_0_1.identifier], app1.raiden.alarm.sleep_time, ) current_block = app0.raiden.rpc_client.block_number() netting_channel = app1.raiden.proxy_manager.payment_channel( canonical_identifier=channelstate_0_1.canonical_identifier ) # The transfer locksroot must not contain the unlocked lock, the # unlock must fail. with pytest.raises(RaidenUnrecoverableError): netting_channel.unlock( sender=channelstate_0_1.our_state.address, receiver=channelstate_0_1.partner_state.address, pending_locks=batch_unlock, given_block_identifier=current_block, ) expected_balance0 = initial_balance0 + deposit0 - amount * 2 expected_balance1 = initial_balance1 + deposit1 + amount * 2 assert token_proxy.balance_of(address0) == expected_balance0 assert token_proxy.balance_of(address1) == expected_balance1
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit): """Tests that batch unlock is properly called. This test will start a single incomplete transfer, the secret will be revealed *on-chain*. The node that receives the tokens has to call unlock, the node that doesn't gain anything does nothing. """ alice_app, bob_app = raiden_network alice_address = alice_app.raiden.address bob_address = bob_app.raiden.address token_network_registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), token_network_registry_address, token_address ) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler # Take a snapshot early on alice_app.raiden.wal.snapshot() canonical_identifier = get_channelstate( alice_app, bob_app, token_network_address ).canonical_identifier assert is_channel_registered(alice_app, bob_app, canonical_identifier) assert is_channel_registered(bob_app, alice_app, canonical_identifier) token_proxy = alice_app.raiden.proxy_manager.token(token_address) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) # Take snapshot before transfer alice_app.raiden.wal.snapshot() alice_to_bob_amount = 10 identifier = 1 secret = Secret(sha3(bob_address)) secrethash = sha256_secrethash(secret) secret_request_event = hold_event_handler.hold_secretrequest_for(secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_address, identifier=identifier, secret=secret, ) secret_request_event.get() # wait for the messages to be exchanged alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) assert lock # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_address, alice_app, deposit, [lock], bob_app, deposit, [] ) # Test WAL restore to return the latest channel state alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, canonical_identifier=alice_bob_channel_state.canonical_identifier, state_change_identifier=HIGH_STATECHANGE_ULID, ) assert restored_channel_state our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # Close the channel before revealing the secret off-chain. This will leave # a pending lock in the channel which has to be unlocked on-chain. # # The token network will emit a ChannelClose event, this will be polled by # both apps and each must start a task for calling settle. RaidenAPI(bob_app.raiden).channel_close( token_network_registry_address, token_address, alice_app.raiden.address ) # The secret has to be registered manually because Bob never learned the # secret. The test is holding the SecretRequest to ensure the off-chain # unlock will not happen and the channel is closed with a pending lock. # # Alternatives would be to hold the unlock messages, or to stop and restart # the apps after the channel is closed. secret_registry_proxy = alice_app.raiden.proxy_manager.secret_registry(secret_registry_address) secret_registry_proxy.register_secret(secret=secret) msg = ( "The lock must still be part of the node state for the test to proceed, " "otherwise there is not unlock to be done." ) assert lock, msg msg = ( "The secret must be registered before the lock expires, in order for " "the unlock to happen on-chain. Otherwise the test will fail on the " "expected balances." ) assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha256_secrethash(secret) waiting.wait_for_settle( alice_app.raiden, token_network_registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) msg = "The channel_state must not have been cleared, one of the ends has pending locks to do." assert is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert is_channel_registered(bob_app, alice_app, canonical_identifier), msg msg = ( "Timeout while waiting for the unlock to be mined. This may happen if " "transaction is rejected, not mined, or the node's alarm task is " "not running." ) with gevent.Timeout(seconds=30, exception=AssertionError(msg)): # Wait for both nodes (Bob and Alice) to see the on-chain unlock wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) msg = ( "The nodes have done the unlock, and both ends have seen it, now the " "channel must be cleared" ) assert not is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert not is_channel_registered(bob_app, alice_app, canonical_identifier), msg alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount msg = "Unexpected end balance after channel settlement with batch unlock." assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance, msg assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance, msg
def deserialize_secret(data: str) -> Secret: return Secret(deserialize_bytes(data))
def test_batch_unlock_after_restart(raiden_network, token_addresses, deposit): """Simulate the case where: - A sends B a transfer - B sends A a transfer - Secrets were never revealed - B closes channel - A crashes - Wait for settle - Wait for unlock from B - Restart A At this point, the current unlock logic will try to unlock iff the node gains from unlocking. Which means that the node will try to unlock either side. In the above scenario, each node will unlock its side. This test makes sure that we do NOT invalidate A's unlock transaction based on the ContractReceiveChannelBatchUnlock caused by B's unlock. """ alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(alice_app), token_network_registry_address=alice_app.raiden.default_registry.address, token_address=token_address, ) assert token_network_address timeout = 10 token_network = views.get_token_network_by_address( chain_state=views.state_from_app(alice_app), token_network_address=token_network_address ) assert token_network channel_identifier = get_channelstate(alice_app, bob_app, token_network_address).identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = 10 identifier = 1 alice_transfer_secret = Secret(sha3(alice_app.raiden.address)) alice_transfer_secrethash = sha256_secrethash(alice_transfer_secret) bob_transfer_secret = Secret(sha3(bob_app.raiden.address)) bob_transfer_secrethash = sha256_secrethash(bob_transfer_secret) alice_transfer_hold = bob_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=alice_transfer_secrethash ) bob_transfer_hold = alice_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=bob_transfer_secrethash ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_app.raiden.address, identifier=identifier, secret=alice_transfer_secret, ) bob_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=alice_app.raiden.address, identifier=identifier + 1, secret=bob_transfer_secret, ) alice_transfer_hold.wait(timeout=timeout) bob_transfer_hold.wait(timeout=timeout) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash) bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash) assert alice_lock assert bob_lock # This is the current state of protocol: # # A -> B LockedTransfer # - protocol didn't continue assert_synced_channel_state( token_network_address=token_network_address, app0=alice_app, balance0=deposit, pending_locks0=[alice_lock], app1=bob_app, balance1=deposit, pending_locks1=[bob_lock], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address=registry_address, token_address=token_address, partner_address=alice_app.raiden.address, ) # wait for the close transaction to be mined, this is necessary to compute # the timeout for the settle with gevent.Timeout(timeout): waiting.wait_for_close( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) channel_closed = raiden_state_changes_search_for_item( bob_app.raiden, ContractReceiveChannelClosed, { "canonical_identifier": { "token_network_address": token_network_address, "channel_identifier": alice_bob_channel_state.identifier, } }, ) assert isinstance(channel_closed, ContractReceiveChannelClosed) settle_max_wait_block = BlockNumber( channel_closed.block_number + alice_bob_channel_state.settle_timeout * 2 ) settle_timeout = BlockTimeout( RuntimeError("settle did not happen"), bob_app.raiden, settle_max_wait_block, alice_app.raiden.alarm.sleep_time, ) with settle_timeout: waiting.wait_for_settle( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, ) alice_app.start() with gevent.Timeout(timeout): wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, )
def test_api_payments_with_resolver( api_server_test_instance: APIServer, raiden_network, token_addresses, resolvers, # pylint: disable=unused-argument ): _, app1 = raiden_network amount = 100 identifier = 42 token_address = token_addresses[0] target_address = app1.raiden.address secret = factories.make_secret() secret_hash = sha256_secrethash(secret) our_address = api_server_test_instance.rest_api.raiden_api.address payment = { "initiator_address": to_checksum_address(our_address), "target_address": to_checksum_address(target_address), "token_address": to_checksum_address(token_address), "amount": str(amount), "identifier": str(identifier), } # payment with secret_hash when both resolver and initiator don't have the secret request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target_address), ), json={ "amount": str(amount), "identifier": str(identifier), "secret_hash": encode_hex(secret_hash), }, ) response = request.send().response assert_proper_response(response, status_code=HTTPStatus.CONFLICT) assert payment == payment # payment with secret where the resolver doesn't have the secret. Should work. request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target_address), ), json={ "amount": str(amount), "identifier": str(identifier), "secret": encode_hex(secret) }, ) response = request.send().response assert_proper_response(response, status_code=HTTPStatus.OK) assert payment == payment # payment with secret_hash where the resolver has the secret. Should work. secret = Secret( decode_hex( "0x2ff886d47b156de00d4cad5d8c332706692b5b572adfe35e6d2f65e92906806e" )) secret_hash = sha256_secrethash(secret) request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target_address), ), json={ "amount": str(amount), "identifier": str(identifier), "secret_hash": encode_hex(secret_hash), }, ) with watch_for_unlock_failures(*raiden_network): response = request.send().response assert_proper_response(response, status_code=HTTPStatus.OK) assert payment == payment
def random_secret() -> Secret: """ Return a random 32 byte secret""" return Secret(os.urandom(constants.SECRET_LENGTH))
def ishash(data: bytes) -> bool: return isinstance(data, bytes) and len(data) == 32 or data is Secret(b"")
def test_pfs_send_unique_capacity_and_fee_updates_during_mediated_transfer( raiden_network): """ Tests that PFSCapacityUpdates and PFSFeeUpdates are being sent only once with the most recent state change in a batch. """ app0, app1 = raiden_network chain_state = views.state_from_app(app0) # There have been two PFSCapacityUpdates and two PFSFeeUpdates per channel per node assert len(get_messages(app0)) == 4 # The mediator has two channels assert len(get_messages(app1)) == 4 # Now we create two state_changes (Deposit) regarding the same channel # and trigger handle_state_changes() of node0. The expected outcome # is that only 1 PFSCapacityUpdate and 1 PFSFeeUpdate is being sent # not one per state change pfs_fee_update_1_of_app0 = get_messages(app0)[1] assert isinstance(pfs_fee_update_1_of_app0, PFSFeeUpdate) pfs_capacity_update_2_of_app0 = get_messages(app0)[2] assert isinstance(pfs_capacity_update_2_of_app0, PFSCapacityUpdate) canonical_identifier = pfs_fee_update_1_of_app0.canonical_identifier new_total_deposit_1 = pfs_capacity_update_2_of_app0.other_capacity * 2 deposit_transaction_1 = TransactionChannelDeposit( app1.raiden.address, TokenAmount(new_total_deposit_1), chain_state.block_number) channel_deposit_1 = ContractReceiveChannelDeposit( transaction_hash=make_transaction_hash(), canonical_identifier=canonical_identifier, deposit_transaction=deposit_transaction_1, block_number=chain_state.block_number, block_hash=chain_state.block_hash, fee_config=MediationFeeConfig(), ) new_total_deposit_2 = new_total_deposit_1 * 2 deposit_transaction_2 = TransactionChannelDeposit( app1.raiden.address, TokenAmount(new_total_deposit_2), chain_state.block_number) channel_deposit_2 = ContractReceiveChannelDeposit( transaction_hash=make_transaction_hash(), canonical_identifier=canonical_identifier, deposit_transaction=deposit_transaction_2, block_number=chain_state.block_number, block_hash=chain_state.block_hash, fee_config=MediationFeeConfig(), ) state_changes = [channel_deposit_1, channel_deposit_2] app0.raiden.handle_state_changes(state_changes=state_changes) # Now we should see that app0 send 2 new messages, # one PFSCapacityUpdate and one PFSFeeUpdate with # the updated amount of the initial amount * 4 # so sending should only be triggered by the second state change pfs_capacity_update_3_of_app0 = get_messages(app0)[4] assert isinstance(pfs_capacity_update_3_of_app0, PFSCapacityUpdate) assert len(get_messages(app0)) == 6 assert (pfs_capacity_update_3_of_app0.other_capacity == pfs_capacity_update_2_of_app0.updating_capacity * 4) # Now we want to test if this also works with a state_change # that triggers only a PFSCapacityUpdate and no PFSFeeUpdate. # So at the end we expect 1 more PFSCapacityUpdate in the room. lock_secret_1 = keccak(b"test_end_state") unlock_message_1 = Unlock( chain_id=chain_state.chain_id, message_identifier=MessageID(123132), payment_identifier=PaymentID(1), nonce=Nonce(2), token_network_address=canonical_identifier.token_network_address, channel_identifier=canonical_identifier.channel_identifier, transferred_amount=TokenAmount(400), locked_amount=LockedAmount(0), locksroot=Locksroot(keccak(b"")), secret=Secret(lock_secret_1), signature=Signature(bytes(65)), ) unlock_message_1.sign(app1.raiden.signer) balance_proof_1 = balanceproof_from_envelope(unlock_message_1) unlock_1 = ReceiveUnlock( message_identifier=MessageID(5135), secret=Secret(lock_secret_1), balance_proof=balance_proof_1, sender=balance_proof_1.sender, ) lock_secret_2 = keccak(b"test_end_state_again") unlock_message_2 = Unlock( chain_id=chain_state.chain_id, message_identifier=MessageID(223132), payment_identifier=PaymentID(2), nonce=Nonce(2), token_network_address=canonical_identifier.token_network_address, channel_identifier=canonical_identifier.channel_identifier, transferred_amount=TokenAmount(500), locked_amount=LockedAmount(0), locksroot=Locksroot(keccak(b"")), secret=Secret(lock_secret_2), signature=Signature(bytes(65)), ) unlock_message_2.sign(app1.raiden.signer) balance_proof_2 = balanceproof_from_envelope(unlock_message_2) unlock_2 = ReceiveUnlock( message_identifier=MessageID(5135), secret=Secret(lock_secret_2), balance_proof=balance_proof_2, sender=balance_proof_2.sender, ) state_changes_2 = [unlock_1, unlock_2] app0.raiden.handle_state_changes(state_changes=state_changes_2) assert len(get_messages(app0)) == 7 assert len([ x for x in get_messages(app0) if isinstance(x, PFSCapacityUpdate) ]) == 4