Ejemplo n.º 1
0
def assert_mirror(channel0, channel1):
    """ Assert that `channel0` has a correct `partner_state` to represent
    `channel1` and vice-versa.
    """
    unclaimed0 = merkleroot(channel0.our_state.merkletree)
    unclaimed1 = merkleroot(channel1.partner_state.merkletree)
    assert unclaimed0 == unclaimed1

    assert channel0.our_state.amount_locked == channel1.partner_state.amount_locked
    assert channel0.transferred_amount == channel1.partner_state.transferred_amount

    balance0 = channel0.our_state.balance(channel0.partner_state)
    balance1 = channel1.partner_state.balance(channel1.our_state)
    assert balance0 == balance1

    assert channel0.distributable == channel0.our_state.distributable(channel0.partner_state)
    assert channel0.distributable == channel1.partner_state.distributable(channel1.our_state)

    unclaimed1 = merkleroot(channel1.our_state.merkletree)
    unclaimed0 = merkleroot(channel0.partner_state.merkletree)
    assert unclaimed1 == unclaimed0

    assert channel1.our_state.amount_locked == channel0.partner_state.amount_locked
    assert channel1.transferred_amount == channel0.partner_state.transferred_amount

    balance1 = channel1.our_state.balance(channel1.partner_state)
    balance0 = channel0.partner_state.balance(channel0.our_state)
    assert balance1 == balance0

    assert channel1.distributable == channel1.our_state.distributable(channel1.partner_state)
    assert channel1.distributable == channel0.partner_state.distributable(channel0.our_state)
Ejemplo n.º 2
0
def test_channel_withdraw_must_not_change_merkletree():
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)
    payment_network_identifier = factories.make_address()

    lock_amount = 10
    lock_expiration = 100
    lock_secret = sha3(b'test_channelstate_mediatedtransfer_overspent')
    lock_secrethash = sha3(lock_secret)
    lock = HashTimeLockState(
        lock_amount,
        lock_expiration,
        lock_secrethash,
    )

    nonce = 1
    transferred_amount = 0
    receive_mediatedtransfer = make_receive_transfer_mediated(
        channel_state,
        privkey2,
        nonce,
        transferred_amount,
        lock,
    )

    is_valid, msg = channel.handle_receive_mediatedtransfer(
        channel_state,
        receive_mediatedtransfer,
    )
    assert is_valid, msg

    assert merkleroot(channel_state.partner_state.merkletree) == lock.lockhash
    assert channel.is_lock_pending(channel_state.partner_state, lock.secrethash)

    closed_block_number = lock_expiration - channel_state.reveal_timeout - 1
    state_change = ContractReceiveChannelClosed(
        payment_network_identifier,
        channel_state.token_address,
        channel_state.identifier,
        partner_model1.participant_address,
        closed_block_number,
    )
    iteration = channel.handle_channel_closed(channel_state, state_change)

    new_channel = iteration.new_state
    withdraw = ContractReceiveChannelWithdraw(
        payment_network_identifier,
        channel_state.token_address,
        channel_state.identifier,
        lock_secret,
        channel_state.our_state.address,
    )
    iteration = channel.handle_channel_withdraw(new_channel, withdraw)

    new_channel = iteration.new_state
    assert merkleroot(new_channel.partner_state.merkletree) == lock.lockhash
    assert not channel.is_lock_pending(new_channel.partner_state, lock.secrethash)
Ejemplo n.º 3
0
    def register_direct_transfer(self, direct_transfer):
        """ Register a direct_transfer.

        Raises:
            InvalidLocksRoot: If the merkleroot of `direct_transfer` does not
            match the current value.
        """
        balance_proof = direct_transfer.to_balanceproof()

        if balance_proof.locksroot != merkleroot(self.merkletree):
            raise InvalidLocksRoot(merkleroot(self.merkletree), balance_proof.locksroot)

        self.balance_proof = balance_proof
    def compute_merkleroot_with(self, include):
        """ Compute the resulting merkle root if the lock `include` is added in
        the tree.
        """
        if not self.is_known(include.hashlock):
            leaves = list(self.merkletree.layers[LEAVES])
            leaves.append(sha3(include.as_bytes))

            tree_with = MerkleTreeState(compute_layers(leaves))
            locksroot = merkleroot(tree_with)
        else:
            locksroot = merkleroot(self.merkletree)

        return locksroot
Ejemplo n.º 5
0
def test_channelstate_get_unlock_proof():
    number_of_transfers = 100
    lock_amounts = cycle([1, 3, 5, 7, 11])
    lock_secrets = [
        make_secret(i)
        for i in range(number_of_transfers)
    ]

    block_number = 1000
    locked_amount = 0
    settle_timeout = 8
    merkletree_leaves = []
    locked_locks = {}
    unlocked_locks = {}

    for lock_amount, lock_secret in zip(lock_amounts, lock_secrets):
        block_number += 1
        locked_amount += lock_amount

        lock_expiration = block_number + settle_timeout
        lock_secrethash = sha3(lock_secret)
        lock = HashTimeLockState(
            lock_amount,
            lock_expiration,
            lock_secrethash,
        )

        merkletree_leaves.append(lock.lockhash)
        if random.randint(0, 1) == 0:
            locked_locks[lock_secrethash] = lock
        else:
            unlocked_locks[lock_secrethash] = UnlockPartialProofState(lock, lock_secret)

    end_state = NettingChannelEndState(HOP1, 300)
    end_state.secrethashes_to_lockedlocks = locked_locks
    end_state.secrethashes_to_unlockedlocks = unlocked_locks
    end_state.merkletree = MerkleTreeState(compute_layers(merkletree_leaves))

    unlock_proof = channel.get_batch_unlock(end_state)
    assert len(unlock_proof) == len(end_state.merkletree.layers[LEAVES])
    leaves_packed = b''.join(lock.encoded for lock in unlock_proof)

    recomputed_merkle_tree = MerkleTreeState(compute_layers(
        merkle_leaves_from_packed_data(leaves_packed),
    ))
    assert len(recomputed_merkle_tree.layers[LEAVES]) == len(end_state.merkletree.layers[LEAVES])

    computed_merkleroot = merkleroot(recomputed_merkle_tree)
    assert merkleroot(end_state.merkletree) == computed_merkleroot
def test_many(tree_up_to=10):
    for number_of_leaves in range(1, tree_up_to):  # skipping the empty tree

        leaves = [sha3(str(value)) for value in range(number_of_leaves)]

        layers = compute_layers(leaves)
        tree = MerkleTreeState(layers)
        root = merkleroot(tree)

        for value in leaves:
            proof = compute_merkleproof_for(tree, value)
            assert validate_proof(proof, root, value)

        reversed_tree = MerkleTreeState(compute_layers(reversed(leaves)))
        assert root == merkleroot(reversed_tree)
Ejemplo n.º 7
0
def test_three():
    hash_0 = b'a' * 32
    hash_1 = b'b' * 32
    hash_2 = b'c' * 32

    leaves = [hash_0, hash_1, hash_2]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)

    hash_01 = (
        b'me\xef\x9c\xa9=5\x16\xa4\xd3\x8a\xb7\xd9\x89\xc2\xb5\x00'
        b'\xe2\xfc\x89\xcc\xdc\xf8x\xf9\xc4m\xaa\xf6\xad\r['
    )
    assert sha3(hash_0 + hash_1) == hash_01
    calculated_root = sha3(hash_2 + hash_01)

    proof0 = compute_merkleproof_for(tree, hash_0)
    proof1 = compute_merkleproof_for(tree, hash_1)
    proof2 = compute_merkleproof_for(tree, hash_2)

    assert proof0 == [hash_1, hash_2]
    assert root == calculated_root
    assert validate_proof(proof0, root, hash_0)

    assert proof1 == [hash_0, hash_2]
    assert root == calculated_root
    assert validate_proof(proof1, root, hash_1)

    # with an odd number of values, the last value wont appear by itself in the
    # proof since it isn't hashed with another value
    assert proof2 == [sha3(hash_0 + hash_1)]
    assert root == calculated_root
    assert validate_proof(proof2, root, hash_2)
Ejemplo n.º 8
0
def test_compute_layers_single_entry():
    hash_0 = sha3(b'x')
    layers = compute_layers([hash_0])
    assert layers[MERKLEROOT][0] == hash_0

    tree = MerkleTreeState(layers)
    assert merkleroot(tree) == hash_0
Ejemplo n.º 9
0
def test_three():
    hash_0 = b'a' * 32
    hash_1 = b'b' * 32
    hash_2 = b'c' * 32

    leaves = [hash_0, hash_1, hash_2]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)

    hash_01 = (b'me\xef\x9c\xa9=5\x16\xa4\xd3\x8a\xb7\xd9\x89\xc2\xb5\x00'
               b'\xe2\xfc\x89\xcc\xdc\xf8x\xf9\xc4m\xaa\xf6\xad\r[')
    assert sha3(hash_0 + hash_1) == hash_01
    calculated_root = sha3(hash_2 + hash_01)

    proof0 = compute_merkleproof_for(tree, hash_0)
    proof1 = compute_merkleproof_for(tree, hash_1)
    proof2 = compute_merkleproof_for(tree, hash_2)

    assert proof0 == [hash_1, hash_2]
    assert root == calculated_root
    assert validate_proof(proof0, root, hash_0)

    assert proof1 == [hash_0, hash_2]
    assert root == calculated_root
    assert validate_proof(proof1, root, hash_1)

    # with an odd number of values, the last value wont appear by itself in the
    # proof since it isn't hashed with another value
    assert proof2 == [sha3(hash_0 + hash_1)]
    assert root == calculated_root
    assert validate_proof(proof2, root, hash_2)
def test_merkle_proof_one_lock(tester_chain,
                               tester_nettingchannel_library_address):
    """ computeMerkleRoot and the python implementation must compute the same
    value for a merkle tree with a single lock."""

    auxiliary = deploy_auxiliary_tester(tester_chain,
                                        tester_nettingchannel_library_address)

    amount = 10
    expiration = 77
    secret = sha3(b'test_merkle_proof_one_lock')
    secrethash = sha3(secret)
    lock = Lock(amount, expiration, secrethash)

    layers = compute_layers([lock.lockhash])
    merkletree = MerkleTreeState(layers)

    proof = compute_merkleproof_for(merkletree, lock.lockhash)
    assert len(proof) == 0, 'with only one element the proof is empty'

    smart_contact_root = auxiliary.computeMerkleRoot(
        lock.as_bytes,
        b''.join(proof),
    )

    assert smart_contact_root == merkleroot(merkletree)
Ejemplo n.º 11
0
def test_compute_layers_single_entry():
    hash_0 = sha3(b'x')
    layers = compute_layers([hash_0])
    assert layers[MERKLEROOT][0] == hash_0

    tree = MerkleTreeState(layers)
    assert merkleroot(tree) == hash_0
Ejemplo n.º 12
0
def create_sendmediatedtransfer(channel_state, initiator, target, amount,
                                identifier, expiration, secrethash):

    our_state = channel_state.our_state
    partner_state = channel_state.partner_state
    our_balance_proof = our_state.balance_proof

    msg = 'caller must make sure there is enough balance'
    assert amount <= get_distributable(our_state, partner_state), msg

    msg = 'caller must make sure the channel is open'
    assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg

    lock = HashTimeLockState(
        amount,
        expiration,
        secrethash,
    )

    merkletree = compute_merkletree_with(
        channel_state.our_state.merkletree,
        lock.lockhash,
    )
    # The caller must ensure the same lock is not being used twice
    assert merkletree, 'lock is already registered'

    locksroot = merkleroot(merkletree)

    if our_balance_proof:
        transferred_amount = our_balance_proof.transferred_amount
    else:
        transferred_amount = 0

    token = channel_state.token_address
    nonce = get_next_nonce(channel_state.our_state)
    recipient = channel_state.partner_state.address

    balance_proof = BalanceProofUnsignedState(
        nonce,
        transferred_amount,
        locksroot,
        channel_state.identifier,
    )

    locked_transfer = LockedTransferUnsignedState(
        identifier,
        token,
        balance_proof,
        lock,
        initiator,
        target,
    )

    mediatedtransfer = SendMediatedTransfer(
        locked_transfer,
        recipient,
    )

    return mediatedtransfer, merkletree
Ejemplo n.º 13
0
def test_withdraw_fails_with_partial_merkle_proof(tree, tester_channels,
                                                  tester_chain,
                                                  settle_timeout):
    """ withdraw must fail if informed proof is not complete. """
    pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0]

    current_block = tester_chain.block.number
    expiration = current_block + settle_timeout - 1
    locks = [
        make_lock(
            hashlock=hashlock,
            expiration=expiration,
        ) for hashlock in tree
    ]

    leaves = [sha3(lock.as_bytes) for lock in locks]
    layers = compute_layers(leaves)
    merkle_tree = MerkleTreeState(layers)

    opened_block = nettingchannel.opened(sender=pkey0)
    nonce = 1 + (opened_block * (2**32))
    direct_transfer = make_direct_transfer(
        nonce=nonce,
        channel=channel0.identifier,
        locksroot=merkleroot(merkle_tree),
        recipient=privatekey_to_address(pkey1))

    address = privatekey_to_address(pkey0)
    sign_key = PrivateKey(pkey0)
    direct_transfer.sign(sign_key, address)

    direct_transfer_hash = sha3(direct_transfer.packed().data[:-65])
    nettingchannel.close(
        direct_transfer.nonce,
        direct_transfer.transferred_amount,
        direct_transfer.locksroot,
        direct_transfer_hash,
        direct_transfer.signature,
        sender=pkey1,
    )

    for lock in locks:
        secret = HASHLOCKS_SECRESTS[lock.hashlock]
        lock_encoded = lock.as_bytes
        merkle_proof = compute_merkleproof_for(merkle_tree, sha3(lock_encoded))

        # withdraw must fail regardless of which part of the proof is removed
        for hash_ in merkle_proof:
            tampered_proof = list(merkle_proof)
            tampered_proof.remove(hash_)

            with pytest.raises(TransactionFailed):
                nettingchannel.withdraw(
                    lock_encoded,
                    b''.join(tampered_proof),
                    secret,
                    sender=pkey1,
                )
Ejemplo n.º 14
0
def test_many(tree_up_to=10):
    for number_of_leaves in range(1, tree_up_to):  # skipping the empty tree

        leaves = [
            sha3(str(value).encode())
            for value in range(number_of_leaves)
        ]

        layers = compute_layers(leaves)
        tree = MerkleTreeState(layers)
        root = merkleroot(tree)

        for value in leaves:
            proof = compute_merkleproof_for(tree, value)
            assert validate_proof(proof, root, value)

        reversed_tree = MerkleTreeState(compute_layers(reversed(leaves)))
        assert root == merkleroot(reversed_tree)
Ejemplo n.º 15
0
 def update(self, amount, lockhash):
     self._merkletree = channel.compute_merkletree_with(
         self._merkletree, lockhash)
     if self.properties:
         self.properties = factories.replace(
             self.properties,
             locked_amount=self.properties.locked_amount + amount,
             locksroot=merkleroot(self._merkletree),
             nonce=self.properties.nonce + 1,
         )
     else:
         self.properties = factories.BalanceProofProperties(
             transferred_amount=0,
             locked_amount=amount,
             nonce=1,
             locksroot=merkleroot(self._merkletree),
             canonical_identifier=self._canonical_identifier,
         )
Ejemplo n.º 16
0
def test_channel_cleared_after_our_unlock():
    our_model, _ = create_model(balance=700, merkletree_width=1)
    partner_model, partner_key1 = create_model(balance=700, merkletree_width=0)
    channel_state = create_channel_from_models(our_model, partner_model,
                                               partner_key1)
    block_number = 1
    block_hash = make_block_hash()

    def make_unlock(unlock_end, partner_end):
        batch_unlock = ContractReceiveChannelBatchUnlock(
            transaction_hash=make_transaction_hash(),
            canonical_identifier=channel_state.canonical_identifier,
            participant=partner_end.address,
            partner=unlock_end.address,
            locksroot=unlock_end.balance_proof.locksroot,
            unlocked_amount=10,
            returned_tokens=0,
            block_number=block_number,
            block_hash=block_hash,
        )
        return batch_unlock

    settle_channel = ContractReceiveChannelSettled(
        transaction_hash=make_transaction_hash(),
        canonical_identifier=channel_state.canonical_identifier,
        our_onchain_locksroot=merkleroot(channel_state.our_state.merkletree),
        partner_onchain_locksroot=merkleroot(
            channel_state.partner_state.merkletree),
        block_number=1,
        block_hash=make_block_hash(),
    )

    assert settle_channel.our_onchain_locksroot is not EMPTY_MERKLE_ROOT
    assert settle_channel.partner_onchain_locksroot is EMPTY_MERKLE_ROOT

    iteration = channel.state_transition(channel_state, settle_channel,
                                         block_number, block_hash)

    batch_unlock = make_unlock(channel_state.our_state,
                               channel_state.partner_state)
    iteration = channel.state_transition(iteration.new_state, batch_unlock,
                                         block_number, block_hash)
    msg = "partner did not have any locks in the merkletree, channel should have been cleaned"
    assert iteration.new_state is None, msg
Ejemplo n.º 17
0
def assert_locked(from_channel, pending_locks):
    """ Assert the locks created from `from_channel`. """
    # a locked transfer is registered in the _partner_ state
    if pending_locks:
        leaves = [sha3(lock.as_bytes) for lock in pending_locks]
        layers = compute_layers(leaves)
        tree = MerkleTreeState(layers)
        root = merkleroot(tree)
    else:
        root = EMPTY_MERKLE_ROOT

    assert len(
        from_channel.our_state.hashlocks_to_pendinglocks) == len(pending_locks)
    assert merkleroot(from_channel.our_state.merkletree) == root
    assert from_channel.our_state.amount_locked == sum(
        lock.amount for lock in pending_locks)
    assert from_channel.locked == sum(lock.amount for lock in pending_locks)

    for lock in pending_locks:
        assert lock.hashlock in from_channel.our_state.hashlocks_to_pendinglocks
Ejemplo n.º 18
0
def assert_locked(from_channel, pending_locks):
    """ Assert the locks created from `from_channel`. """
    # a locked transfer is registered in the _partner_ state
    if pending_locks:
        leaves = [sha3(lock.as_bytes) for lock in pending_locks]
        layers = compute_layers(leaves)
        tree = MerkleTreeState(layers)
        root = merkleroot(tree)
    else:
        root = EMPTY_MERKLE_ROOT

    assert len(from_channel.our_state.hashlocks_to_pendinglocks) == len(
        pending_locks
    )
    assert merkleroot(from_channel.our_state.merkletree) == root
    assert from_channel.our_state.amount_locked == sum(lock.amount for lock in pending_locks)
    assert from_channel.locked == sum(lock.amount for lock in pending_locks)

    for lock in pending_locks:
        assert lock.hashlock in from_channel.our_state.hashlocks_to_pendinglocks
Ejemplo n.º 19
0
def create_unlock(
    channel_state: NettingChannelState,
    message_identifier: typing.MessageID,
    payment_identifier: typing.PaymentID,
    secret: typing.Secret,
    lock: HashTimeLockState,
) -> SendUnlockAndMerkleTree:
    our_state = channel_state.our_state

    msg = 'caller must make sure the lock is known'
    assert is_lock_pending(our_state, lock.secrethash), msg

    msg = 'caller must make sure the channel is open'
    assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg

    our_balance_proof = our_state.balance_proof
    if our_balance_proof:
        transferred_amount = lock.amount + our_balance_proof.transferred_amount
    else:
        transferred_amount = lock.amount

    merkletree = compute_merkletree_without(
        our_state.merkletree,
        lock.lockhash,
    )
    locksroot = merkleroot(merkletree)

    token = channel_state.token_address
    nonce = get_next_nonce(our_state)
    recipient = channel_state.partner_state.address
    locked_amount = get_amount_locked(
        our_state) - lock.amount  # the lock is still registered

    balance_proof = BalanceProofUnsignedState(
        nonce,
        transferred_amount,
        locked_amount,
        locksroot,
        channel_state.token_network_identifier,
        channel_state.identifier,
    )

    queue_name = channel_state.identifier
    unlock_lock = SendBalanceProof(
        recipient,
        queue_name,
        message_identifier,
        payment_identifier,
        token,
        secret,
        balance_proof,
    )

    return unlock_lock, merkletree
Ejemplo n.º 20
0
def test_one():
    hash_0 = b'a' * 32

    leaves = [hash_0]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)
    proof = compute_merkleproof_for(tree, hash_0)

    assert proof == []
    assert root == hash_0
    assert validate_proof(proof, root, hash_0) is True
Ejemplo n.º 21
0
def test_one():
    hash_0 = b'a' * 32

    leaves = [hash_0]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)
    proof = compute_merkleproof_for(tree, hash_0)

    assert proof == []
    assert root == hash_0
    assert validate_proof(proof, root, hash_0) is True
    def compute_merkleroot_without(self, without):
        """ Compute the resulting merkle root if the lock `include` is added in
        the tree.
        """
        if not self.is_known(without.hashlock):
            raise ValueError('unknown lock', lock=without)

        leaves = list(self.merkletree.layers[LEAVES])
        leaves.remove(sha3(without.as_bytes))

        if leaves:
            tree_without = MerkleTreeState(compute_layers(leaves))
            locksroot = merkleroot(tree_without)
        else:
            locksroot = EMPTY_MERKLE_ROOT

        return locksroot
Ejemplo n.º 23
0
    def create_BP(self, w3, cr, initiator, target, secrethash, amount, expiration, s_contract, a_contract, start_time):
        # self.lock.acquire()
        # try:
        #     self.locked_amount[self.i] += amount + s_contract[0]
        # finally:
        #     self.lock.release()
        # print("locked_amount ", self.locked_amount[self.i])

        # uint = 32bytes, address = 20bytes, bytes32 = 32bytes
        leaf = pack_data(['uint256', 'uint256', 'bytes32', 'uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'address'],
                         [amount+s_contract[0], expiration, secrethash, s_contract[0],
                          s_contract[1], a_contract[0][0], a_contract[1][1], start_time, target])
        self.leaves[self.i][cr] = leaf
        temp = []
        for i in list(self.leaves[self.i].values()) :
            temp.append(sha3(i))
        layer = compute_layers(temp)
        tree = MerkleTreeState(layer)
        locksroot = "0x" + merkleroot(tree).hex()
        # print("locksroot ", locksroot)

        self.locksroot[self.i] = locksroot
        self.nonce +=1

        message_data = LockedTransfer_structure(self.nonce, self.chain_id, cr, expiration, self.token_network.address, self.channel_identifier,
                                                 self.addrs[1-self.i], target, initiator, locksroot, secrethash, self.transferred_amount[self.i],
                                                self.locked_amount[self.i], amount, s_contract[0], s_contract[1], a_contract[0][0], a_contract[1][1], start_time)
        packed_message_data = message_data.pack()
        additional_hash = '0x' + sha3(packed_message_data).hex()
        # print("additional_hash ", additional_hash)

        packed_balance = pack_data(['uint256', 'uint256', 'bytes32'], [self.transferred_amount[self.i], self.locked_amount[self.i], locksroot])
        balance_hash = '0x' + sha3(packed_balance).hex()
        # print("balance_hash ", balance_hash)

        packed_balance_proof = pack_data(['uint256', 'bytes32', 'uint256', 'bytes32'],
                                         [self.channel_identifier, balance_hash, self.nonce, additional_hash])

        hashBP = '0x' + sha3(packed_balance_proof).hex()
        signature = w3.eth.account.signHash(message_hash=hashBP, private_key=self.sk)
        # print("signature", signature)
        BP = balanceProof(message_data, additional_hash, balance_hash, signature['signature'].hex())
        self.BP[self.i] = BP

        return BP
Ejemplo n.º 24
0
def test_two():
    hash_0 = b'a' * 32
    hash_1 = b'b' * 32

    leaves = [hash_0, hash_1]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)
    proof0 = compute_merkleproof_for(tree, hash_0)
    proof1 = compute_merkleproof_for(tree, hash_1)

    assert proof0 == [hash_1]
    assert root == sha3(hash_0 + hash_1)
    assert validate_proof(proof0, root, hash_0)

    assert proof1 == [hash_0]
    assert root == sha3(hash_0 + hash_1)
    assert validate_proof(proof1, root, hash_1)
Ejemplo n.º 25
0
def test_two():
    hash_0 = b'a' * 32
    hash_1 = b'b' * 32

    leaves = [hash_0, hash_1]
    layers = compute_layers(leaves)
    tree = MerkleTreeState(layers)
    root = merkleroot(tree)
    proof0 = compute_merkleproof_for(tree, hash_0)
    proof1 = compute_merkleproof_for(tree, hash_1)

    assert proof0 == [hash_1]
    assert root == sha3(hash_0 + hash_1)
    assert validate_proof(proof0, root, hash_0)

    assert proof1 == [hash_0]
    assert root == sha3(hash_0 + hash_1)
    assert validate_proof(proof1, root, hash_1)
Ejemplo n.º 26
0
def test_new_end_state():
    """Test the defaults for an end state object."""
    balance1 = 101
    node_address = factories.make_address()
    end_state = NettingChannelEndState(node_address, balance1)

    lock_secret = sha3(b'test_end_state')
    lock_secrethash = sha3(lock_secret)

    assert channel.is_lock_pending(end_state, lock_secrethash) is False
    assert channel.is_lock_locked(end_state, lock_secrethash) is False
    assert channel.get_next_nonce(end_state) == 1
    assert channel.get_amount_locked(end_state) == 0
    assert not channel.get_known_unlocks(end_state)
    assert merkleroot(end_state.merkletree) == EMPTY_MERKLE_ROOT

    assert not end_state.secrethashes_to_lockedlocks
    assert not end_state.secrethashes_to_unlockedlocks
    def register_locked_transfer(self, locked_transfer):
        """ Register the latest known transfer.

        The sender needs to use this method before sending a locked transfer,
        otherwise the calculate locksroot of the transfer message will be
        invalid and the transfer will be rejected by the partner. Since the
        sender wants the transfer to be accepted by the receiver otherwise the
        transfer won't proceed and the sender won't receive their fee.

        The receiver needs to use this method to update the container with a
        _valid_ transfer, otherwise the locksroot will not contain the pending
        transfer. The receiver needs to ensure that the merkle root has the
        hashlock included, otherwise it won't be able to claim it.

        Args:
            transfer (LockedTransfer): The transfer to be added.

        Raises:
            InvalidLocksRoot: If the merkleroot of `locked_transfer` does not
            match with the expected value.

            ValueError: If the transfer contains a lock that was registered
            previously.
        """
        balance_proof = locked_transfer.to_balanceproof()
        lock = locked_transfer.lock
        lockhashed = sha3(lock.as_bytes)

        if self.is_known(lock.hashlock):
            raise ValueError('hashlock is already registered')

        leaves = list(self.merkletree.layers[LEAVES])
        leaves.append(lockhashed)

        newtree = MerkleTreeState(compute_layers(leaves))
        locksroot = merkleroot(newtree)

        if balance_proof.locksroot != locksroot:
            raise InvalidLocksRoot(locksroot, balance_proof.locksroot)

        self.hashlocks_to_pendinglocks[lock.hashlock] = PendingLock(
            lock, lockhashed)
        self.balance_proof = balance_proof
        self.merkletree = newtree
Ejemplo n.º 28
0
def create_unlock(channel_state, message_identifier, payment_identifier,
                  secret, lock):
    msg = 'caller must make sure the lock is known'
    assert is_lock_pending(channel_state.our_state, lock.secrethash), msg

    msg = 'caller must make sure the channel is open'
    assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg

    our_balance_proof = channel_state.our_state.balance_proof
    if our_balance_proof:
        transferred_amount = lock.amount + our_balance_proof.transferred_amount
    else:
        transferred_amount = lock.amount

    merkletree = compute_merkletree_without(
        channel_state.our_state.merkletree,
        lock.lockhash,
    )
    locksroot = merkleroot(merkletree)

    token = channel_state.token_address
    nonce = get_next_nonce(channel_state.our_state)
    recipient = channel_state.partner_state.address

    balance_proof = BalanceProofUnsignedState(
        nonce,
        transferred_amount,
        locksroot,
        channel_state.identifier,
    )

    queue_name = channel_state.identifier
    unlock_lock = SendBalanceProof(
        recipient,
        queue_name,
        message_identifier,
        payment_identifier,
        token,
        secret,
        balance_proof,
    )

    return unlock_lock, merkletree
def test_merkle_proof(tree, tester_chain,
                      tester_nettingchannel_library_address):
    """ computeMerkleRoot and the python implementation must compute the same value. """

    auxiliary = deploy_auxiliary_tester(tester_chain,
                                        tester_nettingchannel_library_address)

    hashes = [sha3(element) for element in tree]
    layers = compute_layers(hashes)
    merkletree = MerkleTreeState(layers)

    for element in tree:
        proof = compute_merkleproof_for(merkletree, sha3(element))

        smart_contact_root = auxiliary.computeMerkleRoot(
            element,
            b''.join(proof),
        )

        assert smart_contact_root == merkleroot(merkletree)
def test_merkle_proof(
        tree,
        tester_chain,
        tester_nettingchannel_library_address):
    """ computeMerkleRoot and the python implementation must compute the same value. """

    auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address)

    hashes = [sha3(element) for element in tree]
    layers = compute_layers(hashes)
    merkletree = MerkleTreeState(layers)

    for element in tree:
        proof = compute_merkleproof_for(merkletree, sha3(element))

        smart_contact_root = auxiliary.computeMerkleRoot(
            element,
            b''.join(proof),
        )

        assert smart_contact_root == merkleroot(merkletree)
Ejemplo n.º 31
0
def increase_transferred_amount(from_channel, to_channel, amount):
    # increasing the transferred amount by a value larger than distributable
    # would put one end of the channel in a negative balance, which is
    # forbidden
    assert from_channel.distributable >= amount, 'operation would end up in a incosistent state'

    identifier = 1
    nonce = from_channel.get_next_nonce()
    direct_transfer_message = DirectTransfer(
        identifier=identifier,
        nonce=nonce,
        token=from_channel.token_address,
        channel=from_channel.channel_address,
        transferred_amount=from_channel.transferred_amount + amount,
        recipient=from_channel.partner_state.address,
        locksroot=merkleroot(from_channel.partner_state.merkletree),
    )

    # skipping the netting channel register_transfer because the message is not
    # signed
    from_channel.our_state.register_direct_transfer(direct_transfer_message)
    to_channel.partner_state.register_direct_transfer(direct_transfer_message)
Ejemplo n.º 32
0
def increase_transferred_amount(from_channel, to_channel, amount):
    # increasing the transferred amount by a value larger than distributable
    # would put one end of the channel in a negative balance, which is
    # forbidden
    assert from_channel.distributable >= amount, 'operation would end up in a incosistent state'

    identifier = 1
    nonce = from_channel.get_next_nonce()
    direct_transfer_message = DirectTransfer(
        identifier=identifier,
        nonce=nonce,
        token=from_channel.token_address,
        channel=from_channel.channel_address,
        transferred_amount=from_channel.transferred_amount + amount,
        recipient=from_channel.partner_state.address,
        locksroot=merkleroot(from_channel.partner_state.merkletree),
    )

    # skipping the netting channel register_transfer because the message is not
    # signed
    from_channel.our_state.register_direct_transfer(direct_transfer_message)
    to_channel.partner_state.register_direct_transfer(direct_transfer_message)
Ejemplo n.º 33
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.can_transfer:
            raise ValueError(
                'Transfer not possible, no funding or channel 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 = merkleroot(to_.merkletree)

        nonce = self.get_next_nonce()

        return DirectTransfer(
            identifier=identifier,
            nonce=nonce,
            token=self.token_address,
            channel=self.channel_address,
            transferred_amount=transferred_amount,
            recipient=to_.address,
            locksroot=current_locksroot,
        )
    def register_secretmessage(self, message_secret):
        balance_proof = message_secret.to_balanceproof()
        hashlock = sha3(message_secret.secret)
        pendinglock = self.hashlocks_to_pendinglocks.get(hashlock)

        if not pendinglock:
            pendinglock = self.hashlocks_to_unclaimedlocks[hashlock]

        lock = pendinglock.lock
        lockhashed = sha3(lock.as_bytes)

        if not isinstance(balance_proof, BalanceProofState):
            raise ValueError('balance_proof must be a BalanceProof instance')

        if not self.is_known(lock.hashlock):
            raise ValueError('hashlock is not registered')

        leaves = list(self.merkletree.layers[LEAVES])
        leaves.remove(lockhashed)

        if leaves:
            layers = compute_layers(leaves)
            new_merkletree = MerkleTreeState(layers)
            new_locksroot = merkleroot(new_merkletree)
        else:
            new_merkletree = EMPTY_MERKLE_TREE
            new_locksroot = EMPTY_MERKLE_ROOT

        if balance_proof.locksroot != new_locksroot:
            raise InvalidLocksRoot(new_locksroot, balance_proof.locksroot)

        if lock.hashlock in self.hashlocks_to_pendinglocks:
            del self.hashlocks_to_pendinglocks[lock.hashlock]
        else:
            del self.hashlocks_to_unclaimedlocks[lock.hashlock]

        self.merkletree = new_merkletree
        self.balance_proof = balance_proof
    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.can_transfer:
            raise ValueError('Transfer not possible, no funding or channel 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 = merkleroot(to_.merkletree)

        nonce = self.get_next_nonce()

        return DirectTransfer(
            identifier=identifier,
            nonce=nonce,
            token=self.token_address,
            channel=self.channel_address,
            transferred_amount=transferred_amount,
            recipient=to_.address,
            locksroot=current_locksroot,
        )
Ejemplo n.º 36
0
def create_unlock(channel_state, identifier, secret, lock):
    msg = 'caller must make sure the lock is known'
    assert is_known(channel_state.our_state, lock.hashlock), msg

    our_balance_proof = channel_state.our_state.balance_proof
    if our_balance_proof:
        transferred_amount = lock.amount + our_balance_proof.transferred_amount
    else:
        transferred_amount = lock.amount

    merkletree = compute_merkletree_without(
        channel_state.our_state.merkletree,
        lock.lockhash,
    )
    locksroot = merkleroot(merkletree)

    token = channel_state.token_address
    nonce = get_next_nonce(channel_state.our_state)
    recipient = channel_state.partner_state.address

    balance_proof = BalanceProofUnsignedState(
        nonce,
        transferred_amount,
        locksroot,
        channel_state.identifier,
    )

    unlock_lock = SendBalanceProof2(
        identifier,
        token,
        recipient,
        secret,
        balance_proof,
    )

    return unlock_lock, merkletree
Ejemplo n.º 37
0
def test_empty():
    tree = MerkleTreeState([[EMPTY_MERKLE_ROOT]])
    assert merkleroot(tree) == EMPTY_MERKLE_ROOT
Ejemplo n.º 38
0
def make_signed_transfer_for(
        channel_state: NettingChannelState = EMPTY,
        properties: LockedTransferSignedStateProperties = None,
        defaults: LockedTransferSignedStateProperties = None,
        compute_locksroot: bool = False,
        allow_invalid: bool = False,
        only_transfer: bool = True,
) -> LockedTransferSignedState:
    properties: LockedTransferSignedStateProperties = create_properties(
        properties or LockedTransferSignedStateProperties(),
        defaults or SIGNED_TRANSFER_FOR_CHANNEL_DEFAULTS,
    )

    channel_state = if_empty(channel_state, create(NettingChannelStateProperties()))

    if not allow_invalid:
        expiration = properties.transfer.expiration
        valid = channel_state.reveal_timeout < expiration < channel_state.settle_timeout
        assert valid, 'Expiration must be between reveal_timeout and settle_timeout.'

    pubkey = properties.pkey.public_key.format(compressed=False)
    assert publickey_to_address(pubkey) == properties.sender

    if properties.sender == channel_state.our_state.address:
        recipient = channel_state.partner_state.address
    elif properties.sender == channel_state.partner_state.address:
        recipient = channel_state.our_state.address
    else:
        assert False, 'Given sender does not participate in given channel.'

    if compute_locksroot:
        lock = Lock(
            amount=properties.transfer.amount,
            expiration=properties.transfer.expiration,
            secrethash=sha3(properties.transfer.secret),
        )
        locksroot = merkleroot(channel.compute_merkletree_with(
            merkletree=channel_state.partner_state.merkletree,
            lockhash=sha3(lock.as_bytes),
        ))
    else:
        locksroot = properties.transfer.balance_proof.locksroot

    if only_transfer:
        balance_proof_properties = BalanceProofProperties(
            locksroot=locksroot,
            channel_identifier=channel_state.identifier,
            transferred_amount=0,
            locked_amount=properties.transfer.amount,
        )
    else:
        balance_proof_properties = BalanceProofProperties(
            locksroot=locksroot,
            channel_identifier=channel_state.identifier,
        )
    transfer = create(
        LockedTransferSignedStateProperties(
            recipient=recipient,
            transfer=LockedTransferProperties(
                balance_proof=balance_proof_properties,
            ),
        ),
        defaults=properties,
    )

    if not allow_invalid:
        is_valid, msg, _ = channel.is_valid_lockedtransfer(
            transfer_state=transfer,
            channel_state=channel_state,
            sender_state=channel_state.partner_state,
            receiver_state=channel_state.our_state,
        )
        assert is_valid, msg

    return transfer
Ejemplo n.º 39
0
    def register_transfer_from_to(self, block_number, transfer, from_state,
                                  to_state):  # noqa pylint: disable=too-many-branches,too-many-statements
        """ Validates and register a signed transfer, updating the channel's state accordingly.

        Note:
            The transfer must be registered before it is sent, not on
            acknowledgement. That is necessary for two reasons:

            - Guarantee that the transfer is valid.
            - Avoid sending a new transaction without funds.

        Raises:
            InsufficientBalance: If the transfer is negative or above the distributable amount.
            InvalidLocksRoot: If locksroot check fails.
            InvalidNonce: If the expected nonce does not match.
            ValueError: If there is an address mismatch (token or node address).
        """
        if transfer.channel != self.channel_address:
            raise ValueError('Channel address mismatch')

        if transfer.sender != from_state.address:
            raise ValueError('Unsigned transfer')

        # nonce is changed only when a transfer is un/registered, if the test
        # fails either we are out of sync, a message out of order, or it's a
        # forged transfer
        is_invalid_nonce = (transfer.nonce < 1
                            or (from_state.nonce is not None
                                and transfer.nonce != from_state.nonce + 1))
        if is_invalid_nonce:
            # this may occur on normal operation
            if log.isEnabledFor(logging.INFO):
                log.info(
                    'INVALID NONCE',
                    node=pex(self.our_address),
                    from_=pex(transfer.sender),
                    to=pex(to_state.address),
                    expected_nonce=from_state.nonce,
                    nonce=transfer.nonce,
                )
            raise InvalidNonce(transfer)

        # if the locksroot is out-of-sync (because a transfer was created while
        # a Secret was in traffic) the balance _will_ be wrong, so first check
        # the locksroot and then the balance
        if isinstance(transfer, LockedTransfer):
            if from_state.is_known(transfer.lock.hashlock):
                # this may occur on normal operation
                if log.isEnabledFor(logging.INFO):
                    lockhashes = list(
                        from_state.hashlocks_to_unclaimedlocks.values())
                    lockhashes.extend(
                        from_state.hashlocks_to_pendinglocks.values())
                    log.info(
                        'duplicated lock',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        hashlock=pex(transfer.lock.hashlock),
                        lockhash=pex(sha3(transfer.lock.as_bytes)),
                        lockhashes=lpex(lockhashes),
                        received_locksroot=pex(transfer.locksroot),
                    )
                raise ValueError('hashlock is already registered')

            # As a receiver: Check that all locked transfers are registered in
            # the locksroot, if any hashlock is missing there is no way to
            # claim it while the channel is closing
            expected_locksroot = from_state.compute_merkleroot_with(
                transfer.lock)
            if expected_locksroot != transfer.locksroot:
                # this should not happen
                if log.isEnabledFor(logging.WARN):
                    lockhashes = list(
                        from_state.hashlocks_to_unclaimedlocks.values())
                    lockhashes.extend(
                        from_state.hashlocks_to_pendinglocks.values())
                    log.warn(
                        'LOCKSROOT MISMATCH',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        lockhash=pex(sha3(transfer.lock.as_bytes)),
                        lockhashes=lpex(lockhashes),
                        expected_locksroot=pex(expected_locksroot),
                        received_locksroot=pex(transfer.locksroot),
                    )

                raise InvalidLocksRoot(expected_locksroot, transfer.locksroot)

            # For mediators: This is registering the *mediator* paying
            # transfer. The expiration of the lock must be `reveal_timeout`
            # blocks smaller than the *received* paying transfer. This cannot
            # be checked by the paying channel alone.
            #
            # For the initiators: As there is no backing transfer, the
            # expiration is arbitrary, using the channel settle_timeout as an
            # upper limit because the node receiving the transfer will use it
            # as an upper bound while mediating.
            #
            # For the receiver: A lock that expires after the settle period
            # just means there is more time to withdraw it.
            end_settle_period = self.get_settle_expiration(block_number)
            expires_after_settle = transfer.lock.expiration > end_settle_period
            is_sender = transfer.sender == self.our_address

            if is_sender and expires_after_settle:
                if log.isEnabledFor(logging.ERROR):
                    log.error(
                        'Lock expires after the settlement period.',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        lock_expiration=transfer.lock.expiration,
                        current_block=block_number,
                        end_settle_period=end_settle_period,
                    )

                raise ValueError('Lock expires after the settlement period.')

        # only check the balance if the locksroot matched
        if transfer.transferred_amount < from_state.transferred_amount:
            if log.isEnabledFor(logging.ERROR):
                log.error(
                    'NEGATIVE TRANSFER',
                    node=pex(self.our_state.address),
                    from_=pex(from_state.address),
                    to=pex(to_state.address),
                    transfer=transfer,
                )

            raise ValueError('Negative transfer')

        amount = transfer.transferred_amount - from_state.transferred_amount
        distributable = from_state.distributable(to_state)

        if isinstance(transfer, DirectTransfer):
            if amount > distributable:
                raise InsufficientBalance(transfer)

        elif isinstance(transfer, LockedTransfer):
            if amount + transfer.lock.amount > distributable:
                raise InsufficientBalance(transfer)

        elif isinstance(transfer, Secret):
            hashlock = sha3(transfer.secret)
            lock = from_state.get_lock_by_hashlock(hashlock)
            transferred_amount = from_state.transferred_amount + lock.amount

            # transfer.transferred_amount could be larger than the previous
            # transferred_amount + lock.amount, that scenario is a bug of the
            # payer
            if transfer.transferred_amount != transferred_amount:
                raise ValueError(
                    'invalid transferred_amount, expected: {} got: {}'.format(
                        transferred_amount,
                        transfer.transferred_amount,
                    ))

        # all checks need to be done before the internal state of the channel
        # is changed, otherwise if a check fails and the state was changed the
        # channel will be left trashed

        if isinstance(transfer, LockedTransfer):
            if log.isEnabledFor(logging.DEBUG):
                lockhashes = list(
                    from_state.hashlocks_to_unclaimedlocks.values())
                lockhashes.extend(
                    from_state.hashlocks_to_pendinglocks.values())
                log.debug(
                    'REGISTERED LOCK',
                    node=pex(self.our_state.address),
                    from_=pex(from_state.address),
                    to=pex(to_state.address),
                    currentlocksroot=pex(merkleroot(from_state.merkletree)),
                    lockhashes=lpex(lockhashes),
                    lock_amount=transfer.lock.amount,
                    lock_expiration=transfer.lock.expiration,
                    lock_hashlock=pex(transfer.lock.hashlock),
                    lockhash=pex(sha3(transfer.lock.as_bytes)),
                )

            from_state.register_locked_transfer(transfer)

            # register this channel as waiting for the secret (the secret can
            # be revealed through a message or a blockchain log)
            self.external_state.register_channel_for_hashlock(
                self,
                transfer.lock.hashlock,
            )

        if isinstance(transfer, DirectTransfer):
            from_state.register_direct_transfer(transfer)

        elif isinstance(transfer, Secret):
            from_state.register_secretmessage(transfer)

        if log.isEnabledFor(logging.DEBUG):
            log.debug(
                'REGISTERED TRANSFER',
                node=pex(self.our_state.address),
                from_=pex(from_state.address),
                to=pex(to_state.address),
                transfer=repr(transfer),
                transferred_amount=from_state.transferred_amount,
                nonce=from_state.nonce,
                current_locksroot=pex(merkleroot(from_state.merkletree)),
            )
Ejemplo n.º 40
0
def test_unlock(raiden_network, token_addresses, deposit):
    """Unlock can be called on a closed channel."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    alice_to_bob_amount = 10
    identifier = 1
    secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    secrethash = sha3(secret)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    lock = channel.get_lock(alice_bob_channel.our_state, secrethash)
    assert lock

    assert_synched_channel_state(
        token_network_identifier,
        alice_app, deposit, [lock],
        bob_app, deposit, [],
    )

    # get proof, that locked transfermessage was in merkle tree, with locked.root
    unlock_proof = channel.compute_proof_for_lock(
        alice_bob_channel.our_state,
        secret,
        lock,
    )

    assert validate_proof(
        unlock_proof.merkle_proof,
        merkleroot(bob_alice_channel.partner_state.merkletree),
        sha3(lock.encoded),
    )
    assert unlock_proof.lock_encoded == lock.encoded
    assert unlock_proof.secret == secret

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    # Unlock will not be called because the secret was not revealed
    assert lock.expiration > alice_app.raiden.chain.block_number()
    assert lock.secrethash == sha3(secret)

    nettingchannel_proxy = bob_app.raiden.chain.netting_channel(
        bob_alice_channel.identifier,
    )
    nettingchannel_proxy.unlock(unlock_proof)

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel.identifier],
        alice_app.raiden.alarm.wait_time,
    )

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    alice_netted_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_netted_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(alice_app.raiden.address) == alice_netted_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_netted_balance

    # Now let's query the WAL to see if the state changes were logged as expected
    state_changes = alice_app.raiden.wal.storage.get_statechanges_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    assert must_contain_entry(state_changes, ContractReceiveChannelUnlock, {
        'payment_network_identifier': registry_address,
        'token_address': token_address,
        'channel_identifier': alice_bob_channel.identifier,
        'secrethash': secrethash,
        'secret': secret,
        'receiver': bob_app.raiden.address,
    })
def test_withdraw_tampered_lock_amount(
        tree,
        tester_channels,
        tester_chain,
        tester_token,
        settle_timeout):

    """ withdraw must fail if the lock amonut is tampered. """
    pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0]

    current_block = tester_chain.block.number
    expiration = current_block + settle_timeout - 1
    locks = [
        make_lock(
            hashlock=hashlock,
            expiration=expiration,
        )
        for hashlock in tree
    ]

    leaves = [sha3(lock.as_bytes) for lock in locks]
    layers = compute_layers(leaves)
    merkle_tree = MerkleTreeState(layers)

    opened_block = nettingchannel.opened(sender=pkey0)
    nonce = 1 + (opened_block * (2 ** 32))
    direct_transfer = make_direct_transfer(
        nonce=nonce,
        channel=channel0.channel_address,
        locksroot=merkleroot(merkle_tree),
        token=tester_token.address,
        recipient=privatekey_to_address(pkey1)
    )

    address = privatekey_to_address(pkey0)
    sign_key = PrivateKey(pkey0)
    direct_transfer.sign(sign_key, address)

    direct_transfer_hash = sha3(direct_transfer.packed().data[:-65])
    nettingchannel.close(
        direct_transfer.nonce,
        direct_transfer.transferred_amount,
        direct_transfer.locksroot,
        direct_transfer_hash,
        direct_transfer.signature,
        sender=pkey1,
    )

    for lock in locks:
        secret = HASHLOCKS_SECRESTS[lock.hashlock]

        lock_encoded = lock.as_bytes
        merkle_proof = compute_merkleproof_for(merkle_tree, sha3(lock_encoded))

        tampered_lock = make_lock(
            amount=lock.amount * 100,
            hashlock=lock.hashlock,
            expiration=lock.expiration,
        )
        tampered_lock_encoded = sha3(tampered_lock.as_bytes)

        with pytest.raises(TransactionFailed):
            nettingchannel.withdraw(
                tampered_lock_encoded,
                b''.join(merkle_proof),
                secret,
                sender=pkey1,
            )
Ejemplo n.º 42
0
def create_sendlockedtransfer(
    channel_state: NettingChannelState,
    initiator: typing.InitiatorAddress,
    target: typing.TargetAddress,
    amount: typing.PaymentAmount,
    message_identifier: typing.MessageID,
    payment_identifier: typing.PaymentID,
    expiration: typing.BlockExpiration,
    secrethash: typing.SecretHash,
) -> SendLockedTransfer:
    our_state = channel_state.our_state
    partner_state = channel_state.partner_state
    our_balance_proof = our_state.balance_proof

    msg = 'caller must make sure there is enough balance'
    assert amount <= get_distributable(our_state, partner_state), msg

    msg = 'caller must make sure the channel is open'
    assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg

    lock = HashTimeLockState(
        amount,
        expiration,
        secrethash,
    )

    merkletree = compute_merkletree_with(
        channel_state.our_state.merkletree,
        lock.lockhash,
    )
    # The caller must ensure the same lock is not being used twice
    assert merkletree, 'lock is already registered'

    locksroot = merkleroot(merkletree)

    if our_balance_proof:
        transferred_amount = our_balance_proof.transferred_amount
    else:
        transferred_amount = 0

    token = channel_state.token_address
    nonce = get_next_nonce(channel_state.our_state)
    recipient = channel_state.partner_state.address
    locked_amount = get_amount_locked(
        our_state) + amount  # the new lock is not registered yet

    balance_proof = BalanceProofUnsignedState(
        nonce,
        transferred_amount,
        locked_amount,
        locksroot,
        channel_state.token_network_identifier,
        channel_state.identifier,
    )

    locked_transfer = LockedTransferUnsignedState(
        payment_identifier,
        token,
        balance_proof,
        lock,
        initiator,
        target,
    )

    queue_name = channel_state.identifier
    lockedtransfer = SendLockedTransfer(
        recipient,
        queue_name,
        message_identifier,
        locked_transfer,
    )

    return lockedtransfer, merkletree
def test_end_state():
    token_address = make_address()
    privkey1, address1 = make_privkey_address()
    address2 = make_address()
    channel_address = make_address()

    balance1 = 70
    balance2 = 110

    lock_secret = sha3(b'test_end_state')
    lock_amount = 30
    lock_expiration = 10
    lock_hashlock = sha3(lock_secret)

    state1 = ChannelEndState(address1, balance1, None, EMPTY_MERKLE_TREE)
    state2 = ChannelEndState(address2, balance2, None, EMPTY_MERKLE_TREE)

    assert state1.contract_balance == balance1
    assert state2.contract_balance == balance2
    assert state1.balance(state2) == balance1
    assert state2.balance(state1) == balance2

    assert state1.is_locked(lock_hashlock) is False
    assert state2.is_locked(lock_hashlock) is False

    assert merkleroot(state1.merkletree) == EMPTY_MERKLE_ROOT
    assert merkleroot(state2.merkletree) == EMPTY_MERKLE_ROOT

    assert state1.nonce is None
    assert state2.nonce is None

    lock = Lock(
        lock_amount,
        lock_expiration,
        lock_hashlock,
    )
    lock_hash = sha3(lock.as_bytes)

    transferred_amount = 0
    locksroot = state2.compute_merkleroot_with(lock)

    locked_transfer = LockedTransfer(
        1,
        nonce=1,
        token=token_address,
        channel=channel_address,
        transferred_amount=transferred_amount,
        recipient=state2.address,
        locksroot=locksroot,
        lock=lock,
    )

    transfer_target = make_address()
    transfer_initiator = make_address()
    fee = 0
    mediated_transfer = locked_transfer.to_mediatedtransfer(
        transfer_target,
        transfer_initiator,
        fee,
    )
    mediated_transfer.sign(privkey1, address1)

    state1.register_locked_transfer(mediated_transfer)

    assert state1.contract_balance == balance1
    assert state2.contract_balance == balance2
    assert state1.balance(state2) == balance1
    assert state2.balance(state1) == balance2

    assert state1.distributable(state2) == balance1 - lock_amount
    assert state2.distributable(state1) == balance2

    assert state1.amount_locked == lock_amount
    assert state2.amount_locked == 0

    assert state1.is_locked(lock_hashlock) is True
    assert state2.is_locked(lock_hashlock) is False

    assert merkleroot(state1.merkletree) == lock_hash
    assert merkleroot(state2.merkletree) == EMPTY_MERKLE_ROOT

    assert state1.nonce is 1
    assert state2.nonce is None

    with pytest.raises(ValueError):
        state1.update_contract_balance(balance1 - 10)

    state1.update_contract_balance(balance1 + 10)

    assert state1.contract_balance == balance1 + 10
    assert state2.contract_balance == balance2
    assert state1.balance(state2) == balance1 + 10
    assert state2.balance(state1) == balance2

    assert state1.distributable(state2) == balance1 - lock_amount + 10
    assert state2.distributable(state1) == balance2

    assert state1.amount_locked == lock_amount
    assert state2.amount_locked == 0

    assert state1.is_locked(lock_hashlock) is True
    assert state2.is_locked(lock_hashlock) is False

    assert merkleroot(state1.merkletree) == lock_hash
    assert merkleroot(state2.merkletree) == EMPTY_MERKLE_ROOT

    assert state1.nonce is 1
    assert state2.nonce is None

    # registering the secret should not change the locked amount
    state1.register_secret(lock_secret)

    assert state1.contract_balance == balance1 + 10
    assert state2.contract_balance == balance2
    assert state1.balance(state2) == balance1 + 10
    assert state2.balance(state1) == balance2

    assert state1.is_locked(lock_hashlock) is False
    assert state2.is_locked(lock_hashlock) is False

    assert merkleroot(state1.merkletree) == lock_hash
    assert merkleroot(state2.merkletree) == EMPTY_MERKLE_ROOT

    assert state1.nonce is 1
    assert state2.nonce is None

    secret_message = Secret(
        identifier=1,
        nonce=2,
        channel=channel_address,
        transferred_amount=transferred_amount + lock_amount,
        locksroot=EMPTY_MERKLE_ROOT,
        secret=lock_secret,
    )
    secret_message.sign(privkey1, address1)
    state1.register_secretmessage(secret_message)

    assert state1.contract_balance == balance1 + 10
    assert state2.contract_balance == balance2
    assert state1.balance(state2) == balance1 + 10 - lock_amount
    assert state2.balance(state1) == balance2 + lock_amount

    assert state1.distributable(state2) == balance1 + 10 - lock_amount
    assert state2.distributable(state1) == balance2 + lock_amount

    assert state1.amount_locked == 0
    assert state2.amount_locked == 0

    assert state1.is_locked(lock_hashlock) is False
    assert state2.is_locked(lock_hashlock) is False

    assert merkleroot(state1.merkletree) == EMPTY_MERKLE_ROOT
    assert merkleroot(state2.merkletree) == EMPTY_MERKLE_ROOT

    assert state1.nonce is 2
    assert state2.nonce is None
    def register_transfer_from_to(
            self,
            block_number,
            transfer,
            from_state,
            to_state):  # noqa pylint: disable=too-many-branches,too-many-statements

        """ Validates and register a signed transfer, updating the channel's state accordingly.

        Note:
            The transfer must be registered before it is sent, not on
            acknowledgement. That is necessary for two reasons:

            - Guarantee that the transfer is valid.
            - Avoid sending a new transaction without funds.

        Raises:
            InsufficientBalance: If the transfer is negative or above the distributable amount.
            InvalidLocksRoot: If locksroot check fails.
            InvalidNonce: If the expected nonce does not match.
            ValueError: If there is an address mismatch (token or node address).
        """
        if transfer.channel != self.channel_address:
            raise ValueError('Channel address mismatch')

        if transfer.sender != from_state.address:
            raise ValueError('Unsigned transfer')

        # nonce is changed only when a transfer is un/registered, if the test
        # fails either we are out of sync, a message out of order, or it's a
        # forged transfer
        is_invalid_nonce = (
            transfer.nonce < 1 or
            (
                from_state.nonce is not None and
                transfer.nonce != from_state.nonce + 1
            )
        )
        if is_invalid_nonce:
            # this may occur on normal operation
            if log.isEnabledFor(logging.INFO):
                log.info(
                    'INVALID NONCE',
                    node=pex(self.our_address),
                    from_=pex(transfer.sender),
                    to=pex(to_state.address),

                    expected_nonce=from_state.nonce,
                    nonce=transfer.nonce,
                )
            raise InvalidNonce(transfer)

        # if the locksroot is out-of-sync (because a transfer was created while
        # a Secret was in traffic) the balance _will_ be wrong, so first check
        # the locksroot and then the balance
        if isinstance(transfer, LockedTransfer):
            if from_state.is_known(transfer.lock.hashlock):
                # this may occur on normal operation
                if log.isEnabledFor(logging.INFO):
                    lockhashes = list(from_state.hashlocks_to_unclaimedlocks.values())
                    lockhashes.extend(from_state.hashlocks_to_pendinglocks.values())
                    log.info(
                        'duplicated lock',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        hashlock=pex(transfer.lock.hashlock),
                        lockhash=pex(sha3(transfer.lock.as_bytes)),
                        lockhashes=lpex(str(l).encode() for l in lockhashes),
                        received_locksroot=pex(transfer.locksroot),
                    )
                raise ValueError('hashlock is already registered')

            # As a receiver: Check that all locked transfers are registered in
            # the locksroot, if any hashlock is missing there is no way to
            # claim it while the channel is closing
            expected_locksroot = from_state.compute_merkleroot_with(transfer.lock)
            if expected_locksroot != transfer.locksroot:
                # this should not happen
                if log.isEnabledFor(logging.WARN):
                    lockhashes = list(from_state.hashlocks_to_unclaimedlocks.values())
                    lockhashes.extend(from_state.hashlocks_to_pendinglocks.values())
                    log.warn(
                        'LOCKSROOT MISMATCH',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        lockhash=pex(sha3(transfer.lock.as_bytes)),
                        lockhashes=lpex(str(l).encode() for l in lockhashes),
                        expected_locksroot=pex(expected_locksroot),
                        received_locksroot=pex(transfer.locksroot),
                    )

                raise InvalidLocksRoot(expected_locksroot, transfer.locksroot)

            # For mediators: This is registering the *mediator* paying
            # transfer. The expiration of the lock must be `reveal_timeout`
            # blocks smaller than the *received* paying transfer. This cannot
            # be checked by the paying channel alone.
            #
            # For the initiators: As there is no backing transfer, the
            # expiration is arbitrary, using the channel settle_timeout as an
            # upper limit because the node receiving the transfer will use it
            # as an upper bound while mediating.
            #
            # For the receiver: A lock that expires after the settle period
            # just means there is more time to withdraw it.
            end_settle_period = self.get_settle_expiration(block_number)
            expires_after_settle = transfer.lock.expiration > end_settle_period
            is_sender = transfer.sender == self.our_address

            if is_sender and expires_after_settle:
                if log.isEnabledFor(logging.ERROR):
                    log.error(
                        'Lock expires after the settlement period.',
                        node=pex(self.our_address),
                        from_=pex(from_state.address),
                        to=pex(to_state.address),
                        lock_expiration=transfer.lock.expiration,
                        current_block=block_number,
                        end_settle_period=end_settle_period,
                    )

                raise ValueError('Lock expires after the settlement period.')

        # only check the balance if the locksroot matched
        if transfer.transferred_amount < from_state.transferred_amount:
            if log.isEnabledFor(logging.ERROR):
                log.error(
                    'NEGATIVE TRANSFER',
                    node=pex(self.our_state.address),
                    from_=pex(from_state.address),
                    to=pex(to_state.address),
                    transfer=transfer,
                )

            raise ValueError('Negative transfer')

        amount = transfer.transferred_amount - from_state.transferred_amount
        distributable = from_state.distributable(to_state)

        if isinstance(transfer, DirectTransfer):
            if amount > distributable:
                raise InsufficientBalance(transfer)

        elif isinstance(transfer, LockedTransfer):
            if amount + transfer.lock.amount > distributable:
                raise InsufficientBalance(transfer)

        elif isinstance(transfer, Secret):
            hashlock = sha3(transfer.secret)
            lock = from_state.get_lock_by_hashlock(hashlock)
            transferred_amount = from_state.transferred_amount + lock.amount

            # transfer.transferred_amount could be larger than the previous
            # transferred_amount + lock.amount, that scenario is a bug of the
            # payer
            if transfer.transferred_amount != transferred_amount:
                raise ValueError(
                    'invalid transferred_amount, expected: {} got: {}'.format(
                        transferred_amount,
                        transfer.transferred_amount,
                    )
                )

        # all checks need to be done before the internal state of the channel
        # is changed, otherwise if a check fails and the state was changed the
        # channel will be left trashed

        if isinstance(transfer, LockedTransfer):
            if log.isEnabledFor(logging.DEBUG):
                lockhashes = list(from_state.hashlocks_to_unclaimedlocks.values())
                lockhashes.extend(from_state.hashlocks_to_pendinglocks.values())
                log.debug(
                    'REGISTERED LOCK',
                    node=pex(self.our_state.address),
                    from_=pex(from_state.address),
                    to=pex(to_state.address),
                    currentlocksroot=pex(merkleroot(from_state.merkletree)),
                    lockhashes=lpex(str(l).encode() for l in lockhashes),
                    lock_amount=transfer.lock.amount,
                    lock_expiration=transfer.lock.expiration,
                    lock_hashlock=pex(transfer.lock.hashlock),
                    lockhash=pex(sha3(transfer.lock.as_bytes)),
                )

            from_state.register_locked_transfer(transfer)

            # register this channel as waiting for the secret (the secret can
            # be revealed through a message or a blockchain log)
            self.external_state.register_channel_for_hashlock(
                self,
                transfer.lock.hashlock,
            )

        if isinstance(transfer, DirectTransfer):
            from_state.register_direct_transfer(transfer)

        elif isinstance(transfer, Secret):
            from_state.register_secretmessage(transfer)

        if log.isEnabledFor(logging.DEBUG):
            log.debug(
                'REGISTERED TRANSFER',
                node=pex(self.our_state.address),
                from_=pex(from_state.address),
                to=pex(to_state.address),
                transfer=repr(transfer),
                transferred_amount=from_state.transferred_amount,
                nonce=from_state.nonce,
                current_locksroot=pex(merkleroot(from_state.merkletree)),
            )
def test_receiver_cannot_spend_locked_amount():
    token_address = make_address()
    privkey1, address1 = make_privkey_address()
    privkey2, address2 = make_privkey_address()

    balance1 = 33
    balance2 = 11

    reveal_timeout = 7
    settle_timeout = 21
    block_number = 7

    our_state = ChannelEndState(address1, balance1, None, EMPTY_MERKLE_TREE)
    partner_state = ChannelEndState(address2, balance2, None, EMPTY_MERKLE_TREE)
    external_state = make_external_state()

    test_channel = Channel(
        our_state,
        partner_state,
        external_state,
        token_address,
        reveal_timeout,
        settle_timeout,
    )

    amount1 = balance2
    expiration = block_number + settle_timeout
    receive_mediated_transfer0 = test_channel.create_mediatedtransfer(
        address1,
        address2,
        fee=0,
        amount=amount1,
        identifier=1,
        expiration=expiration,
        hashlock=sha3(b'test_locked_amount_cannot_be_spent'),
    )
    receive_mediated_transfer0.sign(privkey2, address2)

    test_channel.register_transfer(
        block_number,
        receive_mediated_transfer0,
    )

    # trying to send one unit of the locked token
    amount2 = balance1 + 1
    lock2 = Lock(
        amount=amount2,
        expiration=expiration,
        hashlock=sha3(b'test_locked_amount_cannot_be_spent2'),
    )
    layers = compute_layers([sha3(lock2.as_bytes)])
    tree2 = MerkleTreeState(layers)
    locksroot2 = merkleroot(tree2)

    send_mediated_transfer0 = MediatedTransfer(
        identifier=1,
        nonce=1,
        token=token_address,
        channel=test_channel.channel_address,
        transferred_amount=0,
        recipient=address2,
        locksroot=locksroot2,
        lock=lock2,
        target=address2,
        initiator=address1,
        fee=0,
    )
    send_mediated_transfer0.sign(privkey1, address1)

    # address1 balance is all locked
    with pytest.raises(InsufficientBalance):
        test_channel.register_transfer(
            block_number,
            send_mediated_transfer0,
        )
def test_sender_cannot_overspend():
    token_address = make_address()
    privkey1, address1 = make_privkey_address()
    address2 = make_address()

    balance1 = 70
    balance2 = 110

    reveal_timeout = 5
    settle_timeout = 15
    block_number = 10

    our_state = ChannelEndState(address1, balance1, None, EMPTY_MERKLE_TREE)
    partner_state = ChannelEndState(address2, balance2, None, EMPTY_MERKLE_TREE)
    external_state = make_external_state()

    test_channel = Channel(
        our_state,
        partner_state,
        external_state,
        token_address,
        reveal_timeout,
        settle_timeout,
    )

    amount = balance1
    expiration = block_number + settle_timeout
    sent_mediated_transfer0 = test_channel.create_mediatedtransfer(
        address1,
        address2,
        fee=0,
        amount=amount,
        identifier=1,
        expiration=expiration,
        hashlock=sha3(b'test_locked_amount_cannot_be_spent'),
    )
    sent_mediated_transfer0.sign(privkey1, address1)

    test_channel.register_transfer(
        block_number,
        sent_mediated_transfer0,
    )

    lock2 = Lock(
        amount=amount,
        expiration=expiration,
        hashlock=sha3(b'test_locked_amount_cannot_be_spent2'),
    )
    leaves = [
        sha3(sent_mediated_transfer0.lock.as_bytes),
        sha3(lock2.as_bytes),
    ]
    tree2 = MerkleTreeState(compute_layers(leaves))
    locksroot2 = merkleroot(tree2)

    sent_mediated_transfer1 = MediatedTransfer(
        identifier=2,
        nonce=sent_mediated_transfer0.nonce + 1,
        token=token_address,
        channel=test_channel.channel_address,
        transferred_amount=0,
        recipient=address2,
        locksroot=locksroot2,
        lock=lock2,
        target=address2,
        initiator=address1,
        fee=0,
    )
    sent_mediated_transfer1.sign(privkey1, address1)

    # address1 balance is all locked
    with pytest.raises(InsufficientBalance):
        test_channel.register_transfer(
            block_number,
            sent_mediated_transfer1,
        )
Ejemplo n.º 47
0
def make_signed_transfer_for(
    channel_state: NettingChannelState = EMPTY,
    properties: LockedTransferSignedStateProperties = None,
    defaults: LockedTransferSignedStateProperties = None,
    compute_locksroot: bool = False,
    allow_invalid: bool = False,
    only_transfer: bool = True,
) -> LockedTransferSignedState:
    properties: LockedTransferSignedStateProperties = create_properties(
        properties or LockedTransferSignedStateProperties(),
        defaults or SIGNED_TRANSFER_FOR_CHANNEL_DEFAULTS,
    )

    channel_state = if_empty(channel_state,
                             create(NettingChannelStateProperties()))

    if not allow_invalid:
        ok = channel_state.reveal_timeout < properties.expiration < channel_state.settle_timeout
        assert ok, "Expiration must be between reveal_timeout and settle_timeout."

    assert privatekey_to_address(properties.pkey) == properties.sender

    if properties.sender == channel_state.our_state.address:
        recipient = channel_state.partner_state.address
    elif properties.sender == channel_state.partner_state.address:
        recipient = channel_state.our_state.address
    else:
        assert False, "Given sender does not participate in given channel."

    if compute_locksroot:
        lock = Lock(
            amount=properties.amount,
            expiration=properties.expiration,
            secrethash=sha3(properties.secret),
        )
        locksroot = merkleroot(
            channel.compute_merkletree_with(
                merkletree=channel_state.partner_state.merkletree,
                lockhash=sha3(lock.as_bytes)))
    else:
        locksroot = properties.locksroot

    if only_transfer:
        transfer_properties = LockedTransferUnsignedStateProperties(
            locksroot=locksroot,
            canonical_identifier=channel_state.canonical_identifier,
            locked_amount=properties.amount,
        )
    else:
        transfer_properties = LockedTransferUnsignedStateProperties(
            locksroot=locksroot,
            canonical_identifier=channel_state.canonical_identifier)
    transfer = create(
        LockedTransferSignedStateProperties(recipient=recipient,
                                            **transfer_properties.__dict__),
        defaults=properties,
    )

    if not allow_invalid:
        is_valid, msg, _ = channel.is_valid_lockedtransfer(
            transfer_state=transfer,
            channel_state=channel_state,
            sender_state=channel_state.partner_state,
            receiver_state=channel_state.our_state,
        )
        assert is_valid, msg

    return transfer
def test_settlement(raiden_network, settle_timeout, reveal_timeout):
    alice_app, bob_app = raiden_network  # pylint: disable=unbalanced-tuple-unpacking

    setup_messages_cb()

    alice_graph = list(alice_app.raiden.token_to_channelgraph.values())[0]
    bob_graph = list(bob_app.raiden.token_to_channelgraph.values())[0]
    assert alice_graph.token_address == bob_graph.token_address

    alice_bob_channel = alice_graph.partneraddress_to_channel[bob_app.raiden.address]
    bob_alice_channel = bob_graph.partneraddress_to_channel[alice_app.raiden.address]

    alice_deposit = alice_bob_channel.balance
    bob_deposit = bob_alice_channel.balance

    token = alice_app.raiden.chain.token(alice_bob_channel.token_address)

    alice_balance = token.balance_of(alice_app.raiden.address)
    bob_balance = token.balance_of(bob_app.raiden.address)

    alice_chain = alice_app.raiden.chain

    alice_to_bob_amount = 10
    expiration = alice_app.raiden.chain.block_number() + reveal_timeout + 1
    secret = b'secretsecretsecretsecretsecretse'
    hashlock = sha3(secret)

    assert bob_app.raiden.address in alice_graph.partneraddress_to_channel

    nettingaddress0 = alice_bob_channel.external_state.netting_channel.address
    nettingaddress1 = bob_alice_channel.external_state.netting_channel.address
    assert nettingaddress0 == nettingaddress1

    identifier = 1
    fee = 0
    transfermessage = alice_bob_channel.create_mediatedtransfer(
        alice_app.raiden.address,
        bob_app.raiden.address,
        fee,
        alice_to_bob_amount,
        identifier,
        expiration,
        hashlock,
    )
    alice_app.raiden.sign(transfermessage)
    alice_bob_channel.register_transfer(
        alice_app.raiden.get_block_number(),
        transfermessage,
    )
    bob_alice_channel.register_transfer(
        bob_app.raiden.get_block_number(),
        transfermessage,
    )

    assert_synched_channels(
        alice_bob_channel, alice_deposit, [],
        bob_alice_channel, bob_deposit, [transfermessage.lock],
    )

    # At this point we are assuming the following:
    #
    #    A -> B MediatedTransfer
    #    B -> A SecretRequest
    #    A -> B RevealSecret
    #    - protocol didn't continue
    #
    # B knowns the secret but doesn't have an updated balance proof, B needs to
    # call settle.

    # get proof, that locked transfermessage was in merkle tree, with locked.root
    lock = bob_alice_channel.partner_state.get_lock_by_hashlock(hashlock)
    assert sha3(secret) == hashlock
    unlock_proof = bob_alice_channel.partner_state.compute_proof_for_lock(secret, lock)

    root = merkleroot(bob_alice_channel.partner_state.merkletree)

    assert validate_proof(
        unlock_proof.merkle_proof,
        root,
        sha3(transfermessage.lock.as_bytes),
    )
    assert unlock_proof.lock_encoded == transfermessage.lock.as_bytes
    assert unlock_proof.secret == secret

    # a ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    balance_proof = transfermessage.to_balanceproof()
    bob_alice_channel.external_state.close(balance_proof)
    wait_until_block(alice_chain, alice_chain.block_number() + 1)

    assert alice_bob_channel.external_state.close_event.wait(timeout=15)
    assert bob_alice_channel.external_state.close_event.wait(timeout=15)

    assert alice_bob_channel.external_state.closed_block != 0
    assert bob_alice_channel.external_state.closed_block != 0
    assert alice_bob_channel.external_state.settled_block == 0
    assert bob_alice_channel.external_state.settled_block == 0

    # unlock will not be called by Channel.channel_closed because we did not
    # register the secret
    assert lock.expiration > alice_app.raiden.chain.block_number()
    assert lock.hashlock == sha3(secret)

    bob_alice_channel.external_state.netting_channel.withdraw([unlock_proof])

    settle_expiration = alice_chain.block_number() + settle_timeout + 2
    wait_until_block(alice_chain, settle_expiration)

    assert alice_bob_channel.external_state.settle_event.wait(timeout=15)
    assert bob_alice_channel.external_state.settle_event.wait(timeout=15)
    # settle must be called by the apps triggered by the ChannelClose event,
    # and the channels must update it's state based on the ChannelSettled event
    assert alice_bob_channel.external_state.settled_block != 0
    assert bob_alice_channel.external_state.settled_block != 0

    address0 = alice_app.raiden.address
    address1 = bob_app.raiden.address

    alice_netted_balance = alice_balance + alice_deposit - alice_to_bob_amount
    bob_netted_balance = bob_balance + bob_deposit + alice_to_bob_amount

    assert token.balance_of(address0) == alice_netted_balance
    assert token.balance_of(address1) == bob_netted_balance

    # Now let's query the WAL to see if the state changes were logged as expected
    state_changes = [
        change[1] for change in get_all_state_changes(alice_app.raiden.transaction_log)
        if not isinstance(change[1], Block)
    ]

    assert must_contain_entry(state_changes, ContractReceiveClosed, {
        'channel_address': nettingaddress0,
        'closing_address': bob_app.raiden.address,
        'block_number': alice_bob_channel.external_state.closed_block,
    })

    assert must_contain_entry(state_changes, ReceiveSecretReveal, {
        'secret': secret,
        'sender': bob_app.raiden.address,
    })

    assert must_contain_entry(state_changes, ContractReceiveWithdraw, {
        'channel_address': nettingaddress0,
        'secret': secret,
        'receiver': bob_app.raiden.address,
    })

    assert must_contain_entry(state_changes, ContractReceiveSettled, {
        'channel_address': nettingaddress0,
        'block_number': bob_alice_channel.external_state.settled_block,
    })
def test_withdraw_tampered_merkle_proof(tree, tester_channels, tester_chain, settle_timeout):
    """ withdraw must fail if the proof is tampered. """
    pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0]

    current_block = tester_chain.block.number
    expiration = current_block + settle_timeout - 1
    locks = [
        make_lock(
            hashlock=hashlock,
            expiration=expiration,
        )
        for hashlock in tree
    ]

    leaves = [sha3(lock.as_bytes) for lock in locks]
    layers = compute_layers(leaves)
    merkle_tree = MerkleTreeState(layers)

    opened_block = nettingchannel.opened(sender=pkey0)
    nonce = 1 + (opened_block * (2 ** 32))
    direct_transfer = make_direct_transfer(
        nonce=nonce,
        channel=channel0.channel_address,
        locksroot=merkleroot(merkle_tree),
        recipient=privatekey_to_address(pkey1)
    )

    address = privatekey_to_address(pkey0)
    sign_key = PrivateKey(pkey0)
    direct_transfer.sign(sign_key, address)

    direct_transfer_hash = sha3(direct_transfer.packed().data[:-65])
    nettingchannel.close(
        direct_transfer.nonce,
        direct_transfer.transferred_amount,
        direct_transfer.locksroot,
        direct_transfer_hash,
        direct_transfer.signature,
        sender=pkey1,
    )

    for lock in locks:
        secret = HASHLOCKS_SECRESTS[lock.hashlock]

        lock_encoded = lock.as_bytes
        merkle_proof = compute_merkleproof_for(merkle_tree, sha3(lock_encoded))

        # withdraw must fail regardless of which part of the proof is tampered
        for pos, hash_ in enumerate(merkle_proof):
            # changing arbitrary bytes from the proof
            tampered_hash = bytearray(hash_)
            tampered_hash[6], tampered_hash[7] = tampered_hash[7], tampered_hash[6]

            tampered_proof = list(merkle_proof)
            tampered_proof[pos] = tampered_hash

            joiner = b''
            with pytest.raises(TransactionFailed):
                nettingchannel.withdraw(
                    lock_encoded,
                    joiner.join(tampered_proof),
                    secret,
                    sender=pkey1,
                )
Ejemplo n.º 50
0
 def __repr__(self):
     return '<MerkleTreeState root:{}>'.format(
         pex(merkleroot(self)),
     )