mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-03 02:35:20 +00:00
verifier: small refactor
This commit is contained in:
parent
89aa9eb0a7
commit
d2abaf54e8
1 changed files with 39 additions and 25 deletions
|
@ -20,12 +20,18 @@
|
||||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
|
||||||
|
from typing import Sequence, Optional
|
||||||
|
|
||||||
from .util import ThreadJob, bh2u
|
from .util import ThreadJob, bh2u
|
||||||
from .bitcoin import Hash, hash_decode, hash_encode
|
from .bitcoin import Hash, hash_decode, hash_encode
|
||||||
from .transaction import Transaction
|
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):
|
class SPV(ThreadJob):
|
||||||
|
@ -85,28 +91,17 @@ class SPV(ThreadJob):
|
||||||
tx_hash = params[0]
|
tx_hash = params[0]
|
||||||
tx_height = merkle.get('block_height')
|
tx_height = merkle.get('block_height')
|
||||||
pos = merkle.get('pos')
|
pos = merkle.get('pos')
|
||||||
try:
|
merkle_branch = merkle.get('merkle')
|
||||||
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
|
|
||||||
header = self.network.blockchain().read_header(tx_height)
|
header = self.network.blockchain().read_header(tx_height)
|
||||||
# FIXME: if verification fails below,
|
try:
|
||||||
# we should make a fresh connection to a server to
|
verify_tx_is_in_block(tx_hash, merkle_branch, pos, header, tx_height)
|
||||||
# recover from this, as this TX will now never verify
|
except MerkleVerificationFailure as e:
|
||||||
if not header:
|
self.print_error(str(e))
|
||||||
self.print_error(
|
# FIXME: we should make a fresh connection to a server
|
||||||
"merkle verification failed for {} (missing header {})"
|
# to recover from this, as this TX will now never verify
|
||||||
.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))
|
|
||||||
return
|
return
|
||||||
# we passed all the tests
|
# we passed all the tests
|
||||||
self.merkle_roots[tx_hash] = merkle_root
|
self.merkle_roots[tx_hash] = header.get('merkle_root')
|
||||||
try:
|
try:
|
||||||
# note: we could pop in the beginning, but then we would request
|
# note: we could pop in the beginning, but then we would request
|
||||||
# this proof again in case of verification failure from the same server
|
# this proof again in case of verification failure from the same server
|
||||||
|
@ -118,11 +113,17 @@ class SPV(ThreadJob):
|
||||||
self.wallet.save_verified_tx(write=True)
|
self.wallet.save_verified_tx(write=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def hash_merkle_root(cls, merkle_s, target_hash, pos):
|
def hash_merkle_root(cls, merkle_branch: Sequence[str], tx_hash: str, leaf_pos_in_tree: int):
|
||||||
h = hash_decode(target_hash)
|
"""Return calculated merkle root."""
|
||||||
for i in range(len(merkle_s)):
|
try:
|
||||||
item = merkle_s[i]
|
h = hash_decode(tx_hash)
|
||||||
h = Hash(hash_decode(item) + h) if ((pos >> i) & 1) else Hash(h + hash_decode(item))
|
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))
|
cls._raise_if_valid_tx(bh2u(h))
|
||||||
return hash_encode(h)
|
return hash_encode(h)
|
||||||
|
|
||||||
|
@ -156,3 +157,16 @@ class SPV(ThreadJob):
|
||||||
|
|
||||||
def is_up_to_date(self):
|
def is_up_to_date(self):
|
||||||
return not self.requested_merkle
|
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))
|
||||||
|
|
Loading…
Add table
Reference in a new issue