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
Wukong committed Aug 12, 2021
1 parent fb39857 commit ad5b2a5
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 20 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
5 changes: 5 additions & 0 deletions jmclient/jmclient/cryptoengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ def derive_bip32_pub_export(cls, master_key, path):
return super(BTC_Watchonly_P2WPKH, cls).derive_bip32_pub_export(
master_key, BTC_Watchonly_Timelocked_P2WSH.get_watchonly_path(path))

@classmethod
def derive_bip32_priv_export(cls, master_key, path):
#return pub key instead of priv key in watchonly wallet
return BTC_Watchonly_P2WPKH.derive_bip32_pub_export(master_key, path)

@classmethod
def sign_transaction(cls, tx, index, privkey, amount,
hashcode=btc.SIGHASH_ALL, **kwargs):
Expand Down
28 changes: 20 additions & 8 deletions jmclient/jmclient/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from .blockchaininterface import INF_HEIGHT
from .support import select_gradual, select_greedy, select_greediest, \
select, NotEnoughFundsException
from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH,\
from .cryptoengine import BTC_Watchonly_P2WPKH, TYPE_P2PKH, TYPE_P2SH_P2WPKH,\
TYPE_P2WPKH, TYPE_TIMELOCK_P2WSH, TYPE_SEGWIT_WALLET_FIDELITY_BONDS,\
TYPE_WATCHONLY_FIDELITY_BONDS, TYPE_WATCHONLY_TIMELOCK_P2WSH, TYPE_WATCHONLY_P2WPKH,\
ENGINES
Expand Down Expand Up @@ -2485,14 +2485,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 @@ -2502,6 +2498,21 @@ 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]


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 @@ -2511,6 +2522,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
}
37 changes: 26 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.
"""
parser = OptionParser(usage='usage: %prog [options] [wallet file] [method] [args..]',
description=description, formatter=IndentedHelpFormatterWithNL())
Expand Down Expand Up @@ -214,7 +216,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 @@ -1268,7 +1270,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 @@ -1281,15 +1283,23 @@ def wallet_createwatchonly(wallet_root_path, master_pub_key):
if not password:
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 pub 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 @@ -1453,7 +1463,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']
Expand Down Expand Up @@ -1576,6 +1586,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 ad5b2a5

Please sign in to comment.