def test_recoverAddressFromSignature(tester_chain, tester_nettingchannel_library_address): auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) privkey, address = make_privkey_address() message_identifier = random.randint(0, UINT64_MAX) msg = DirectTransfer(message_identifier=message_identifier, payment_identifier=1, nonce=1, registry_address='x' * 20, token='x' * 20, channel=auxiliary.address, transferred_amount=10, recipient='y' * 20, locksroot=HASH) msg.sign(privkey, address) data = msg.encode() signature = data[-65:] extra_hash = sha3(data[:-65]) computed_address = auxiliary.recoverAddressFromSignature( msg.nonce, msg.transferred_amount, msg.locksroot, extra_hash, signature) assert normalize_address(computed_address) == msg.sender
def test_update_must_fail_with_a_nonparticipant_transfer( tester_channels, private_keys): """ updateTransfer must not accept a transfer from a non participant. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] nonparticipant_key = private_keys[2] # make a transfer where pkey1 is the target transfer_nonparticipant = DirectTransfer( identifier=1, nonce=1, token=channel0.token_address, transferred_amount=10, recipient=channel1.our_address, locksroot='', ) nonparticipant_address = privatekey_to_address(nonparticipant_key) nonparticipant_sign_key = PrivateKey(nonparticipant_key, ctx=GLOBAL_CTX, raw=True) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data) nettingchannel.close('', sender=pkey0) with pytest.raises(TransactionFailed): nettingchannel.updateTransfer(transfer_nonparticipant_data, sender=pkey1)
def test_close_accepts_only_transfer_from_participants(tester_channels, private_keys): """ Close must not accept a transfer signed by a non participant. """ pkey0, _, nettingchannel, channel0, _ = tester_channels[0] nonparticipant_key = private_keys[2] opened_block = nettingchannel.opened(sender=pkey0) # make a transfer where pkey0 is the target transfer_nonparticipant = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, channel=channel0.channel_address, transferred_amount=10, recipient=channel0.our_address, locksroot=EMPTY_MERKLE_ROOT, ) nonparticipant_address = privatekey_to_address(nonparticipant_key) nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) transfer_nonparticipant_hash = sha3(transfer_nonparticipant.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( transfer_nonparticipant.nonce, transfer_nonparticipant.transferred_amount, transfer_nonparticipant.locksroot, transfer_nonparticipant_hash, transfer_nonparticipant.signature, sender=pkey0, )
def test_update_must_fail_with_a_wrong_recipient(tester_channels, private_keys): """ updateTransfer must not accept a transfer from a non participant. """ pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) nonparticipant_address = privatekey_to_address(private_keys[2]) # make a transfer where pkey1 is the target transfer_wrong_recipient = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2**32)), token=channel0.token_address, transferred_amount=10, recipient=nonparticipant_address, locksroot='', ) our_address = privatekey_to_address(pkey0) our_sign_key = PrivateKey(pkey0) transfer_wrong_recipient.sign(our_sign_key, our_address) transfer_wrong_recipient_data = str(transfer_wrong_recipient.packed().data) nettingchannel.close('', sender=pkey0) with pytest.raises(TransactionFailed): nettingchannel.updateTransfer(transfer_wrong_recipient_data, sender=pkey1)
def test_close_wrong_channel(tester_channels): """ Close must not accept a transfer aimed at a different channel. """ pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) wrong_address = make_address() # make a transfer where the recipient is totally wrong transfer_wrong_channel = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, channel=wrong_address, transferred_amount=10, recipient=channel0.our_address, locksroot=EMPTY_MERKLE_ROOT, ) transfer_wrong_channel.sign(PrivateKey(pkey1), privatekey_to_address(pkey1)) transfer_wrong_channel_hash = sha3(transfer_wrong_channel.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( transfer_wrong_channel.nonce, transfer_wrong_channel.transferred_amount, transfer_wrong_channel.locksroot, transfer_wrong_channel_hash, transfer_wrong_channel.signature, sender=pkey0, )
def test_signature_split(tester_chain, tester_nettingchannel_library_address): auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) privkey, address = make_privkey_address() message_identifier = random.randint(0, UINT64_MAX) msg = DirectTransfer(message_identifier=message_identifier, payment_identifier=1, nonce=1, registry_address='x' * 20, token='x' * 20, channel=auxiliary.address, transferred_amount=10, recipient='y' * 20, locksroot=HASH) msg.sign(privkey, address) msg = msg.encode() # signature = len(msg) - 65 signature = msg[len(msg) - 65:] signature = signature[:-1] + chr(27).encode() r, s, v = auxiliary.signatureSplit(signature) assert v == 27 assert r == signature[:32] assert s == signature[32:64] signature = signature[:-1] + chr(28).encode() _, _, v = auxiliary.signatureSplit(signature) assert v == 28 with pytest.raises(TransactionFailed): signature = signature[:-1] + chr(4).encode() r, s, v = auxiliary.signatureSplit(signature)
def test_close_wrong_channel(tester_channels): """ Close must not accept a transfer aimed at a different channel. """ pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) wrong_address = make_address() # make a transfer where the recipient is totally wrong transfer_wrong_channel = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2**32)), token=channel0.token_address, channel=wrong_address, transferred_amount=10, recipient=channel0.our_state.address, locksroot=EMPTY_MERKLE_ROOT, ) transfer_wrong_channel.sign(PrivateKey(pkey1), privatekey_to_address(pkey1)) transfer_wrong_channel_hash = sha3( transfer_wrong_channel.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( transfer_wrong_channel.nonce, transfer_wrong_channel.transferred_amount, transfer_wrong_channel.locksroot, transfer_wrong_channel_hash, transfer_wrong_channel.signature, sender=pkey0, )
def test_recoverAddressFromSignature(tester_chain, tester_nettingchannel_library_address): auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) privkey, address = make_privkey_address() msg = DirectTransfer( identifier=1, nonce=1, token='x' * 20, channel=auxiliary.address, transferred_amount=10, recipient='y' * 20, locksroot=HASH ) msg.sign(privkey, address) data = msg.encode() signature = data[-65:] extra_hash = sha3(data[:-65]) computed_address = auxiliary.recoverAddressFromSignature( msg.nonce, msg.transferred_amount, msg.locksroot, extra_hash, signature ) assert normalize_address(computed_address) == msg.sender
def test_close_accepts_only_transfer_from_participants(tester_channels, private_keys): """ Close must not accept a transfer from a non participant. """ pkey0, _, nettingchannel, channel0, _ = tester_channels[0] nonparticipant_key = private_keys[2] opened_block = nettingchannel.opened(sender=pkey0) # make a transfer where pkey0 is the target transfer_nonparticipant = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2**32)), token=channel0.token_address, transferred_amount=10, recipient=channel0.our_address, locksroot='', ) nonparticipant_address = privatekey_to_address(nonparticipant_key) nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data) with pytest.raises(TransactionFailed): nettingchannel.close(transfer_nonparticipant_data, sender=pkey0)
def test_signature_split(tester_state, tester_nettingchannel_library_address): auxiliary = deploy_auxiliary_tester(tester_state, tester_nettingchannel_library_address) privkey, address = make_privkey_address() msg = DirectTransfer(identifier=1, nonce=1, token='x' * 20, transferred_amount=10, recipient='y' * 20, locksroot=HASH) msg.sign(privkey, address) msg = msg.encode() # signature = len(msg) - 65 signature = msg[len(msg) - 65:] signature = signature[:-1] + chr(27) r, s, v = auxiliary.signatureSplit(signature) assert v == 27 assert r == signature[:32] assert s == signature[32:64] signature = signature[:-1] + chr(28) _, _, v = auxiliary.signatureSplit(signature) assert v == 28 with pytest.raises(TransactionFailed): signature = signature[:-1] + chr(4) r, s, v = auxiliary.signatureSplit(signature)
def test_signature_split(tester_chain, tester_nettingchannel_library_address): auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) privkey, address = make_privkey_address() msg = DirectTransfer( identifier=1, nonce=1, token='x' * 20, channel=auxiliary.address, transferred_amount=10, recipient='y' * 20, locksroot=HASH ) msg.sign(privkey, address) msg = msg.encode() # signature = len(msg) - 65 signature = msg[len(msg) - 65:] signature = signature[:-1] + chr(27).encode() r, s, v = auxiliary.signatureSplit(signature) assert v == 27 assert r == signature[:32] assert s == signature[32:64] signature = signature[:-1] + chr(28).encode() _, _, v = auxiliary.signatureSplit(signature) assert v == 28 with pytest.raises(TransactionFailed): signature = signature[:-1] + chr(4).encode() r, s, v = auxiliary.signatureSplit(signature)
def test_receive_directtransfer_invalidnonce(raiden_network, deposit, token_addresses): app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] channel0 = get_channelstate(app0, app1, token_address) transferred_amount = 10 same_payment_identifier = 1 message_identifier = random.randint(0, UINT64_MAX) event = channel.send_directtransfer( registry_address, channel0, transferred_amount, message_identifier, same_payment_identifier, ) direct_transfer_message = DirectTransfer.from_event(event) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # Send a *different* direct transfer with the *same nonce* invalid_transferred_amount = transferred_amount // 2 message_identifier = random.randint(0, UINT64_MAX) invalid_direct_transfer_message = DirectTransfer( message_identifier=message_identifier, payment_identifier=same_payment_identifier, nonce=1, registry_address=registry_address, token=token_address, channel=channel0.identifier, transferred_amount=invalid_transferred_amount, recipient=app1.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( invalid_direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_address, app0, deposit - transferred_amount, [], app1, deposit + transferred_amount, [], )
def test_update_must_fail_with_a_channel_address(tester_channels): """ updateTransfer must not accept a transfer signed with the wrong channel address. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) wrong_channel = factories.make_address() # make a transfer where pkey1 is the target transfer_wrong_recipient = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2**32)), token=channel0.token_address, channel=wrong_channel, transferred_amount=10, recipient=channel1.our_state.address, locksroot=EMPTY_MERKLE_ROOT, ) our_address = privatekey_to_address(pkey0) our_sign_key = PrivateKey(pkey0) transfer_wrong_recipient.sign(our_sign_key, our_address) nettingchannel.close(sender=pkey0) transfer_wrong_recipient_hash = sha3( transfer_wrong_recipient.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.updateTransfer( transfer_wrong_recipient.nonce, transfer_wrong_recipient.transferred_amount, transfer_wrong_recipient.locksroot, transfer_wrong_recipient_hash, transfer_wrong_recipient.signature, sender=pkey1, )
def test_update_must_fail_with_a_channel_address(tester_channels, private_keys): """ updateTransfer must not accept a transfer signed with the wrong channel address. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) wrong_channel = make_address() # make a transfer where pkey1 is the target transfer_wrong_recipient = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, channel=wrong_channel, transferred_amount=10, recipient=channel1.our_address, locksroot=EMPTY_MERKLE_ROOT, ) our_address = privatekey_to_address(pkey0) our_sign_key = PrivateKey(pkey0) transfer_wrong_recipient.sign(our_sign_key, our_address) nettingchannel.close(sender=pkey0) transfer_wrong_recipient_hash = sha3(transfer_wrong_recipient.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.updateTransfer( transfer_wrong_recipient.nonce, transfer_wrong_recipient.transferred_amount, transfer_wrong_recipient.locksroot, transfer_wrong_recipient_hash, transfer_wrong_recipient.signature, sender=pkey1, )
def test_close_accepts_only_transfer_from_participants(tester_channels, private_keys): """ Close must not accept a transfer signed by a non participant. """ pkey0, _, nettingchannel, channel0, _ = tester_channels[0] nonparticipant_key = private_keys[2] opened_block = nettingchannel.opened(sender=pkey0) # make a transfer where pkey0 is the target transfer_nonparticipant = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2**32)), token=channel0.token_address, channel=channel0.identifier, transferred_amount=10, recipient=channel0.our_state.address, locksroot=EMPTY_MERKLE_ROOT, ) nonparticipant_address = privatekey_to_address(nonparticipant_key) nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) transfer_nonparticipant_hash = sha3( transfer_nonparticipant.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( transfer_nonparticipant.nonce, transfer_nonparticipant.transferred_amount, transfer_nonparticipant.locksroot, transfer_nonparticipant_hash, transfer_nonparticipant.signature, sender=pkey0, )
def make_receive_transfer_direct( payment_network_identifier, channel_state, privkey, nonce, transferred_amount, locksroot=EMPTY_MERKLE_ROOT): address = privatekey_to_address(privkey.secret) if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError('Private key does not match any of the participants.') identifier = nonce mediated_transfer_msg = DirectTransfer( identifier, nonce, channel_state.token_address, channel_state.identifier, transferred_amount, channel_state.partner_state.address, locksroot, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_directtransfer = ReceiveTransferDirect( payment_network_identifier, channel_state.token_address, identifier, balance_proof, ) return receive_directtransfer
def test_direct_transfer(iterations=ITERATIONS): nonce = 1 asset = ADDRESS balance = 1 recipient = ADDRESS locksroot = HASH msg = DirectTransfer(nonce, asset, balance, recipient, locksroot) msg.sign(PRIVKEY) run_timeit('DirectTransfer', msg, iterations=iterations)
def test_decode_transfer(): bad_encoded_data = '0500000000000000000000010bd4060688a1800ae986e4840aebc924bb40b5bf3893263bf8b2d0373a34b8d359c5edd823110747000000000000000000000000000000000000000000000000000000000000000160d09b4687c162154b290ee5fcbd7c6285590969b3c873e94b690ee9c4f5df510000000000000000000000000000000000000000000000000000000000000000ff9636ccb66e73219fd166cd6ffbc9c6215f74ff31c1fd4131cf532b29ee096f65278c459253fba65bf019c723a68bb4a6153ea8378cd1b15d55825e1a291b6f0001' bad_data = bad_encoded_data.decode('hex') nonce = 1 asset = ASSET_ADDRESS balance = 1 recipient = RECIPIENT_ADDRESS locksroot = LOCKSROOT msg = DirectTransfer( nonce, asset, balance, recipient, locksroot, ).sign(INITIATOR_PRIVKEY) packed = msg.packed() data = str(packed.data) s = tester.state() c = s.abi_contract(None, path=decoder_path, language="solidity") o1 = c.decodeTransfer1(data) o2 = c.decodeTransfer2(data) assert data[0] == '\x05' # make sure data has right cmdid assert len(data) == 213 cmd_id_pad = o1[0] assert cmd_id_pad == data[:4] nonce = o1[1] assert nonce == packed.nonce asset = o1[2] assert asset == str(packed.asset).encode('hex') recipient = o1[3] assert len(recipient) == 40 assert recipient == str(packed.recipient).encode('hex') transfered_amount = o1[4] assert transfered_amount == packed.transfered_amount optionalLocksroot = o2[0] assert optionalLocksroot == str(packed.locksroot) optionalSecret = o2[1] assert optionalSecret == '0000000000000000000000000000000000000000000000000000000000000000'.decode('hex') signature = str(packed.signature) r = o2[2] s = o2[3] v = o2[4] assert r == signature[:32] assert s == signature[32:64] assert v == int(signature[64].encode('hex')) + 27 with pytest.raises(TransactionFailed): c.decodeSecret(bad_data)
def test_receive_directtransfer_invalidlocksroot(raiden_network, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] channel0 = get_channelstate(app0, app1, token_address) balance0 = channel.get_balance(channel0.our_state, channel0.partner_state) balance1 = channel.get_balance(channel0.partner_state, channel0.our_state) payment_identifier = 1 invalid_locksroot = UNIT_SECRETHASH channel_identifier = channel0.identifier message_identifier = random.randint(0, UINT64_MAX) direct_transfer_message = DirectTransfer( message_identifier=message_identifier, payment_identifier=payment_identifier, nonce=1, token=token_address, channel=channel_identifier, transferred_amount=0, recipient=app1.raiden.address, locksroot=invalid_locksroot, ) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state(token_address, app0, balance0, [], app1, balance1, [])
def test_close_tampered_identifier(tester_channels): """ Messages with a tampered identifier must be rejected. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] payment_network_identifier = factories.make_address() transfer0 = make_direct_transfer_from_channel( payment_network_identifier, channel0, channel1, amount=90, pkey=pkey0, ) transfer0_data = transfer0.encode() tampered_transfer = DirectTransfer.decode(transfer0_data) tampered_transfer.payment_identifier += 1 tampered_transfer_hash = sha3(tampered_transfer.encode()[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( tampered_transfer.nonce, tampered_transfer.transferred_amount, tampered_transfer.locksroot, tampered_transfer_hash, tampered_transfer.signature, sender=pkey1, )
def test_receive_directtransfer_invalidnonce(raiden_network, deposit, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) transferred_amount = 10 same_payment_identifier = 1 message_identifier = random.randint(0, UINT64_MAX) event = channel.send_directtransfer( channel0, transferred_amount, message_identifier, same_payment_identifier, ) direct_transfer_message = DirectTransfer.from_event(event) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # Send a *different* direct transfer with the *same nonce* invalid_transferred_amount = transferred_amount // 2 message_identifier = random.randint(0, UINT64_MAX) invalid_direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=same_payment_identifier, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=invalid_transferred_amount, locked_amount=0, recipient=app1.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( invalid_direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_network_identifier, app0, deposit - transferred_amount, [], app1, deposit + transferred_amount, [], )
def make_direct_transfer( message_identifier=None, payment_identifier=0, nonce=1, registry_address=ADDRESS, token=ADDRESS, channel_identifier=UNIT_CHANNEL_ID, transferred_amount=0, locked_amount=0, recipient=ADDRESS, locksroot=EMPTY_MERKLE_ROOT, ): if message_identifier is None: message_identifier = random.randint(0, UINT64_MAX) return DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=payment_identifier, nonce=nonce, token_network_address=registry_address, token=token, channel_identifier=channel_identifier, transferred_amount=transferred_amount, locked_amount=locked_amount, recipient=recipient, locksroot=locksroot, )
def test_transfer_from_outdated(raiden_network, settle_timeout): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking graph0 = app0.raiden.token_to_channelgraph.values()[0] graph1 = app1.raiden.token_to_channelgraph.values()[0] channel0 = graph0.partneraddress_to_channel[app1.raiden.address] channel1 = graph1.partneraddress_to_channel[app0.raiden.address] balance0 = channel0.balance balance1 = channel1.balance assert graph0.token_address == graph1.token_address assert app1.raiden.address in graph0.partneraddress_to_channel amount = 10 result = app0.raiden.transfer_async( graph0.token_address, amount, target=app1.raiden.address, ) assert result.wait(timeout=10) assert_synched_channels(channel0, balance0 - amount, [], channel1, balance1 + amount, []) channel1.external_state.close(channel1.received_transfers[-1], ) wait_until_block(app1.raiden.chain, app1.raiden.chain.block_number() + 1) assert channel0.external_state.close_event.wait(timeout=25) assert channel1.external_state.close_event.wait(timeout=25) assert channel0.external_state.closed_block != 0 assert channel1.external_state.closed_block != 0 wait_until_block( app0.raiden.chain, app0.raiden.chain.block_number() + settle_timeout, ) assert channel0.external_state.settle_event.wait(timeout=25) assert channel1.external_state.settle_event.wait(timeout=25) assert channel0.external_state.settled_block != 0 assert channel1.external_state.settled_block != 0 # and now receive one more transfer from the closed channel direct_transfer_message = DirectTransfer( identifier=1, nonce=1, token=graph0.token_address, channel=channel0.channel_address, transferred_amount=10, recipient=app0.raiden.address, locksroot=UNIT_HASHLOCK, ) sign_and_send(direct_transfer_message, app1.raiden.private_key, app1.raiden.address, app1)
def test_close_tampered_nonce(tester_chain, tester_channels): """ Messages with a tampered nonce must be rejected. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] block_number = tester_chain.block.number transfer0 = make_direct_transfer_from_channel( block_number, channel0, channel1, amount=90, pkey=pkey0, ) transfer0_data = transfer0.encode() tampered_transfer = DirectTransfer.decode(transfer0_data) tampered_transfer.nonce += 1 tampered_transfer_hash = sha3(tampered_transfer.encode()[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( tampered_transfer.nonce, tampered_transfer.transferred_amount, tampered_transfer.locksroot, tampered_transfer_hash, tampered_transfer.signature, sender=pkey1, )
def create_directtransfer(self, amount, identifier): """ Return a DirectTransfer message. This message needs to be signed and registered with the channel before sent. """ if not self.isopen: raise ValueError('The channel is closed') from_ = self.our_state to_ = self.partner_state distributable = from_.distributable(to_) if amount <= 0 or amount > distributable: log.debug( 'Insufficient funds', amount=amount, distributable=distributable, ) raise ValueError('Insufficient funds') transferred_amount = from_.transferred_amount + amount current_locksroot = to_.balance_proof.merkleroot_for_unclaimed() return DirectTransfer( identifier=identifier, nonce=from_.nonce, asset=self.asset_address, transferred_amount=transferred_amount, recipient=to_.address, locksroot=current_locksroot, )
def create_directtransfer(self, amount, secret=None): """ Return a DirectTransfer message. This message needs to be signed and registered with the channel before sent. """ if not self.isopen: raise ValueError('The channel is closed') from_ = self.our_state to_ = self.partner_state distributable = from_.distributable(to_) if amount <= 0 or amount > distributable: log.debug( 'Insufficient funds', amount=amount, distributable=distributable, ) raise ValueError('Insufficient funds') # start of critical read section transfered_amount = from_.transfered_amount + amount current_locksroot = to_.locked.root # end of critical read section return DirectTransfer( nonce=from_.nonce, asset=self.asset_address, transfered_amount=transfered_amount, recipient=to_.address, locksroot=current_locksroot, secret=secret, )
def test_close_tampered_nonce(tester_channels): """ Messages with a tampered nonce must be rejected. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] transfer0 = make_direct_transfer_from_channel( channel0, channel1, amount=90, pkey=pkey0, ) transfer0_data = transfer0.encode() tampered_transfer = DirectTransfer.decode(transfer0_data) tampered_transfer.nonce += 1 tampered_transfer_hash = sha3(tampered_transfer.encode()[:-65]) with pytest.raises(TransactionFailed): nettingchannel.close( tampered_transfer.nonce, tampered_transfer.transferred_amount, tampered_transfer.locksroot, tampered_transfer_hash, tampered_transfer.signature, sender=pkey1, )
def test_receive_directtransfer_invalidsender(raiden_network, deposit, token_addresses): app0, app1 = raiden_network registry = app0.raiden.default_registry.address token_address = token_addresses[0] other_key, other_address = make_privkey_address() channel0 = get_channelstate(app0, app1, token_address) channel_identifier = channel0.identifier message_identifier = random.randint(0, UINT64_MAX) direct_transfer_message = DirectTransfer( message_identifier=message_identifier, payment_identifier=1, nonce=1, registry_address=registry, token=token_address, channel=channel_identifier, transferred_amount=10, recipient=app0.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( direct_transfer_message, other_key, other_address, app0, ) assert_synched_channel_state(token_address, app0, deposit, [], app1, deposit, [])
def make_direct_transfer( message_identifier=None, payment_identifier=0, nonce=1, registry_address=ADDRESS, token=ADDRESS, channel=ADDRESS, transferred_amount=0, locked_amount=0, recipient=ADDRESS, locksroot=EMPTY_MERKLE_ROOT, ): if message_identifier is None: message_identifier = random.randint(0, UINT64_MAX) return DirectTransfer( message_identifier, payment_identifier, nonce, registry_address, token, channel, transferred_amount, locked_amount, recipient, locksroot, )
def make_receive_transfer_direct( payment_network_identifier, channel_state, privkey, nonce, transferred_amount, locksroot=EMPTY_MERKLE_ROOT, registry_address=UNIT_REGISTRY_IDENTIFIER, locked_amount=None, ): address = privatekey_to_address(privkey.secret) if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError('Private key does not match any of the participants.') if locked_amount is None: locked_amount = channel.get_amount_locked(channel_state.our_state) message_identifier = random.randint(0, UINT64_MAX) payment_identifier = nonce mediated_transfer_msg = DirectTransfer( message_identifier, payment_identifier, nonce, registry_address, channel_state.token_address, channel_state.identifier, transferred_amount, locked_amount, channel_state.partner_state.address, locksroot, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_directtransfer = ReceiveTransferDirect( payment_network_identifier, channel_state.token_address, message_identifier, payment_identifier, balance_proof, ) return receive_directtransfer
def handle_send_directtransfer( raiden: 'RaidenService', send_direct_transfer: SendDirectTransfer): direct_transfer_message = DirectTransfer.from_event(send_direct_transfer) raiden.sign(direct_transfer_message) raiden.send_async( send_direct_transfer.recipient, direct_transfer_message, )
def test_direct_transfer(iterations=ITERATIONS): identifier = 1 nonce = 1 token = ADDRESS balance = 1 recipient = ADDRESS locksroot = HASH msg = DirectTransfer( identifier, nonce, token, balance, recipient, locksroot, ) msg.sign(PRIVKEY, ADDRESS) run_timeit('DirectTransfer', msg, iterations=iterations)
def direct_transfer(draw, token, recipient, locksroot): return DirectTransfer( draw(identifier), draw(nonce), draw(token), draw(transferred_amount), draw(recipient), draw(locksroot), )
def test_direct_transfer_min_max(payment_identifier, nonce, transferred_amount): direct_transfer = make_direct_transfer( payment_identifier=payment_identifier, nonce=nonce, transferred_amount=transferred_amount, ) direct_transfer.sign(PRIVKEY) assert DirectTransfer.from_dict(direct_transfer.to_dict()) == direct_transfer
def test_update_must_fail_with_a_nonparticipant_transfer( tester_registry_address, tester_channels, private_keys, ): """ updateTransfer must not accept a transfer from a non participant. """ pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] nonparticipant_key = private_keys[2] opened_block = nettingchannel.opened(sender=pkey0) # make a transfer where pkey1 is the target message_identifier = random.randint(0, UINT64_MAX) transfer_nonparticipant = DirectTransfer( message_identifier=message_identifier, payment_identifier=1, nonce=1 + (opened_block * (2**32)), registry_address=tester_registry_address, token=channel0.token_address, channel=channel0.identifier, transferred_amount=10, locked_amount=0, recipient=channel1.our_state.address, locksroot=EMPTY_MERKLE_ROOT, ) nonparticipant_address = privatekey_to_address(nonparticipant_key) nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) nettingchannel.close(sender=pkey0) transfer_nonparticipant_hash = sha3( transfer_nonparticipant.packed().data[:-65]) with pytest.raises(TransactionFailed): nettingchannel.updateTransfer( transfer_nonparticipant.nonce, transfer_nonparticipant.transferred_amount, transfer_nonparticipant.locksroot, transfer_nonparticipant_hash, transfer_nonparticipant.signature, sender=pkey1, )
def test_received_directtransfer_closedchannel(raiden_network, token_addresses, deposit): app0, app1 = raiden_network token_address = token_addresses[0] registry_address = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), registry_address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) wait_until_block( app0.raiden.chain, app0.raiden.chain.block_number() + 1, ) # Now receive one direct transfer for the closed channel message_identifier = random.randint(0, UINT64_MAX) direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=1, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=10, locked_amount=0, recipient=app0.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # The local state must not change since the channel is already closed assert_synched_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
def test_direct_transfer_min_max(identifier, nonce, transferred_amount): direct_transfer = make_direct_transfer( identifier=identifier, nonce=nonce, transferred_amount=transferred_amount, ) direct_transfer.sign(PRIVKEY, ADDRESS) assert DirectTransfer.from_dict( direct_transfer.to_dict()) == direct_transfer
def decode_transfer(transfer_encoded): if transfer_encoded[0] == DIRECTTRANSFER: return DirectTransfer.decode(transfer_encoded) elif transfer_encoded[0] == MEDIATEDTRANSFER: return MediatedTransfer.decode(transfer_encoded) elif transfer_encoded[0] == REFUNDTRANSFER: return RefundTransfer.decode(transfer_encoded) else: raise ValueError('invalid transfer type {}'.format( type(transfer_encoded[0])))
def test_transfer_from_outdated(raiden_network, settle_timeout): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking token_manager0 = app0.raiden.managers_by_token_address.values()[0] token_manager1 = app1.raiden.managers_by_token_address.values()[0] channel0 = token_manager0.partneraddress_channel[app1.raiden.address] channel1 = token_manager1.partneraddress_channel[app0.raiden.address] balance0 = channel0.balance balance1 = channel1.balance assert token_manager0.token_address == token_manager1.token_address assert app1.raiden.address in token_manager0.partneraddress_channel amount = 10 app0.raiden.api.transfer( token_manager0.token_address, amount, target=app1.raiden.address, ) assert_synched_channels(channel0, balance0 - amount, [], channel1, balance1 + amount, []) app1.raiden.api.close(token_manager0.token_address, app0.raiden.address) wait_until_block(app1.raiden.chain, app1.raiden.chain.block_number() + 1) assert channel0.close_event.wait(timeout=25) assert channel1.close_event.wait(timeout=25) assert channel0.external_state.closed_block != 0 assert channel1.external_state.closed_block != 0 wait_until_block( app0.raiden.chain, app0.raiden.chain.block_number() + settle_timeout, ) assert channel0.settle_event.wait(timeout=25) assert channel1.settle_event.wait(timeout=25) assert channel0.external_state.settled_block != 0 assert channel1.external_state.settled_block != 0 # and now receive one more transfer from the closed channel direct_transfer = DirectTransfer(identifier=1, nonce=1, token=token_manager0.token_address, transferred_amount=10, recipient=app0.raiden.address, locksroot=HASH) sign_and_send(direct_transfer, app1.raiden.private_key, app1.raiden.address, app1)
def decode_transfer(transfer_encoded): if transfer_encoded[0] == DIRECTTRANSFER: return DirectTransfer.decode(transfer_encoded) elif transfer_encoded[0] == MEDIATEDTRANSFER: return MediatedTransfer.decode(transfer_encoded) elif transfer_encoded[0] == CANCELTRANSFER: return CancelTransfer.decode(transfer_encoded) # convinience for testing only (LockedTransfer are not exchanged between nodes) elif transfer_encoded[0] == LOCKEDTRANSFER: return LockedTransfer.decode(transfer_encoded) else: raise ValueError('invalid transfer type {}'.format(type(transfer_encoded[0])))
def test_decode_direct_transfer( private_keys, settle_timeout, tester_state, tester_token, tester_events, tester_registry): privatekey0 = tester.DEFAULT_KEY privatekey1 = private_keys[1] address0 = privatekey_to_address(privatekey0) address1 = privatekey_to_address(privatekey1) dtester = deploy_decoder_tester(tester_token.address, address0, address1, settle_timeout) locksroot = sha3("Waldemarstr") message = DirectTransfer( identifier=1, nonce=2, asset=tester_token.address, transferred_amount=1337, recipient=address1, locksroot=locksroot ) message.sign(PrivateKey(privatekey0, ctx=GLOBAL_CTX, raw=True), address0) _, publickey = wrap_and_validate(message.encode()) recovered_address = address_from_key(publickey) assert recovered_address == address0 assert dtester.testDecodeTransfer(message.encode()) is True assert dtester.decodedNonce() == 2 assert dtester.decodedAsset() == tester_token.address.encode('hex') assert dtester.decodedRecipient() == address1.encode('hex') assert dtester.decodedAmount() == 1337 assert dtester.decodedLocksroot() == locksroot
def test_ncc(): token_library_path = get_contract_path('StandardToken.sol') token_path = get_contract_path('HumanStandardToken.sol') s = tester.state() assert s.block.number < 1150000 s.block.number = 1158001 assert s.block.number > 1150000 # Token creation lib_token = s.abi_contract(None, path=token_library_path, language="solidity") token = s.abi_contract(None, path=token_path, language="solidity", libraries={'StandardToken': lib_token.address.encode('hex')}, constructor_parameters=[10000, "raiden", 0, "rd"]) # Getter creation lib_getter = s.abi_contract(None, path=decode_lib, language="solidity") getter = s.abi_contract(None, path=getter_path, language="solidity", libraries={'Decoder': lib_getter.address.encode('hex')}) INITIATOR_PRIVKEY = tester.k0 INITIATOR_ADDRESS = privtoaddr(INITIATOR_PRIVKEY) RECIPIENT_PRIVKEY = tester.k1 RECIPIENT_ADDRESS = privtoaddr(RECIPIENT_PRIVKEY) ASSET_ADDRESS = token.address HASHLOCK = sha3(INITIATOR_PRIVKEY) LOCK_AMOUNT = 29 LOCK_EXPIRATION = 31 LOCK = Lock(LOCK_AMOUNT, LOCK_EXPIRATION, HASHLOCK) LOCKSROOT = merkleroot([ sha3(LOCK.as_bytes), ]) # print direct_transfer.encode('hex') nonce = 1 asset = ASSET_ADDRESS balance = 1 recipient = RECIPIENT_ADDRESS locksroot = LOCKSROOT msg = DirectTransfer( nonce, asset, balance, recipient, locksroot, ).sign(INITIATOR_PRIVKEY) packed = msg.packed() direct_transfer = str(packed.data) # pure python recover sen = recover_publickey(direct_transfer[:148], str(packed.signature)) assert address_from_key(sen) == tester.a0 # addr = getter.ecTest(direct_transfer[:148], sig) # assert addr == INITIATOR_ADDRESS.encode('hex') sender = getter.getSender(direct_transfer) assert sender == tester.a0.encode('hex') # with sigSplit directly in Getters.sol r, s, v = getter.sigSplit(str(packed.signature)) assert r == str(packed.signature[:32]) assert s == str(packed.signature[32:64]) assert v == packed.signature[64] + 27 sender = getter.getSender(direct_transfer) assert sender == tester.a0.encode('hex')
def close(self, ctx, sender, transfers_encoded, locked_encoded, # noqa merkleproof_encoded, secret): """" Request the closing of the channel. Can be called multiple times. lock period starts with first valid call. Args: sender (address): The sender address. transfers_encoded (List[transfer]): A list of maximum length of 2 containing the transfer encoded using the fixed length format, may be empty. ctx: Block chain state used for mocking. locked_encoded (bin): The Lock to be unlocked. merkleproof_encoded (bin): A proof that the given lock is contained in the latest transfer. The binary data is composed of a single hash at every 4bytes. secret (bin): The secret that unlocks the lock `hashlock = sha3(secret)`. Todo: if challenged, keep track of who provided the last valid answer, punish the wrongdoer here, check that participants only updates their own balance are counted, because they could sign something for the other party to blame it. """ # pylint: disable=too-many-arguments,too-many-locals,too-many-branches # if len(transfers_encoded): # raise ValueError('transfers_encoded needs at least 1 item.') if len(transfers_encoded) > 2: raise ValueError('transfers_encoded cannot have more than 2 items.') if self.settled: raise RuntimeError('contract is settled') # the merkleproof can be empty, if there is only one haslock has_oneofunlocked = locked_encoded or secret has_allofunlocked = locked_encoded and secret if has_oneofunlocked and not has_allofunlocked: raise ValueError( 'all arguments `merkle_proof`, `locked`, and `secret` must be provided' ) last_sent_transfers = [] for data in transfers_encoded: if data[0] == DIRECTTRANSFER: last_sent_transfers.append( DirectTransfer.decode(data) ) elif data[0] == MEDIATEDTRANSFER: last_sent_transfers.append( MediatedTransfer.decode(data) ) elif data[0] == CANCELTRANSFER: last_sent_transfers.append( CancelTransfer.decode(data) ) # convinience for testing only (LockedTransfer are not exchanged between nodes) elif data[0] == LOCKEDTRANSFER: last_sent_transfers.append( LockedTransfer.decode(data) ) else: raise ValueError('invalid transfer type {}'.format(type(data[0]))) # keep the latest claim for transfer in last_sent_transfers: if transfer.sender not in self.participants: raise ValueError('Invalid tansfer, sender is not a participant') sender_state = self.participants[transfer.sender] if is_newer_transfer(transfer, sender_state): sender_state['last_sent_transfer'] = transfer partner = self.partner(sender) partner_state = self.participants[partner] if last_sent_transfers: transfer = last_sent_transfers[-1] # XXX: check me # register un-locked if merkleproof_encoded: merkle_proof = tuple32(merkleproof_encoded) lock = Lock.from_bytes(locked_encoded) hashlock = lock.hashlock if hashlock != sha3(secret): raise ValueError('invalid secret') # the partner might not have made a transfer if partner_state['last_sent_transfer'] is not None: assert check_proof( merkle_proof, partner_state['last_sent_transfer'].locksroot, sha3(transfer.lock.as_bytes), ) partner_state['unlocked'].append(lock) if self.closed is None: log.debug('closing contract', netcontract_address=pex(self.netcontract_address)) self.closed = ctx['block_number']