URI: 
       tverifier: small refactor - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit d2abaf54e80621d2a4076c7dd996e08038e9eade
   DIR parent 89aa9eb0a7ecdde51ae9e4989d06e14b0122e514
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Mon, 23 Jul 2018 19:55:47 +0200
       
       verifier: small refactor
       
       Diffstat:
         M electrum/verifier.py                |      64 +++++++++++++++++++------------
       
       1 file changed, 39 insertions(+), 25 deletions(-)
       ---
   DIR diff --git a/electrum/verifier.py b/electrum/verifier.py
       t@@ -20,12 +20,18 @@
        # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
        # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        # SOFTWARE.
       +
       +from typing import Sequence, Optional
       +
        from .util import ThreadJob, bh2u
        from .bitcoin import Hash, hash_decode, hash_encode
        from .transaction import Transaction
        
        
       -class InnerNodeOfSpvProofIsValidTx(Exception): pass
       +class MerkleVerificationFailure(Exception): pass
       +class MissingBlockHeader(MerkleVerificationFailure): pass
       +class MerkleRootMismatch(MerkleVerificationFailure): pass
       +class InnerNodeOfSpvProofIsValidTx(MerkleVerificationFailure): pass
        
        
        class SPV(ThreadJob):
       t@@ -85,28 +91,17 @@ class SPV(ThreadJob):
                tx_hash = params[0]
                tx_height = merkle.get('block_height')
                pos = merkle.get('pos')
       -        try:
       -            merkle_root = self.hash_merkle_root(merkle['merkle'], tx_hash, pos)
       -        except InnerNodeOfSpvProofIsValidTx:
       -            self.print_error("merkle verification failed for {} (inner node looks like tx)"
       -                             .format(tx_hash))
       -            return
       +        merkle_branch = merkle.get('merkle')
                header = self.network.blockchain().read_header(tx_height)
       -        # FIXME: if verification fails below,
       -        # we should make a fresh connection to a server to
       -        # recover from this, as this TX will now never verify
       -        if not header:
       -            self.print_error(
       -                "merkle verification failed for {} (missing header {})"
       -                .format(tx_hash, tx_height))
       -            return
       -        if header.get('merkle_root') != merkle_root:
       -            self.print_error(
       -                "merkle verification failed for {} (merkle root mismatch {} != {})"
       -                .format(tx_hash, header.get('merkle_root'), merkle_root))
       +        try:
       +            verify_tx_is_in_block(tx_hash, merkle_branch, pos, header, tx_height)
       +        except MerkleVerificationFailure as e:
       +            self.print_error(str(e))
       +            # FIXME: we should make a fresh connection to a server
       +            # to recover from this, as this TX will now never verify
                    return
                # we passed all the tests
       -        self.merkle_roots[tx_hash] = merkle_root
       +        self.merkle_roots[tx_hash] = header.get('merkle_root')
                try:
                    # note: we could pop in the beginning, but then we would request
                    # this proof again in case of verification failure from the same server
       t@@ -118,11 +113,17 @@ class SPV(ThreadJob):
                    self.wallet.save_verified_tx(write=True)
        
            @classmethod
       -    def hash_merkle_root(cls, merkle_s, target_hash, pos):
       -        h = hash_decode(target_hash)
       -        for i in range(len(merkle_s)):
       -            item = merkle_s[i]
       -            h = Hash(hash_decode(item) + h) if ((pos >> i) & 1) else Hash(h + hash_decode(item))
       +    def hash_merkle_root(cls, merkle_branch: Sequence[str], tx_hash: str, leaf_pos_in_tree: int):
       +        """Return calculated merkle root."""
       +        try:
       +            h = hash_decode(tx_hash)
       +            merkle_branch_bytes = [hash_decode(item) for item in merkle_branch]
       +            int(leaf_pos_in_tree)  # raise if invalid
       +        except Exception as e:
       +            raise MerkleVerificationFailure(e)
       +
       +        for i, item in enumerate(merkle_branch_bytes):
       +            h = Hash(item + h) if ((leaf_pos_in_tree >> i) & 1) else Hash(h + item)
                    cls._raise_if_valid_tx(bh2u(h))
                return hash_encode(h)
        
       t@@ -156,3 +157,16 @@ class SPV(ThreadJob):
        
            def is_up_to_date(self):
                return not self.requested_merkle
       +
       +
       +def verify_tx_is_in_block(tx_hash: str, merkle_branch: Sequence[str],
       +                          leaf_pos_in_tree: int, block_header: Optional[dict],
       +                          block_height: int) -> None:
       +    """Raise MerkleVerificationFailure if verification fails."""
       +    if not block_header:
       +        raise MissingBlockHeader("merkle verification failed for {} (missing header {})"
       +                                 .format(tx_hash, block_height))
       +    calc_merkle_root = SPV.hash_merkle_root(merkle_branch, tx_hash, leaf_pos_in_tree)
       +    if block_header.get('merkle_root') != calc_merkle_root:
       +        raise MerkleRootMismatch("merkle verification failed for {} ({} != {})".format(
       +            tx_hash, block_header.get('merkle_root'), calc_merkle_root))