Browse Source

Add merkle proof functions to BitcoinCoreInterface

master
chris-belcher 6 years ago
parent
commit
255d15562d
No known key found for this signature in database
GPG Key ID: EF734EA677F31129
  1. 25
      jmclient/jmclient/blockchaininterface.py
  2. 40
      jmclient/jmclient/wallet_service.py

25
jmclient/jmclient/blockchaininterface.py

@ -4,6 +4,7 @@ import random
import sys import sys
import time import time
from decimal import Decimal from decimal import Decimal
import binascii
from twisted.internet import reactor, task from twisted.internet import reactor, task
import jmbitcoin as btc import jmbitcoin as btc
@ -406,6 +407,30 @@ class BitcoinCoreInterface(BlockchainInterface):
except JsonRpcError: except JsonRpcError:
return self.rpc('getblock', [blockhash])['time'] return self.rpc('getblock', [blockhash])['time']
def get_tx_merkle_branch(self, txid, blockhash=None):
if not blockhash:
tx = self.rpc("gettransaction", [txid])
if tx["confirmations"] < 1:
raise ValueError("Transaction not in block")
blockhash = tx["blockhash"]
try:
core_proof = self.rpc("gettxoutproof", [[txid], blockhash])
except JsonRpcError:
raise ValueError("Block containing transaction is pruned")
return self.core_proof_to_merkle_branch(core_proof)
def core_proof_to_merkle_branch(self, core_proof):
core_proof = binascii.unhexlify(core_proof)
#first 80 bytes of a proof given by core are just a block header
#so we can save space by replacing it with a 4-byte block height
return core_proof[80:]
def verify_tx_merkle_branch(self, txid, block_height, merkle_branch):
block_hash = self.rpc("getblockhash", [block_height])
core_proof = self.rpc("getblockheader", [block_hash, False]) + \
binascii.hexlify(merkle_branch).decode()
ret = self.rpc("verifytxoutproof", [core_proof])
return len(ret) == 1 and ret[0] == txid
class RegtestBitcoinCoreMixin(): class RegtestBitcoinCoreMixin():
""" """

40
jmclient/jmclient/wallet_service.py

@ -537,6 +537,46 @@ class WalletService(Service):
jmprint(restart_msg, "important") jmprint(restart_msg, "important")
sys.exit(EXIT_SUCCESS) sys.exit(EXIT_SUCCESS)
def sync_burner_outputs(self, burner_txes):
mixdepth = FidelityBondMixin.FIDELITY_BOND_MIXDEPTH
address_type = FidelityBondMixin.BIP32_BURN_ID
self.wallet.set_next_index(mixdepth, address_type, self.wallet.gap_limit,
force=True)
highest_used_index = 0
known_burner_outputs = self.wallet.get_burner_outputs()
for index in range(self.wallet.gap_limit):
path = self.wallet.get_path(mixdepth, address_type, index)
path_privkey, engine = self.wallet._get_priv_from_path(path)
path_pubkey = engine.privkey_to_pubkey(path_privkey)
path_pubkeyhash = btc.bin_hash160(path_pubkey)
for burner_tx in burner_txes:
burner_pubkeyhash, gettx = burner_tx
if burner_pubkeyhash != path_pubkeyhash:
continue
highest_used_index = index
path_repr = self.wallet.get_path_repr(path)
if path_repr.encode() in known_burner_outputs:
continue
txid = gettx["txid"]
jlog.info("Found a burner transaction txid=" + txid + " path = "
+ path_repr)
try:
merkle_branch = self.bci.get_tx_merkle_branch(txid, gettx["blockhash"])
except ValueError as e:
jlog.warning(repr(e))
jlog.warning("Merkle branch likely not available, use "
+ "wallet-tool `addtxoutproof`")
merkle_branch = None
block_height = self.bci.rpc("getblockheader", [gettx["blockhash"]])["height"]
if merkle_branch:
assert self.bci.verify_tx_merkle_branch(txid, block_height, merkle_branch)
self.wallet.add_burner_output(path_repr, gettx["hex"], block_height,
merkle_branch, gettx["blockindex"])
self.wallet.set_next_index(mixdepth, address_type, highest_used_index + 1)
def sync_addresses(self): def sync_addresses(self):
""" Triggered by use of --recoversync option in scripts, """ Triggered by use of --recoversync option in scripts,
attempts a full scan of the blockchain without assuming attempts a full scan of the blockchain without assuming

Loading…
Cancel
Save