Skip to content

Commit

Permalink
Create a new kind of watch only wallet: SegwitWatchonlyWallet
Browse files Browse the repository at this point in the history
  • Loading branch information
BitcoinWukong authored and Wukong committed Dec 10, 2021
1 parent e0236a8 commit f90a760
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 19 deletions.
5 changes: 4 additions & 1 deletion jmbitcoin/jmbitcoin/secp256k1_deterministic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
# Below code ASSUMES binary inputs and compressed pubkeys
MAINNET_PRIVATE = b'\x04\x88\xAD\xE4'
MAINNET_PUBLIC = b'\x04\x88\xB2\x1E'
MAINNET_PUBLIC_P2SH_P2WPKH = b'\x04\x9D\x7C\xB2'
MAINNET_PUBLIC_P2WPKH = b'\x04\xB2\x47\x46'

TESTNET_PRIVATE = b'\x04\x35\x83\x94'
TESTNET_PUBLIC = b'\x04\x35\x87\xCF'
SIGNET_PRIVATE = b'\x04\x35\x83\x94'
SIGNET_PUBLIC = b'\x04\x35\x87\xCF'
PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE, SIGNET_PRIVATE]
PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC, SIGNET_PUBLIC]
PUBLIC = [MAINNET_PUBLIC, MAINNET_PUBLIC_P2SH_P2WPKH, MAINNET_PUBLIC_P2WPKH, TESTNET_PUBLIC, SIGNET_PUBLIC]

privtopub = privkey_to_pubkey

Expand Down
31 changes: 24 additions & 7 deletions jmclient/jmclient/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -2581,14 +2581,10 @@ class SegwitLegacyWallet(ImportWalletMixin, BIP39WalletMixin, PSBTWalletMixin, S
class SegwitWallet(ImportWalletMixin, BIP39WalletMixin, PSBTWalletMixin, SNICKERWalletMixin, BIP84Wallet):
TYPE = TYPE_P2WPKH

class SegwitWalletFidelityBonds(FidelityBondMixin, SegwitWallet):
TYPE = TYPE_SEGWIT_WALLET_FIDELITY_BONDS


class FidelityBondWatchonlyWallet(FidelityBondMixin, BIP84Wallet):
TYPE = TYPE_WATCHONLY_FIDELITY_BONDS
_ENGINE = ENGINES[TYPE_WATCHONLY_P2WPKH]
_TIMELOCK_ENGINE = ENGINES[TYPE_WATCHONLY_TIMELOCK_P2WSH]
class WatchonlyMixin(object):
# When watching an external wallet, we only watch account 0
WATCH_ONLY_MIXDEPTH = 0

@classmethod
def _verify_entropy(cls, ent):
Expand All @@ -2598,6 +2594,26 @@ def _verify_entropy(cls, ent):
def _derive_bip32_master_key(cls, master_entropy):
return btc.bip32_deserialize(master_entropy.decode())


class SegwitWatchonlyWallet(WatchonlyMixin, BIP84Wallet):
TYPE = TYPE_WATCHONLY_P2WPKH
_ENGINE = ENGINES[TYPE_WATCHONLY_P2WPKH]

def _get_key_ident(self):
return sha256(sha256(
self.get_bip32_pub_export(0, self.BIP32_EXT_ID).encode('ascii')).digest())\
.digest()[:3]


class SegwitWalletFidelityBonds(FidelityBondMixin, SegwitWallet):
TYPE = TYPE_SEGWIT_WALLET_FIDELITY_BONDS


class FidelityBondWatchonlyWallet(FidelityBondMixin, WatchonlyMixin, BIP84Wallet):
TYPE = TYPE_WATCHONLY_FIDELITY_BONDS
_ENGINE = ENGINES[TYPE_WATCHONLY_P2WPKH]
_TIMELOCK_ENGINE = ENGINES[TYPE_WATCHONLY_TIMELOCK_P2WSH]

def _get_bip32_export_path(self, mixdepth=None, address_type=None):
path = super()._get_bip32_export_path(mixdepth, address_type)
return path
Expand All @@ -2607,6 +2623,7 @@ def _get_bip32_export_path(self, mixdepth=None, address_type=None):
LegacyWallet.TYPE: LegacyWallet,
SegwitLegacyWallet.TYPE: SegwitLegacyWallet,
SegwitWallet.TYPE: SegwitWallet,
SegwitWatchonlyWallet.TYPE: SegwitWatchonlyWallet,
SegwitWalletFidelityBonds.TYPE: SegwitWalletFidelityBonds,
FidelityBondWatchonlyWallet.TYPE: FidelityBondWatchonlyWallet
}
38 changes: 27 additions & 11 deletions jmclient/jmclient/wallet_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
VolatileStorage, StoragePasswordError, is_segwit_mode, SegwitLegacyWallet,
LegacyWallet, SegwitWallet, FidelityBondMixin, FidelityBondWatchonlyWallet,
is_native_segwit_mode, load_program_config, add_base_options, check_regtest)
from jmclient.wallet import SegwitWatchonlyWallet, WatchonlyMixin
from jmclient.wallet_service import WalletService
from jmbase.support import (get_password, jmprint, EXIT_FAILURE,
EXIT_ARGERROR, utxo_to_utxostr, hextobin, bintohex,
Expand Down Expand Up @@ -51,7 +52,8 @@ def get_wallettool_parser():
(gettimelockaddress) Obtain a timelocked address. Argument is locktime value as yyyy-mm. For example `2021-03`.
(addtxoutproof) Add a tx out proof as metadata to a burner transaction. Specify path with
-H and proof which is output of Bitcoin Core\'s RPC call gettxoutproof.
(createwatchonly) Create a watch-only fidelity bond wallet.
(createwatchonly) Create a watch-only wallet.
(createfbwatchonly) Create a watch-only fidelity bond wallet.
(setlabel) Set the label associated with the given address.
"""
parser = OptionParser(usage='usage: %prog [options] [wallet file] [method] [args..]',
Expand Down Expand Up @@ -235,7 +237,7 @@ def __init__(self, wallet_path_repr, account, address_type, branchentries=None,
FidelityBondMixin.BIP32_BURN_ID]
self.address_type = address_type
if xpub:
assert xpub.startswith('xpub') or xpub.startswith('tpub')
assert xpub.startswith('xpub') or xpub.startswith('tpub') or xpub.startswith('ypub') or xpub.startswith('zpub')
self.xpub = xpub if xpub else ""
self.branchentries = branchentries

Expand Down Expand Up @@ -1315,7 +1317,7 @@ def wallet_addtxoutproof(wallet_service, hdpath, txoutproof):
new_merkle_branch, block_index)
return "Done"

def wallet_createwatchonly(wallet_root_path, master_pub_key):
def wallet_createwatchonly(wallet_root_path, master_pub_key, is_fidelity_bond_wallet = False):

wallet_name = cli_get_wallet_file_name(defaultname="watchonly.jmdat")
if not wallet_name:
Expand All @@ -1326,17 +1328,26 @@ def wallet_createwatchonly(wallet_root_path, master_pub_key):

password = cli_get_wallet_passphrase_check()
if not password:
jmprint("The passphrase can not be empty", "error")
return ""

entropy = FidelityBondMixin.get_xpub_from_fidelity_bond_master_pub_key(master_pub_key)
if not entropy:
jmprint("Error with provided master pub key", "error")
return ""
if is_fidelity_bond_wallet:
entropy = FidelityBondMixin.get_xpub_from_fidelity_bond_master_pub_key(master_pub_key)
if not entropy:
jmprint("Error with provided master public key", "error")
return ""
else:
entropy = master_pub_key
entropy = entropy.encode()

wallet = create_wallet(wallet_path, password,
max_mixdepth=FidelityBondMixin.FIDELITY_BOND_MIXDEPTH,
wallet_cls=FidelityBondWatchonlyWallet, entropy=entropy)
if is_fidelity_bond_wallet:
create_wallet(wallet_path, password,
max_mixdepth=FidelityBondMixin.FIDELITY_BOND_MIXDEPTH,
wallet_cls=FidelityBondWatchonlyWallet, entropy=entropy)
else:
create_wallet(wallet_path, password,
max_mixdepth=WatchonlyMixin.WATCH_ONLY_MIXDEPTH,
wallet_cls=SegwitWatchonlyWallet, entropy=entropy)
return "Done"

def get_configured_wallet_type(support_fidelity_bonds):
Expand Down Expand Up @@ -1503,7 +1514,7 @@ def wallet_tool_main(wallet_root_path):
check_regtest(blockchain_start=False)
# full path to the wallets/ subdirectory in the user data area:
wallet_root_path = os.path.join(jm_single().datadir, wallet_root_path)
noseed_methods = ['generate', 'recover', 'createwatchonly']
noseed_methods = ['generate', 'recover', 'createwatchonly', 'createfbwatchonly']
methods = ['display', 'displayall', 'summary', 'showseed', 'importprivkey',
'history', 'showutxos', 'freeze', 'gettimelockaddress',
'addtxoutproof', 'changepass', 'setlabel']
Expand Down Expand Up @@ -1626,6 +1637,11 @@ def wallet_tool_main(wallet_root_path):
+ 'Core\'s RPC call gettxoutproof', "error")
sys.exit(EXIT_ARGERROR)
return wallet_addtxoutproof(wallet_service, options.hd_path, args[2])
elif method == "createfbwatchonly":
if len(args) < 2:
jmprint("args: [master public key]", "error")
sys.exit(EXIT_ARGERROR)
return wallet_createwatchonly(wallet_root_path, args[1], is_fidelity_bond_wallet=True)
elif method == "createwatchonly":
if len(args) < 2:
jmprint("args: [master public key]", "error")
Expand Down

0 comments on commit f90a760

Please sign in to comment.