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
Example #2
0
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,
        )
Example #4
0
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)
Example #7
0
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
Example #9
0
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,
        [],
    )
Example #13
0
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,
        )
Example #15
0
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,
        )
Example #16
0
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
Example #17
0
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)
Example #18
0
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)
Example #19
0
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)
Example #20
0
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, [])
Example #21
0
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, [],
    )
Example #23
0
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,
    )
Example #24
0
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,
        )
Example #26
0
    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,
        )
Example #27
0
    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,
        )
Example #28
0
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, [])
Example #30
0
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,
    )
Example #31
0
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
Example #32
0
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)
Example #34
0
def direct_transfer(draw, token, recipient, locksroot):
    return DirectTransfer(
        draw(identifier),
        draw(nonce),
        draw(token),
        draw(transferred_amount),
        draw(recipient),
        draw(locksroot),
    )
Example #35
0
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
Example #36
0
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)
Example #37
0
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,
        )
Example #38
0
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,
        [],
    )
Example #39
0
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
Example #40
0
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])))
Example #41
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)
Example #42
0
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
Example #44
0
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')
Example #45
0
    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']