From 23035b9aa0b5361a3e12e6e46dd76b60fb6d8eb8 Mon Sep 17 00:00:00 2001
From: Jack Robison <jackrobison@lbry.io>
Date: Sun, 29 Nov 2020 13:35:43 -0500
Subject: [PATCH] Merkle staticmethods

---
 lbry/wallet/server/merkle.py | 77 +++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 36 deletions(-)

diff --git a/lbry/wallet/server/merkle.py b/lbry/wallet/server/merkle.py
index 1a42b0185..8cf1ca08b 100644
--- a/lbry/wallet/server/merkle.py
+++ b/lbry/wallet/server/merkle.py
@@ -43,10 +43,12 @@ class Merkle:
     def __init__(self, hash_func=double_sha256):
         self.hash_func = hash_func
 
-    def tree_depth(self, hash_count):
-        return self.branch_length(hash_count) + 1
+    @staticmethod
+    def tree_depth(hash_count):
+        return Merkle.branch_length(hash_count) + 1
 
-    def branch_length(self, hash_count):
+    @staticmethod
+    def branch_length(hash_count):
         """Return the length of a merkle branch given the number of hashes."""
         if not isinstance(hash_count, int):
             raise TypeError('hash_count must be an integer')
@@ -54,7 +56,8 @@ class Merkle:
             raise ValueError('hash_count must be at least 1')
         return ceil(log(hash_count, 2))
 
-    def branch_and_root(self, hashes, index, length=None):
+    @staticmethod
+    def branch_and_root(hashes, index, length=None, hash_func=double_sha256):
         """Return a (merkle branch, merkle_root) pair given hashes, and the
         index of one of those hashes.
         """
@@ -64,7 +67,7 @@ class Merkle:
         # This also asserts hashes is not empty
         if not 0 <= index < len(hashes):
             raise ValueError(f"index '{index}/{len(hashes)}' out of range")
-        natural_length = self.branch_length(len(hashes))
+        natural_length = Merkle.branch_length(len(hashes))
         if length is None:
             length = natural_length
         else:
@@ -73,7 +76,6 @@ class Merkle:
             if length < natural_length:
                 raise ValueError('length out of range')
 
-        hash_func = self.hash_func
         branch = []
         for _ in range(length):
             if len(hashes) & 1:
@@ -85,44 +87,47 @@ class Merkle:
 
         return branch, hashes[0]
 
-    def root(self, hashes, length=None):
+    @staticmethod
+    def root(hashes, length=None):
         """Return the merkle root of a non-empty iterable of binary hashes."""
-        branch, root = self.branch_and_root(hashes, 0, length)
+        branch, root = Merkle.branch_and_root(hashes, 0, length)
         return root
 
-    def root_from_proof(self, hash, branch, index):
-        """Return the merkle root given a hash, a merkle branch to it, and
-        its index in the hashes array.
+    # @staticmethod
+    # def root_from_proof(hash, branch, index, hash_func=double_sha256):
+    #     """Return the merkle root given a hash, a merkle branch to it, and
+    #     its index in the hashes array.
+    #
+    #     branch is an iterable sorted deepest to shallowest.  If the
+    #     returned root is the expected value then the merkle proof is
+    #     verified.
+    #
+    #     The caller should have confirmed the length of the branch with
+    #     branch_length().  Unfortunately this is not easily done for
+    #     bitcoin transactions as the number of transactions in a block
+    #     is unknown to an SPV client.
+    #     """
+    #     for elt in branch:
+    #         if index & 1:
+    #             hash = hash_func(elt + hash)
+    #         else:
+    #             hash = hash_func(hash + elt)
+    #         index >>= 1
+    #     if index:
+    #         raise ValueError('index out of range for branch')
+    #     return hash
 
-        branch is an iterable sorted deepest to shallowest.  If the
-        returned root is the expected value then the merkle proof is
-        verified.
-
-        The caller should have confirmed the length of the branch with
-        branch_length().  Unfortunately this is not easily done for
-        bitcoin transactions as the number of transactions in a block
-        is unknown to an SPV client.
-        """
-        hash_func = self.hash_func
-        for elt in branch:
-            if index & 1:
-                hash = hash_func(elt + hash)
-            else:
-                hash = hash_func(hash + elt)
-            index >>= 1
-        if index:
-            raise ValueError('index out of range for branch')
-        return hash
-
-    def level(self, hashes, depth_higher):
+    @staticmethod
+    def level(hashes, depth_higher):
         """Return a level of the merkle tree of hashes the given depth
         higher than the bottom row of the original tree."""
         size = 1 << depth_higher
-        root = self.root
+        root = Merkle.root
         return [root(hashes[n: n + size], depth_higher)
                 for n in range(0, len(hashes), size)]
 
-    def branch_and_root_from_level(self, level, leaf_hashes, index,
+    @staticmethod
+    def branch_and_root_from_level(level, leaf_hashes, index,
                                    depth_higher):
         """Return a (merkle branch, merkle_root) pair when a merkle-tree has a
         level cached.
@@ -146,10 +151,10 @@ class Merkle:
         if not isinstance(leaf_hashes, list):
             raise TypeError("leaf_hashes must be a list")
         leaf_index = (index >> depth_higher) << depth_higher
-        leaf_branch, leaf_root = self.branch_and_root(
+        leaf_branch, leaf_root = Merkle.branch_and_root(
             leaf_hashes, index - leaf_index, depth_higher)
         index >>= depth_higher
-        level_branch, root = self.branch_and_root(level, index)
+        level_branch, root = Merkle.branch_and_root(level, index)
         # Check last so that we know index is in-range
         if leaf_root != level[index]:
             raise ValueError('leaf hashes inconsistent with level')