From 9cd60433e71fcb329adcb2826a7b479dfa465e27 Mon Sep 17 00:00:00 2001 From: scgbckbone Date: Tue, 17 Jun 2025 15:53:38 +0200 Subject: [PATCH] move --- shared/miniscript.py | 54 ++++++++++++---------------------------- testing/test_teleport.py | 11 +++----- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/shared/miniscript.py b/shared/miniscript.py index 5dd5e243..b31b7103 100644 --- a/shared/miniscript.py +++ b/shared/miniscript.py @@ -218,11 +218,9 @@ class MiniScriptWallet(BaseStorageWallet): return branch, idx def get_my_deriv(self, my_xfp): - # TODO we can have more our keys in descriptor - # maybe lowest account/change index should be chosen - for e in self.xfp_paths(): - if e[0] == my_xfp: - return keypath_to_str(e) + # lowest public key from lexicographically sorted list is at index 0 + mine = self.xpubs_from_xfp(my_xfp) + return mine[0].origin.str_derivation() def derive_desc(self, xfp_paths): branch, idx = self.subderivation_indexes(xfp_paths) @@ -625,53 +623,38 @@ class MiniScriptWallet(BaseStorageWallet): if xfp == k.origin.cc_fp: res.append(k) - return res + assert res, "missing xfp %s" % xfp2str(xfp) + # returned is list of keys with corresponding master xfp + # key in list are lexicographically sorted based on their public keys + # lowes public key first + return sorted(res, key=lambda o: o.serialize()) def kt_make_rxkey(self, xfp): # Derive the receiver's pubkey from preshared xpub and a special derivation # - also provide the keypair we're using from our side of connection # - returns 4 byte nonce which is sent un-encrypted, his_pubkey and my_keypair ri = ngu.random.uniform(1<<28) - keys = self.xpubs_from_xfp(xfp) - if not keys: - raise RuntimeError("missing xfp") - elif len(keys) > 1: - # what to do here, out key is there more than once but has different origin derivation - print("len keys is more than 1", keys) + # sorted lexicographically, always use the lowest pubkey from the list at index 0 + keys = self.xpubs_from_xfp(xfp) k = keys[0] k = k.derive(KT_RXPUBKEY_DERIV).derive(ri) pubkey = k.node.pubkey() kp = self.kt_my_keypair(ri) - - #print("psbt sender: ri=%d toward xfp: %s ... %s" % (ri, xfp2str(xfp), B2A(pubkey))) - return ri.to_bytes(4, 'big'), pubkey, kp def kt_my_keypair(self, ri): # Calc my keypair for sending PSBT files. # - my_xfp = settings.get('xfp') - keys = self.xpubs_from_xfp(my_xfp) - assert keys - the_key = keys[0] - # including xfp(bytes) at index 0 - deriv = the_key.origin.psbt_derivation() - deriv.append(KT_RXPUBKEY_DERIV) - deriv.append(ri) - - # skip index 0 where xfp is - path = keypath_to_str(deriv) + # sorted lexicographically, always use the lowest pubkey from the list at index 0 + keys = self.xpubs_from_xfp(settings.get('xfp')) + subpath = "/%d/%d" % (KT_RXPUBKEY_DERIV, ri) + path = keys[0].origin.str_derivation() + subpath with stash.SensitiveValues() as sv: node = sv.derive_path(path) - kp = ngu.secp256k1.keypair(node.privkey()) - - #print("my keypair: ri=%d my_xfp=%s ... %s" % ( - # ri, xfp2str(my_xfp), B2A(kp.pubkey().to_bytes()))) - return kp @classmethod @@ -691,17 +674,12 @@ class MiniScriptWallet(BaseStorageWallet): kp = msc.kt_my_keypair(ri) for k in msc.keys: kk = Key.from_string(k) - if kk.origin.cc_fp == my_xfp: continue + if kk.origin.cc_fp == my_xfp: + continue kk = kk.derive(KT_RXPUBKEY_DERIV).derive(ri) - his_pubkey = kk.node.pubkey() - - #print("try decode: ri=%d toward xfp: %s ... from %s <= to %s" % ( - # ri, xfp2str(xfp), B2A(his_pubkey), B2A(kp.pubkey().to_bytes())), end=' ... ') - # if implied session key decodes the checksum, it is right ses_key, body = decode_step1(kp, his_pubkey, payload[4:]) - if ses_key: return ses_key, body, kk.origin.cc_fp diff --git a/testing/test_teleport.py b/testing/test_teleport.py index 105cbee6..a6bee1ad 100644 --- a/testing/test_teleport.py +++ b/testing/test_teleport.py @@ -723,7 +723,7 @@ def test_send_backup(testcase, rx_start, tx_start, cap_menu, enter_complex, pick @pytest.mark.parametrize("taproot", [True, False]) @pytest.mark.parametrize("keys", [True, False, None]) @pytest.mark.parametrize("policy", [ - "thresh(4,pk(@0),s:pk(@1),s:pk(@2),s:pk(@3),sln:older(SEQ))", + "thresh(4,pk(@0),s:pk(@1),s:pk(@2),s:pk(@3),sln:older(12960))", ]) def test_teleport_miniscript_sign(dev, taproot, policy, get_cc_key, bitcoind, use_regtest, clear_miniscript, set_bip39_pw, press_select, pick_menu_item, @@ -734,15 +734,12 @@ def test_teleport_miniscript_sign(dev, taproot, policy, get_cc_key, bitcoind, us reset_seed_words() use_regtest() clear_miniscript() - sequence = 5 - locktime = 0 - policy = policy.replace("SEQ", str(sequence)) # bitcoin core is PSBT provider name = "msc_tele" wo = bitcoind.create_wallet(name, disable_private_keys=True, blank=True) - deriv = "86h/%dh/0h" if taproot else "48h/1h/%dh/2h" + deriv = "86h/1h/%dh" if taproot else "48h/1h/%dh/2h" if keys is True: # actually just 2 signers - both with 2 keys with different subderivation (change based) deriv = deriv % 0 @@ -758,7 +755,7 @@ def test_teleport_miniscript_sign(dev, taproot, policy, get_cc_key, bitcoind, us signers = [keys[0], keys[2]] elif keys is False: - # 3 signers, account based + # 3 signers, 1 signer has two keys with different account derivation index keys = [get_cc_key(deriv % 0)] for i in range(1, 3): seed = Mnemonic.to_seed(simulator_fixed_words, passphrase=str(i)+str(i)) @@ -827,7 +824,7 @@ def test_teleport_miniscript_sign(dev, taproot, policy, get_cc_key, bitcoind, us psbt_resp = wo.walletcreatefundedpsbt( [], [{bitcoind.supply_wallet.getnewaddress(): 2.5}], - locktime, + 0, {"fee_rate": 2, "change_type": af}, ) psbt = psbt_resp.get("psbt")