Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

66 verify proof without leaves just with proof root and leaf #69

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions merkly/mtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,61 @@ def human_leaves(self) -> List[str]:
@property
def human_short_leaves(self) -> List[str]:
return [leaf.hex() for leaf in self.short_leaves]

@staticmethod
def verify_proof(proof: List[Node], raw_leaf: str, root: str, **kwargs) -> bool:
"""
Verify the validity of a Merkle proof for a given leaf against the expected root hash.

This method checks whether the provided proof can reconstruct the root hash
from the given raw leaf data. It uses the specified hash function to compute
the hashes along the proof path.

Args:
proof (List[Node]): A list of Nodes representing the Merkle proof. Each Node
contains the hash of a sibling node and its position (left or right) in the tree.
raw_leaf (str): The raw leaf data (in string format) for which the proof is
being verified. This data should correspond to a leaf in the Merkle tree.
root (str): The expected root hash (in hexadecimal string format) that the
proof should reconstruct if valid.
**kwargs: Optional keyword arguments. Can include:
- hash_function (Callable[[bytes, bytes], bytes]): A custom hash function
that takes two byte inputs and returns a hash. If not provided,
the default `keccak` function is used.

Returns:
bool: Returns True if the proof is valid and reconstructs the expected root
hash; otherwise, returns False.

Example:
proof = [Node(data=b"abcd", side=Side.LEFT), Node(data=b"efgh", side=Side.RIGHT)]
leaf = "a"
root = "0xe35e6e14fdf91ecc6adfb74856bcd8a2c22544bd10bded94f2a9fecc77cf630b"
is_valid = MerkleTree.verify_proof(proof, leaf, root)
"""
if not kwargs.get("hash_function", None):
hash_function: Callable[[bytes, bytes], bytes] = lambda x, y: keccak(x + y)
else:
hash_function = kwargs["hash_function"]

full_proof = [hash_function(raw_leaf.encode(), bytes())]
full_proof.extend(proof)

def concat_nodes(left: Node, right: Node) -> Node:
if isinstance(left, Node) is not True:
start_node = left
if right.side == Side.RIGHT:
data = hash_function(start_node, right.data)
return Node(data=data, side=Side.LEFT)
else:
data = hash_function(right.data, start_node)
return Node(data=data, side=Side.RIGHT)
else:
if right.side == Side.RIGHT:
data = hash_function(left.data, right.data)
return Node(data=data, side=Side.LEFT)
else:
data = hash_function(right.data, left.data)
return Node(data=data, side=Side.RIGHT)

return reduce(concat_nodes, full_proof).data.hex() == root
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "merkly"
version = "1.1.1"
version = "1.2.0"
description = "🌳 The simple and easy implementation of Merkle Tree"
authors = ["Lucas Oliveira <olivmath@protonmail.com>"]
repository = "https://github.com/olivmath/merkly.git"
Expand Down Expand Up @@ -39,7 +39,7 @@ classifiers = [
pre-commit = "^3.0.3"
coverage = "^7.2.7"
pyclean = "^3.0.0"
pytest = "^7.2.1"
pytest = "^8.3.3"
black = "^24.1.1"

[build-system]
Expand Down
2 changes: 2 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> [!WARNING]

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when shortcut reference links are used. Note test

[no-shortcut-reference-link] Use the trailing [] on reference links
Dismissed Show dismissed Hide dismissed
> For run tests you need install javascript deps!
46 changes: 46 additions & 0 deletions test/merkle_proof/test_merkle_proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,49 @@

result = tree.proof(leaf)
assert tree.verify(result, leaf)


def get_data_from_api():
leaves = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
]
tree = MerkleTree(leaves)

leaf = "1"
proof = tree.proof(leaf)
return proof, leaf


def test_verify_merkle_proof_without_leaves():
proof, leaf = get_data_from_api()

root = "3aa22c94ceb510827b04fa792ebdd7346eb2984ebb24e58dac66d7795c2af4e8"

# Test valid proof
result = MerkleTree.verify_proof(proof, leaf, root)
assert result, "Expected proof to be valid"
Dismissed Show dismissed Hide dismissed
Dismissed Show dismissed Hide dismissed

# Test invalid proof scenario
invalid_leaf = "invalid_leaf_data"
invalid_result = MerkleTree.verify_proof(proof, invalid_leaf, root)
assert not invalid_result, "Expected proof to be invalid for incorrect leaf"
Dismissed Show dismissed Hide dismissed
Dismissed Show dismissed Hide dismissed

# Test with a different root
different_root = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
different_result = MerkleTree.verify_proof(proof, leaf, different_root)
assert not different_result, "Expected proof to be invalid for different root"
Dismissed Show dismissed Hide dismissed
Dismissed Show dismissed Hide dismissed
Loading