def test_compute_layers_duplicated(): hash_0 = sha3(b'x') hash_1 = sha3(b'y') with pytest.raises(ValueError): compute_layers([hash_0, hash_0]) with pytest.raises(ValueError): compute_layers([hash_0, hash_1, hash_0])
def test_compute_layers_duplicated(): hash_0 = sha3(b'x') hash_1 = sha3(b'y') with pytest.raises(ValueError): compute_layers([hash_0, hash_0]) with pytest.raises(ValueError): compute_layers([hash_0, hash_1, hash_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)
def test_merkle_proof_missing_byte(tree, tester_chain, tester_nettingchannel_library_address): """ computeMerkleRoot must fail if the proof is missing a byte. """ auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) hashes = [sha3(element) for element in tree] layers = compute_layers(hashes) merkletree = MerkleTreeState(layers) element = hashes[-1] proof = compute_merkleproof_for(merkletree, element) # for each element of the proof, remove a byte from the start and the end and test it for element_to_tamper in range(len(proof)): tampered_proof = list(proof) tampered_proof[element_to_tamper] = tampered_proof[ element_to_tamper][:-1] with pytest.raises(TransactionFailed): auxiliary.computeMerkleRoot( element, b''.join(tampered_proof), ) tampered_proof = list(proof) tampered_proof[element_to_tamper] = tampered_proof[element_to_tamper][ 1:] with pytest.raises(TransactionFailed): auxiliary.computeMerkleRoot( element, b''.join(tampered_proof), )
def deserialize_merkletree_layers(data: typing.List[str]): elements = map_list(deserialize_bytes, data) if len(elements) == 0: from raiden.transfer.state import make_empty_merkle_tree return make_empty_merkle_tree().layers return compute_layers(elements)
def test_merkle_proof_missing_byte( tree, tester_chain, tester_nettingchannel_library_address): """ computeMerkleRoot must fail if the proof is missing a byte. """ auxiliary = deploy_auxiliary_tester(tester_chain, tester_nettingchannel_library_address) hashes = [sha3(element) for element in tree] layers = compute_layers(hashes) merkletree = MerkleTreeState(layers) element = hashes[-1] proof = compute_merkleproof_for(merkletree, element) # for each element of the proof, remove a byte from the start and the end and test it for element_to_tamper in range(len(proof)): tampered_proof = list(proof) tampered_proof[element_to_tamper] = tampered_proof[element_to_tamper][:-1] with pytest.raises(TransactionFailed): auxiliary.computeMerkleRoot( element, b''.join(tampered_proof), ) tampered_proof = list(proof) tampered_proof[element_to_tamper] = tampered_proof[element_to_tamper][1:] with pytest.raises(TransactionFailed): auxiliary.computeMerkleRoot( element, b''.join(tampered_proof), )
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
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_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)
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
def deserialize_merkletree_layers(data: List[str]) -> List[List[Keccak256]]: elements = cast(List[Keccak256], map_list(deserialize_bytes, data)) if len(elements) == 0: from raiden.transfer.state import make_empty_merkle_tree return make_empty_merkle_tree().layers return compute_layers(elements)
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, )
def deserialize_merkletree_layers(data: typing.List[str]): elements = map_list(deserialize_bytes, data) if len(elements) == 0: return [ [], # the leaves are empty [bytes(32)], # the root is the constant 0 ] return compute_layers(elements)
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)
def test_serialization_merkletree_layers(): hash_0 = b"a" * 32 hash_1 = b"b" * 32 leaves = [hash_0, hash_1] layers = compute_layers(leaves) data = serialization.serialize_merkletree_layers(layers) restored = serialization.deserialize_merkletree_layers(data) assert layers == restored
def make_receive_transfer_mediated( channel_state, privkey, nonce, transferred_amount, lock, merkletree_leaves=None): if not isinstance(lock, HashTimeLockState): raise ValueError('lock must be of type HashTimeLockState') 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 merkletree_leaves is None: layers = [[lock.lockhash]] else: assert lock.lockhash in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] message_identifier = random.randint(0, UINT64_MAX) payment_identifier = nonce transfer_target = factories.make_address() transfer_initiator = factories.make_address() mediated_transfer_msg = LockedTransfer( message_identifier, payment_identifier, nonce, channel_state.token_address, channel_state.identifier, transferred_amount, channel_state.partner_state.address, locksroot, lock, transfer_target, transfer_initiator, ) mediated_transfer_msg.sign(privkey, address) balance_proof = balanceproof_from_envelope(mediated_transfer_msg) receive_lockedtransfer = LockedTransferSignedState( payment_identifier, channel_state.token_address, balance_proof, lock, transfer_initiator, transfer_target, ) return receive_lockedtransfer
def test_serialization_merkletree_layers(): hash_0 = b'a' * 32 hash_1 = b'b' * 32 leaves = [hash_0, hash_1] layers = compute_layers(leaves) data = serialization.serialize_merkletree_layers(layers) restored = serialization.deserialize_merkletree_layers(data) assert layers == restored
def create(self) -> NettingChannelEndState: state = NettingChannelEndState(self.address or make_address(), self.balance) merkletree_leaves = (self.merkletree_leaves or make_merkletree_leaves(self.merkletree_width) or None) if merkletree_leaves: state.merkletree = MerkleTreeState( compute_layers(merkletree_leaves)) return state
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 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 make_receive_expired_lock( channel_state, privkey, nonce, transferred_amount, lock, merkletree_leaves=None, locked_amount=None, chain_id=None, ): if not isinstance(lock, HashTimeLockState): raise ValueError('lock must be of type HashTimeLockState') 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 merkletree_leaves is None: layers = EMPTY_MERKLE_TREE.layers else: assert lock.lockhash not in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] chain_id = chain_id or channel_state.chain_id lock_expired_msg = LockExpired( chain_id=chain_id, nonce=nonce, message_identifier=random.randint(0, UINT64_MAX), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, channel_identifier=channel_state.identifier, token_network_address=channel_state.token_network_identifier, recipient=channel_state.partner_state.address, secrethash=lock.secrethash, ) lock_expired_msg.sign(privkey) balance_proof = balanceproof_from_envelope(lock_expired_msg) receive_lockedtransfer = ReceiveLockExpired( channel_state.partner_state.address, balance_proof, lock.secrethash, random.randint(0, UINT64_MAX), ) return receive_lockedtransfer
def make_receive_expired_lock( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, merkletree_leaves: List[Keccak256] = None, locked_amount: LockedAmount = None, chain_id: ChainID = None, ) -> ReceiveLockExpired: if not isinstance(lock, HashTimeLockState): raise ValueError("lock must be of type HashTimeLockState") signer = LocalSigner(privkey) address = signer.address 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 merkletree_leaves is None: layers = make_empty_merkle_tree().layers else: assert lock.lockhash not in merkletree_leaves layers = compute_layers(merkletree_leaves) locksroot = layers[MERKLEROOT][0] chain_id = chain_id or channel_state.chain_id lock_expired_msg = LockExpired( chain_id=chain_id, nonce=nonce, message_identifier=random.randint(0, UINT64_MAX), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=locksroot, channel_identifier=channel_state.identifier, token_network_address=channel_state.token_network_identifier, recipient=channel_state.partner_state.address, secrethash=lock.secrethash, ) lock_expired_msg.sign(signer) balance_proof = balanceproof_from_envelope(lock_expired_msg) receive_lockedtransfer = ReceiveLockExpired( balance_proof=balance_proof, secrethash=lock.secrethash, message_identifier=random.randint(0, UINT64_MAX), ) return receive_lockedtransfer
def _(properties, defaults=None) -> NettingChannelEndState: args = _properties_to_kwargs(properties, defaults or NETTING_CHANNEL_END_STATE_DEFAULTS) state = NettingChannelEndState(args['address'] or make_address(), args['balance']) merkletree_leaves = ( args['merkletree_leaves'] or make_merkletree_leaves(args['merkletree_width']) or None ) if merkletree_leaves: state.merkletree = MerkleTreeState(compute_layers(merkletree_leaves)) return state
def _(properties, defaults=None) -> NettingChannelEndState: args = _properties_to_kwargs( properties, defaults or NETTING_CHANNEL_END_STATE_DEFAULTS) state = NettingChannelEndState(args['address'] or make_address(), args['balance']) merkletree_leaves = (args['merkletree_leaves'] or make_merkletree_leaves(args['merkletree_width']) or None) if merkletree_leaves: state.merkletree = MerkleTreeState(compute_layers(merkletree_leaves)) return state
def _(properties, defaults=None) -> NettingChannelEndState: args = _properties_to_kwargs( properties, defaults or NettingChannelEndStateProperties.DEFAULTS) state = NettingChannelEndState(args["address"] or make_address(), args["balance"]) merkletree_leaves = (args["merkletree_leaves"] or make_merkletree_leaves(args["merkletree_width"]) or None) if merkletree_leaves: state.merkletree = MerkleTreeState(compute_layers(merkletree_leaves)) return state
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
def compute_merkletree_with( merkletree: MerkleTreeState, lockhash: typing.LockHash, ) -> typing.Optional[MerkleTreeState]: """Register the given lockhash with the existing merkle tree.""" # Use None to inform the caller the lockshash is already known result = None leaves = merkletree.layers[LEAVES] if lockhash not in leaves: leaves = list(leaves) leaves.append(lockhash) result = MerkleTreeState(compute_layers(leaves)) return result
def compute_merkletree_without(merkletree, lockhash): # Use None to inform the caller the lockshash is unknown result = None leaves = merkletree.layers[LEAVES] if lockhash in leaves: leaves = list(leaves) leaves.remove(lockhash) if leaves: result = MerkleTreeState(compute_layers(leaves)) else: result = EMPTY_MERKLE_TREE return result
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.encoded) for lock in pending_locks] layers = compute_layers(leaves) tree = MerkleTreeState(layers) else: tree = EMPTY_MERKLE_TREE assert from_channel.our_state.merkletree == tree for lock in pending_locks: pending = lock.secrethash in from_channel.our_state.secrethashes_to_lockedlocks unclaimed = lock.secrethash in from_channel.our_state.secrethashes_to_unlockedlocks assert pending or unclaimed
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.encoded) for lock in pending_locks] layers = compute_layers(leaves) tree = MerkleTreeState(layers) else: tree = EMPTY_MERKLE_TREE assert from_channel.our_state.merkletree == tree for lock in pending_locks: pending = lock.secrethash in from_channel.our_state.secrethashes_to_lockedlocks unclaimed = lock.secrethash in from_channel.our_state.secrethashes_to_unlockedlocks assert pending or unclaimed
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
def assert_locked(from_channel: NettingChannelState, pending_locks: List[HashTimeLockState]) -> None: """ Assert the locks created from `from_channel`. """ # a locked transfer is registered in the _partner_ state if pending_locks: leaves = [sha3(lock.encoded) for lock in pending_locks] layers = compute_layers(leaves) tree = MerkleTreeState(layers) else: tree = make_empty_merkle_tree() assert from_channel.our_state.merkletree == tree for lock in pending_locks: pending = lock.secrethash in from_channel.our_state.secrethashes_to_lockedlocks unclaimed = lock.secrethash in from_channel.our_state.secrethashes_to_unlockedlocks assert pending or unclaimed
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
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)
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)
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
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
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
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)
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_compute_layers_empty(): with pytest.raises(AssertionError): compute_layers([])
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, )
def test_compute_layers_invalid_length(): with pytest.raises(HashLengthNot32): compute_layers(['not32bytes', 'neither']) with pytest.raises(HashLengthNot32): compute_layers([''])
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, )
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, )
def restore_channel(self, serialized_channel): token_address = serialized_channel.token_address netting_channel = self.chain.netting_channel( serialized_channel.channel_address, ) # restoring balances from the blockchain since the serialized # value could be falling behind. channel_details = netting_channel.detail() # our_address is checked by detail assert channel_details['partner_address'] == serialized_channel.partner_address if serialized_channel.our_leaves: our_layers = compute_layers(serialized_channel.our_leaves) our_tree = MerkleTreeState(our_layers) else: our_tree = EMPTY_MERKLE_TREE our_state = ChannelEndState( channel_details['our_address'], channel_details['our_balance'], serialized_channel.our_balance_proof, our_tree, ) if serialized_channel.partner_leaves: partner_layers = compute_layers(serialized_channel.partner_leaves) partner_tree = MerkleTreeState(partner_layers) else: partner_tree = EMPTY_MERKLE_TREE partner_state = ChannelEndState( channel_details['partner_address'], channel_details['partner_balance'], serialized_channel.partner_balance_proof, partner_tree, ) def register_channel_for_hashlock(channel, hashlock): self.register_channel_for_hashlock( token_address, channel, hashlock, ) external_state = ChannelExternalState( register_channel_for_hashlock, netting_channel, ) details = ChannelDetails( serialized_channel.channel_address, our_state, partner_state, external_state, serialized_channel.reveal_timeout, channel_details['settle_timeout'], ) graph = self.token_to_channelgraph[token_address] graph.add_channel(details) channel = graph.address_to_channel.get( serialized_channel.channel_address, ) channel.our_state.balance_proof = serialized_channel.our_balance_proof channel.partner_state.balance_proof = serialized_channel.partner_balance_proof