Browse Source

add TaprootFidelityBondWatchonlyWallet

add_frost_channel_encryption
zebra-lucky 2 months ago
parent
commit
9dc1fcc6f8
  1. 39
      src/jmclient/cryptoengine.py
  2. 65
      src/jmclient/wallet.py
  3. 10
      src/jmclient/wallet_utils.py
  4. 1
      test/jmclient/test_payjoin.py

39
src/jmclient/cryptoengine.py

@ -19,7 +19,8 @@ from .configure import get_network, jm_single
TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, TYPE_P2SH_M_N, TYPE_TIMELOCK_P2WSH, \
TYPE_SEGWIT_WALLET_FIDELITY_BONDS, TYPE_WATCHONLY_FIDELITY_BONDS, \
TYPE_WATCHONLY_TIMELOCK_P2WSH, TYPE_WATCHONLY_P2WPKH, TYPE_P2WSH, \
TYPE_P2TR, TYPE_P2TR_FROST, TYPE_TAPROOT_WALLET_FIDELITY_BONDS = range(13)
TYPE_P2TR, TYPE_P2TR_FROST, TYPE_TAPROOT_WALLET_FIDELITY_BONDS, \
TYPE_TAPROOT_WATCHONLY_FIDELITY_BONDS, TYPE_WATCHONLY_P2TR, = range(15)
NET_MAINNET, NET_TESTNET, NET_SIGNET = range(3)
NET_MAP = {'mainnet': NET_MAINNET, 'testnet': NET_TESTNET,
'signet': NET_SIGNET}
@ -504,6 +505,34 @@ class BTC_P2TR(BTCEngine):
spent_outputs=spent_outputs)
class BTC_Watchonly_P2TR(BTC_P2TR):
@classmethod
def derive_bip32_privkey(cls, master_key, path):
return BTC_Watchonly_Timelocked_P2WSH.derive_bip32_privkey(master_key, path)
@classmethod
def privkey_to_wif(cls, privkey_locktime):
return BTC_Watchonly_Timelocked_P2WSH.privkey_to_wif(privkey_locktime)
@staticmethod
def privkey_to_pubkey(privkey):
#in watchonly wallets there are no privkeys, so functions
# like _get_key_from_path() actually return pubkeys and
# this function is a noop
return privkey
@classmethod
def derive_bip32_pub_export(cls, master_key, path):
return super(BTC_Watchonly_P2TR, cls).derive_bip32_pub_export(
master_key, BTC_Watchonly_Timelocked_P2WSH.get_watchonly_path(path))
@classmethod
async def sign_transaction(cls, tx, index, privkey, amount,
hashcode=btc.SIGHASH_ALL, **kwargs):
raise RuntimeError("Cannot spend from watch-only wallets")
class BTC_P2TR_FROST(BTC_P2TR):
@classmethod
@ -526,11 +555,17 @@ ENGINES = {
TYPE_P2PKH: BTC_P2PKH,
TYPE_P2SH_P2WPKH: BTC_P2SH_P2WPKH,
TYPE_P2WPKH: BTC_P2WPKH,
TYPE_TIMELOCK_P2WSH: BTC_Timelocked_P2WSH,
TYPE_WATCHONLY_TIMELOCK_P2WSH: BTC_Watchonly_Timelocked_P2WSH,
TYPE_WATCHONLY_P2WPKH: BTC_Watchonly_P2WPKH,
TYPE_SEGWIT_WALLET_FIDELITY_BONDS: BTC_P2WPKH,
TYPE_P2TR: BTC_P2TR,
TYPE_P2TR_FROST: BTC_P2TR_FROST,
TYPE_WATCHONLY_P2TR: BTC_Watchonly_P2TR,
TYPE_TAPROOT_WALLET_FIDELITY_BONDS: BTC_P2TR,
TYPE_P2TR_FROST: BTC_P2TR_FROST,
}

65
src/jmclient/wallet.py

@ -30,7 +30,8 @@ from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WSH,\
TYPE_P2WPKH, TYPE_TIMELOCK_P2WSH, TYPE_SEGWIT_WALLET_FIDELITY_BONDS,\
TYPE_WATCHONLY_FIDELITY_BONDS, TYPE_WATCHONLY_TIMELOCK_P2WSH, \
TYPE_WATCHONLY_P2WPKH, TYPE_P2TR, TYPE_P2TR_FROST, ENGINES, \
detect_script_type, EngineError, TYPE_TAPROOT_WALLET_FIDELITY_BONDS
detect_script_type, EngineError, TYPE_TAPROOT_WALLET_FIDELITY_BONDS, \
TYPE_TAPROOT_WATCHONLY_FIDELITY_BONDS, TYPE_WATCHONLY_P2TR
from .storage import DKGRecoveryStorage
from .support import get_random_bytes
from . import mn_encode, mn_decode
@ -870,7 +871,7 @@ class BaseWallet(object):
elif self.TYPE == TYPE_P2SH_P2WPKH:
return 'p2sh-p2wpkh'
elif self.TYPE in (TYPE_P2TR, TYPE_TAPROOT_WALLET_FIDELITY_BONDS,
TYPE_P2TR_FROST):
TYPE_P2TR_FROST, TYPE_WATCHONLY_P2TR):
return 'p2tr'
elif self.TYPE in (TYPE_P2WPKH,
TYPE_SEGWIT_WALLET_FIDELITY_BONDS):
@ -3854,12 +3855,72 @@ class FidelityBondWatchonlyWallet(FidelityBondMixin, BIP84Wallet):
return pubkey, self._ENGINE
class TaprootFidelityBondWatchonlyWallet(FidelityBondMixin, BIP86Wallet):
TYPE = TYPE_TAPROOT_WATCHONLY_FIDELITY_BONDS
_ENGINE = ENGINES[TYPE_WATCHONLY_P2TR]
_TIMELOCK_ENGINE = ENGINES[TYPE_WATCHONLY_TIMELOCK_P2WSH]
@classmethod
def _verify_entropy(cls, ent):
return ent[1:4] == b"pub"
@classmethod
def _derive_bip32_master_key(cls, master_entropy):
return btc.bip32_deserialize(master_entropy.decode())
def _get_bip32_export_path(self, mixdepth=None, address_type=None):
path = super()._get_bip32_export_path(mixdepth, address_type)
return path
def _get_key_from_path(self, path,
validate_cache: bool = False):
raise WalletCannotGetPrivateKeyFromWatchOnly()
async def _get_keypair_from_path(self, path,
validate_cache: bool = False):
raise WalletCannotGetPrivateKeyFromWatchOnly()
async def _get_pubkey_from_path(self, path,
validate_cache: bool = False):
if not self._is_my_bip32_path(path):
return await super()._get_pubkey_from_path(path,
validate_cache=validate_cache)
if self.is_timelocked_path(path):
key_path = path[:-1]
locktime = path[-1]
cache = self._get_cache_for_path(key_path)
pubkey = cache.get(b'P')
if pubkey is None or validate_cache:
new_pubkey = self._TIMELOCK_ENGINE.derive_bip32_privkey(
self._master_key, key_path)
if pubkey is None:
cache[b'P'] = pubkey = new_pubkey
elif pubkey != new_pubkey:
raise WalletCacheValidationFailed()
return (pubkey, locktime), self._TIMELOCK_ENGINE
cache = self._get_cache_for_path(path)
pubkey = cache.get(b'P')
if pubkey is None or validate_cache:
new_pubkey = self._ENGINE.derive_bip32_privkey(
self._master_key, path)
if pubkey is None:
cache[b'P'] = pubkey = new_pubkey
elif pubkey != new_pubkey:
raise WalletCacheValidationFailed()
return pubkey, self._ENGINE
WALLET_IMPLEMENTATIONS = {
LegacyWallet.TYPE: LegacyWallet,
SegwitLegacyWallet.TYPE: SegwitLegacyWallet,
SegwitWallet.TYPE: SegwitWallet,
SegwitWalletFidelityBonds.TYPE: SegwitWalletFidelityBonds,
FidelityBondWatchonlyWallet.TYPE: FidelityBondWatchonlyWallet,
TaprootWallet.TYPE: TaprootWallet,
TaprootWalletFidelityBonds.TYPE: TaprootWalletFidelityBonds,
TaprootFidelityBondWatchonlyWallet.TYPE: TaprootFidelityBondWatchonlyWallet,
FrostWallet.TYPE: FrostWallet,
}

10
src/jmclient/wallet_utils.py

@ -33,8 +33,10 @@ from jmbase.support import (get_password, jmprint, EXIT_FAILURE,
from jmfrost.chilldkg_ref.chilldkg import hostpubkey_gen
from .frost_clients import FROSTClient
from .frost_ipc import FrostIPCServer, FrostIPCClient
from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, \
TYPE_SEGWIT_WALLET_FIDELITY_BONDS, TYPE_P2TR, TYPE_P2TR_FROST
from .cryptoengine import (
TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH,
TYPE_SEGWIT_WALLET_FIDELITY_BONDS, TYPE_P2TR, TYPE_P2TR_FROST,
TYPE_TAPROOT_WALLET_FIDELITY_BONDS)
from .output import fmt_utxo
import jmbitcoin as btc
from .descriptor import descsum_create
@ -1495,7 +1497,7 @@ def get_configured_wallet_type(support_fidelity_bonds):
if is_frost_mode():
return TYPE_P2TR_FROST
if is_taproot_mode():
return TYPE_P2TR
configured_type = TYPE_P2TR
elif is_segwit_mode():
if is_native_segwit_mode():
configured_type = TYPE_P2WPKH
@ -1507,6 +1509,8 @@ def get_configured_wallet_type(support_fidelity_bonds):
if configured_type == TYPE_P2WPKH:
return TYPE_SEGWIT_WALLET_FIDELITY_BONDS
elif configured_type == TYPE_P2TR:
return TYPE_TAPROOT_WALLET_FIDELITY_BONDS
else:
raise ValueError("Fidelity bonds not supported with the configured "
"options of segwit and native. Edit joinmarket.cfg")

1
test/jmclient/test_payjoin.py

@ -10,7 +10,6 @@ from twisted.internet import reactor, defer
from twisted.web.server import Site, NOT_DONE_YET
from twisted.web.client import readBody
from twisted.web.http_headers import Headers
from twisted.trial import unittest
import urllib.parse as urlparse
from urllib.parse import urlencode

Loading…
Cancel
Save