From b1148267254a1e20c71bfc7933d223f852948548 Mon Sep 17 00:00:00 2001 From: Frederic Lepied Date: Fri, 15 Oct 2021 08:56:53 +0200 Subject: [PATCH] contrib/xpub.py support ypub/zpub --- contrib/history.sh | 19 +------- contrib/venv_wrapper.sh | 18 ++++++++ contrib/xpub.py | 97 +++++++++++++++++++++++++++++++---------- contrib/xpub.sh | 1 + 4 files changed, 95 insertions(+), 40 deletions(-) mode change 100755 => 120000 contrib/history.sh create mode 100755 contrib/venv_wrapper.sh create mode 120000 contrib/xpub.sh diff --git a/contrib/history.sh b/contrib/history.sh deleted file mode 100755 index b320eb8..0000000 --- a/contrib/history.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -eu - -cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" - -venv=false -if [ "$1" = "--venv" ]; -then - shift - venv=true -fi - -if [ $venv = true ] && [ ! -d .venv ]; then - virtualenv .venv - .venv/bin/pip install pycoin logbook prettytable -fi - -exec .venv/bin/python history.py "$@" diff --git a/contrib/history.sh b/contrib/history.sh new file mode 120000 index 0000000..a7906e5 --- /dev/null +++ b/contrib/history.sh @@ -0,0 +1 @@ +venv_wrapper.sh \ No newline at end of file diff --git a/contrib/venv_wrapper.sh b/contrib/venv_wrapper.sh new file mode 100755 index 0000000..0464f16 --- /dev/null +++ b/contrib/venv_wrapper.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" + +cmd="$(basename -- "${BASH_SOURCE[0]}" .sh)".py + +if [ "$1" = "--venv" ]; then + shift + if [ ! -d .venv ]; then + virtualenv .venv + .venv/bin/pip install pycoin logbook prettytable base58 + fi + PATH=$PWD/.venv/bin:$PATH +fi + +exec python "$cmd" "$@" diff --git a/contrib/xpub.py b/contrib/xpub.py index 33f5b58..df3c94b 100755 --- a/contrib/xpub.py +++ b/contrib/xpub.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import argparse +import base58 import hashlib import sys @@ -10,10 +11,71 @@ import client log = Logger("xpub") +prefix_dict = { + 'mainnet': { + 'xpub': '0488b21e', # P2PKH or P2SH - m/44'/0' + 'ypub': '049d7cb2', # P2WPKH in P2SH - m/49'/0' + 'zpub': '04b24746', # P2WPKH - m/84'/0' + }, + 'testnet': { + 'tpub': '043587cf', # P2PKH or P2SH - m/44'/1' + 'upub': '044a5262', # P2WPKH in P2SH - m/49'/1' + 'vpub': '045f1cf6', # P2WPKH - m/84'/1' + }, + 'regtest': { + }, +} + + +def convert_key(key, target_prefix, network_name): + decoded_key_bytes = base58.b58decode_check(key) + target_key_bytes = ( + bytes.fromhex(prefix_dict[network_name][target_prefix]) + + decoded_key_bytes[4:]) + return base58.b58encode_check(target_key_bytes).decode('ascii') + + +def compute_balance(xpub, conn, network): + total = 0 + for change in (0, 1): + empty = 0 + for n in range(1000): + address = xpub.subkey(change).subkey(n).address() + script = network.parse.address(address).script() + script_hash = hashlib.sha256(script).digest()[::-1].hex() + # conn.call([client.request('blockchain.scripthash.subscribe', + # script_hash)]) + result, = conn.call( + [client.request('blockchain.scripthash.get_history', + script_hash)]) + ntx = len(result) + if len(result): + log.debug(result) + result, = conn.call( + [client.request('blockchain.scripthash.get_balance', + script_hash)]) + confirmed = result['confirmed'] / 1e8 + total += confirmed + + log.info( + '{}/{}: {} -> {} BTC confirmed, {} BTC unconfirmed, ' + '{} txs balance = {} BTC', change, n, address, + result["confirmed"] / 1e8, result["unconfirmed"] / 1e8, ntx, total) + + if confirmed or ntx: + empty = 0 + else: + empty += 1 + if empty >= 10: + break + return total + + def main(): parser = argparse.ArgumentParser() parser.add_argument('--host', default='localhost') - parser.add_argument('--network', default='mainnet') + parser.add_argument('--network', default='mainnet', + choices=['mainnet', 'testnet', 'regtest']) parser.add_argument('xpub') args = parser.parse_args() @@ -31,32 +93,23 @@ def main(): conn = client.Client((args.host, port)) total = 0 - xpub = network.parse.bip32(args.xpub) + xpub = (network.parse.bip32(args.xpub) or network.parse.bip49(args.xpub) or + network.parse.bip84(args.xpub)) if xpub is None: - log.error('Invalid BIP32 pub key %s' % args.xpub) + log.error('Invalid BIP32/BIP49/BIP84 pub key %s' % args.xpub) sys.exit(1) - for change in (0, 1): - empty = 0 - for n in range(1000): - address = xpub.subkey(change).subkey(n).address() - script = network.parse.address(address).script() - script_hash = hashlib.sha256(script).digest()[::-1].hex() - # conn.call([client.request('blockchain.scripthash.subscribe', script_hash)]) - result, = conn.call([client.request('blockchain.scripthash.get_history', script_hash)]) - ntx = len(result) - result, = conn.call([client.request('blockchain.scripthash.get_balance', script_hash)]) - log.info('{}/{}: {} -> {} BTC confirmed, {} BTC unconfirmed, {} txs', change, n, address, result["confirmed"], result["unconfirmed"], ntx) + total = compute_balance(xpub, conn, network) + + for prefix in prefix_dict[args.network]: + if args.xpub[:4] != prefix: + key = convert_key(args.xpub, prefix, args.network) + log.info('Trying with {}', key) + xpub = (network.parse.bip32(key) or network.parse.bip49(key) + or network.parse.bip84(key)) + total += compute_balance(xpub, conn, network) - confirmed = result['confirmed'] / 1e8 - total += confirmed - if confirmed or ntx: - empty = 0 - else: - empty += 1 - if empty >= 20: - break log.info('total balance: {} BTC', total) diff --git a/contrib/xpub.sh b/contrib/xpub.sh new file mode 120000 index 0000000..a7906e5 --- /dev/null +++ b/contrib/xpub.sh @@ -0,0 +1 @@ +venv_wrapper.sh \ No newline at end of file