transaction.py: rm PartialTxInput.{num_sig, script_type}
This commit is contained in:
@@ -397,9 +397,6 @@ class Commands:
|
|||||||
keypairs[pubkey] = privkey, compressed
|
keypairs[pubkey] = privkey, compressed
|
||||||
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type)
|
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type)
|
||||||
txin.script_descriptor = desc
|
txin.script_descriptor = desc
|
||||||
txin.script_type = txin_type
|
|
||||||
txin.pubkeys = [bfh(pubkey)]
|
|
||||||
txin.num_sig = 1
|
|
||||||
inputs.append(txin)
|
inputs.append(txin)
|
||||||
|
|
||||||
outputs = [PartialTxOutput.from_address_and_value(txout['address'], int(txout.get('value', txout['value_sats'])))
|
outputs = [PartialTxOutput.from_address_and_value(txout['address'], int(txout.get('value', txout['value_sats'])))
|
||||||
@@ -428,8 +425,6 @@ class Commands:
|
|||||||
if address in txins_dict.keys():
|
if address in txins_dict.keys():
|
||||||
for txin in txins_dict[address]:
|
for txin in txins_dict[address]:
|
||||||
txin.script_descriptor = desc
|
txin.script_descriptor = desc
|
||||||
txin.pubkeys = [pubkey]
|
|
||||||
txin.script_type = txin_type
|
|
||||||
tx.sign({pubkey.hex(): (priv2, compressed)})
|
tx.sign({pubkey.hex(): (priv2, compressed)})
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ from typing import (
|
|||||||
Tuple,
|
Tuple,
|
||||||
Sequence,
|
Sequence,
|
||||||
Mapping,
|
Mapping,
|
||||||
|
Set,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -376,6 +377,22 @@ class Descriptor(object):
|
|||||||
script_sig=script_sig,
|
script_sig=script_sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_satisfaction_progress(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
sigdata: Mapping[bytes, bytes] = None, # pubkey -> sig
|
||||||
|
) -> Tuple[int, int]:
|
||||||
|
"""Returns (num_sigs_we_have, num_sigs_required) towards satisfying this script.
|
||||||
|
Besides signatures, later this can also consider hash-preimages.
|
||||||
|
"""
|
||||||
|
assert not self.is_range()
|
||||||
|
nhave, nreq = 0, 0
|
||||||
|
for desc in self.subdescriptors:
|
||||||
|
a, b = desc.get_satisfaction_progress()
|
||||||
|
nhave += a
|
||||||
|
nreq += b
|
||||||
|
return nhave, nreq
|
||||||
|
|
||||||
def is_range(self) -> bool:
|
def is_range(self) -> bool:
|
||||||
for pubkey in self.pubkeys:
|
for pubkey in self.pubkeys:
|
||||||
if pubkey.is_range():
|
if pubkey.is_range():
|
||||||
@@ -388,6 +405,47 @@ class Descriptor(object):
|
|||||||
def is_segwit(self) -> bool:
|
def is_segwit(self) -> bool:
|
||||||
return any([desc.is_segwit() for desc in self.subdescriptors])
|
return any([desc.is_segwit() for desc in self.subdescriptors])
|
||||||
|
|
||||||
|
def get_all_pubkeys(self) -> Set[bytes]:
|
||||||
|
"""Returns set of pubkeys that appear at any level in this descriptor."""
|
||||||
|
assert not self.is_range()
|
||||||
|
all_pubkeys = set([p.get_pubkey_bytes() for p in self.pubkeys])
|
||||||
|
for desc in self.subdescriptors:
|
||||||
|
all_pubkeys |= desc.get_all_pubkeys()
|
||||||
|
return all_pubkeys
|
||||||
|
|
||||||
|
def get_simple_singlesig(self) -> Optional['Descriptor']:
|
||||||
|
"""Returns innermost pk/pkh/wpkh descriptor, or None if we are not a simple singlesig.
|
||||||
|
|
||||||
|
note: besides pk,pkh,sh(wpkh),wpkh, overly complicated stuff such as sh(pk),wsh(sh(pkh),etc is also accepted
|
||||||
|
"""
|
||||||
|
if len(self.subdescriptors) == 1:
|
||||||
|
return self.subdescriptors[0].get_simple_singlesig()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_simple_multisig(self) -> Optional['MultisigDescriptor']:
|
||||||
|
"""Returns innermost multi descriptor, or None if we are not a simple multisig."""
|
||||||
|
if len(self.subdescriptors) == 1:
|
||||||
|
return self.subdescriptors[0].get_simple_multisig()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def to_legacy_electrum_script_type(self) -> str:
|
||||||
|
if isinstance(self, PKDescriptor):
|
||||||
|
return "p2pk"
|
||||||
|
elif isinstance(self, PKHDescriptor):
|
||||||
|
return "p2pkh"
|
||||||
|
elif isinstance(self, WPKHDescriptor):
|
||||||
|
return "p2wpkh"
|
||||||
|
elif isinstance(self, SHDescriptor) and isinstance(self.subdescriptors[0], WPKHDescriptor):
|
||||||
|
return "p2wpkh-p2sh"
|
||||||
|
elif isinstance(self, SHDescriptor) and isinstance(self.subdescriptors[0], MultisigDescriptor):
|
||||||
|
return "p2sh"
|
||||||
|
elif isinstance(self, WSHDescriptor) and isinstance(self.subdescriptors[0], MultisigDescriptor):
|
||||||
|
return "p2wsh"
|
||||||
|
elif (isinstance(self, SHDescriptor) and isinstance(self.subdescriptors[0], WSHDescriptor)
|
||||||
|
and isinstance(self.subdescriptors[0].subdescriptors[0], MultisigDescriptor)):
|
||||||
|
return "p2wsh-p2sh"
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
class PKDescriptor(Descriptor):
|
class PKDescriptor(Descriptor):
|
||||||
"""
|
"""
|
||||||
@@ -421,6 +479,14 @@ class PKDescriptor(Descriptor):
|
|||||||
witness_items=(sig,),
|
witness_items=(sig,),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_satisfaction_progress(self, *, sigdata=None) -> Tuple[int, int]:
|
||||||
|
if sigdata is None: sigdata = {}
|
||||||
|
signatures = list(sigdata.values())
|
||||||
|
return len(signatures), 1
|
||||||
|
|
||||||
|
def get_simple_singlesig(self) -> Optional['Descriptor']:
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class PKHDescriptor(Descriptor):
|
class PKHDescriptor(Descriptor):
|
||||||
"""
|
"""
|
||||||
@@ -455,6 +521,14 @@ class PKHDescriptor(Descriptor):
|
|||||||
witness_items=(sig, pubkey),
|
witness_items=(sig, pubkey),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_satisfaction_progress(self, *, sigdata=None) -> Tuple[int, int]:
|
||||||
|
if sigdata is None: sigdata = {}
|
||||||
|
signatures = list(sigdata.values())
|
||||||
|
return len(signatures), 1
|
||||||
|
|
||||||
|
def get_simple_singlesig(self) -> Optional['Descriptor']:
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class WPKHDescriptor(Descriptor):
|
class WPKHDescriptor(Descriptor):
|
||||||
"""
|
"""
|
||||||
@@ -492,9 +566,17 @@ class WPKHDescriptor(Descriptor):
|
|||||||
witness_items=(sig, pubkey),
|
witness_items=(sig, pubkey),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_satisfaction_progress(self, *, sigdata=None) -> Tuple[int, int]:
|
||||||
|
if sigdata is None: sigdata = {}
|
||||||
|
signatures = list(sigdata.values())
|
||||||
|
return len(signatures), 1
|
||||||
|
|
||||||
def is_segwit(self) -> bool:
|
def is_segwit(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_simple_singlesig(self) -> Optional['Descriptor']:
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class MultisigDescriptor(Descriptor):
|
class MultisigDescriptor(Descriptor):
|
||||||
"""
|
"""
|
||||||
@@ -552,6 +634,14 @@ class MultisigDescriptor(Descriptor):
|
|||||||
witness_items=(0, *signatures),
|
witness_items=(0, *signatures),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_satisfaction_progress(self, *, sigdata=None) -> Tuple[int, int]:
|
||||||
|
if sigdata is None: sigdata = {}
|
||||||
|
signatures = list(sigdata.values())
|
||||||
|
return len(signatures), self.thresh
|
||||||
|
|
||||||
|
def get_simple_multisig(self) -> Optional['MultisigDescriptor']:
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class SHDescriptor(Descriptor):
|
class SHDescriptor(Descriptor):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -516,9 +516,6 @@ def create_sweeptx_their_ctx_to_remote(
|
|||||||
txin._trusted_value_sats = val
|
txin._trusted_value_sats = val
|
||||||
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=our_payment_pubkey, script_type='p2wpkh')
|
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=our_payment_pubkey, script_type='p2wpkh')
|
||||||
txin.script_descriptor = desc
|
txin.script_descriptor = desc
|
||||||
txin.script_type = 'p2wpkh'
|
|
||||||
txin.pubkeys = [bfh(our_payment_pubkey)]
|
|
||||||
txin.num_sig = 1
|
|
||||||
sweep_inputs = [txin]
|
sweep_inputs = [txin]
|
||||||
tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh
|
tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh
|
||||||
fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
|
fee = config.estimate_fee(tx_size_bytes, allow_fallback_to_static_rates=True)
|
||||||
|
|||||||
@@ -823,10 +823,6 @@ def make_funding_input(local_funding_pubkey: bytes, remote_funding_pubkey: bytes
|
|||||||
ppubkeys = [descriptor.PubkeyProvider.parse(pk) for pk in pubkeys]
|
ppubkeys = [descriptor.PubkeyProvider.parse(pk) for pk in pubkeys]
|
||||||
multi = descriptor.MultisigDescriptor(pubkeys=ppubkeys, thresh=2, is_sorted=True)
|
multi = descriptor.MultisigDescriptor(pubkeys=ppubkeys, thresh=2, is_sorted=True)
|
||||||
c_input.script_descriptor = descriptor.WSHDescriptor(subdescriptor=multi)
|
c_input.script_descriptor = descriptor.WSHDescriptor(subdescriptor=multi)
|
||||||
|
|
||||||
c_input.script_type = 'p2wsh'
|
|
||||||
c_input.pubkeys = [bfh(pk) for pk in pubkeys]
|
|
||||||
c_input.num_sig = 2
|
|
||||||
c_input._trusted_value_sats = funding_sat
|
c_input._trusted_value_sats = funding_sat
|
||||||
return c_input
|
return c_input
|
||||||
|
|
||||||
|
|||||||
@@ -444,9 +444,10 @@ class BitBox02Client(HardwareClientBase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert (desc := txin.script_descriptor)
|
||||||
if tx_script_type is None:
|
if tx_script_type is None:
|
||||||
tx_script_type = txin.script_type
|
tx_script_type = desc.to_legacy_electrum_script_type()
|
||||||
elif tx_script_type != txin.script_type:
|
elif tx_script_type != desc.to_legacy_electrum_script_type():
|
||||||
raise Exception("Cannot mix different input script types")
|
raise Exception("Cannot mix different input script types")
|
||||||
|
|
||||||
if tx_script_type == "p2wpkh":
|
if tx_script_type == "p2wpkh":
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import copy
|
|||||||
from electrum.crypto import sha256d, EncodeAES_bytes, DecodeAES_bytes, hmac_oneshot
|
from electrum.crypto import sha256d, EncodeAES_bytes, DecodeAES_bytes, hmac_oneshot
|
||||||
from electrum.bitcoin import public_key_to_p2pkh
|
from electrum.bitcoin import public_key_to_p2pkh
|
||||||
from electrum.bip32 import BIP32Node, convert_bip32_intpath_to_strpath, is_all_public_derivation
|
from electrum.bip32 import BIP32Node, convert_bip32_intpath_to_strpath, is_all_public_derivation
|
||||||
|
from electrum import descriptor
|
||||||
from electrum import ecc
|
from electrum import ecc
|
||||||
from electrum.ecc import msg_magic
|
from electrum.ecc import msg_magic
|
||||||
from electrum.wallet import Standard_Wallet
|
from electrum.wallet import Standard_Wallet
|
||||||
@@ -527,7 +528,8 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||||||
if txin.is_coinbase_input():
|
if txin.is_coinbase_input():
|
||||||
self.give_error("Coinbase not supported") # should never happen
|
self.give_error("Coinbase not supported") # should never happen
|
||||||
|
|
||||||
if txin.script_type != 'p2pkh':
|
assert (desc := txin.script_descriptor)
|
||||||
|
if desc.to_legacy_electrum_script_type() != 'p2pkh':
|
||||||
p2pkhTransaction = False
|
p2pkhTransaction = False
|
||||||
|
|
||||||
my_pubkey, inputPath = self.find_my_pubkey_in_txinout(txin)
|
my_pubkey, inputPath = self.find_my_pubkey_in_txinout(txin)
|
||||||
@@ -557,9 +559,10 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
|||||||
tx_copy = copy.deepcopy(tx)
|
tx_copy = copy.deepcopy(tx)
|
||||||
# monkey-patch method of tx_copy instance to change serialization
|
# monkey-patch method of tx_copy instance to change serialization
|
||||||
def input_script(self, txin: PartialTxInput, *, estimate_size=False):
|
def input_script(self, txin: PartialTxInput, *, estimate_size=False):
|
||||||
if txin.script_type == 'p2pkh':
|
desc = txin.script_descriptor
|
||||||
|
if isinstance(desc, descriptor.PKHDescriptor):
|
||||||
return Transaction.get_preimage_script(txin)
|
return Transaction.get_preimage_script(txin)
|
||||||
raise Exception("unsupported type %s" % txin.script_type)
|
raise Exception(f"unsupported txin type. only p2pkh is supported. got: {desc.to_string()[:10]}")
|
||||||
tx_copy.input_script = input_script.__get__(tx_copy, PartialTransaction)
|
tx_copy.input_script = input_script.__get__(tx_copy, PartialTransaction)
|
||||||
tx_dbb_serialized = tx_copy.serialize_to_network()
|
tx_dbb_serialized = tx_copy.serialize_to_network()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ class Jade_KeyStore(Hardware_KeyStore):
|
|||||||
jade_inputs = []
|
jade_inputs = []
|
||||||
for txin in tx.inputs():
|
for txin in tx.inputs():
|
||||||
pubkey, path = self.find_my_pubkey_in_txinout(txin)
|
pubkey, path = self.find_my_pubkey_in_txinout(txin)
|
||||||
witness_input = txin.script_type in ['p2wpkh-p2sh', 'p2wsh-p2sh', 'p2wpkh', 'p2wsh']
|
witness_input = txin.is_segwit()
|
||||||
redeem_script = Transaction.get_preimage_script(txin)
|
redeem_script = Transaction.get_preimage_script(txin)
|
||||||
redeem_script = bytes.fromhex(redeem_script) if redeem_script is not None else None
|
redeem_script = bytes.fromhex(redeem_script) if redeem_script is not None else None
|
||||||
input_tx = txin.utxo
|
input_tx = txin.utxo
|
||||||
@@ -280,6 +280,7 @@ class Jade_KeyStore(Hardware_KeyStore):
|
|||||||
change = [None] * len(tx.outputs())
|
change = [None] * len(tx.outputs())
|
||||||
for index, txout in enumerate(tx.outputs()):
|
for index, txout in enumerate(tx.outputs()):
|
||||||
if txout.is_mine and txout.is_change:
|
if txout.is_mine and txout.is_change:
|
||||||
|
assert (desc := txout.script_descriptor)
|
||||||
if is_multisig:
|
if is_multisig:
|
||||||
# Multisig - wallet details must be registered on Jade hw
|
# Multisig - wallet details must be registered on Jade hw
|
||||||
multisig_name = _register_multisig_wallet(wallet, self, txout.address)
|
multisig_name = _register_multisig_wallet(wallet, self, txout.address)
|
||||||
@@ -294,7 +295,7 @@ class Jade_KeyStore(Hardware_KeyStore):
|
|||||||
else:
|
else:
|
||||||
# Pass entire path
|
# Pass entire path
|
||||||
pubkey, path = self.find_my_pubkey_in_txinout(txout)
|
pubkey, path = self.find_my_pubkey_in_txinout(txout)
|
||||||
change[index] = {'path':path, 'variant': txout.script_type}
|
change[index] = {'path':path, 'variant': desc.to_legacy_electrum_script_type()}
|
||||||
|
|
||||||
# The txn itself
|
# The txn itself
|
||||||
txn_bytes = bytes.fromhex(tx.serialize_to_network())
|
txn_bytes = bytes.fromhex(tx.serialize_to_network())
|
||||||
|
|||||||
@@ -376,12 +376,13 @@ class KeepKeyPlugin(HW_PluginBase):
|
|||||||
assert isinstance(tx, PartialTransaction)
|
assert isinstance(tx, PartialTransaction)
|
||||||
assert isinstance(txin, PartialTxInput)
|
assert isinstance(txin, PartialTxInput)
|
||||||
assert keystore
|
assert keystore
|
||||||
if len(txin.pubkeys) > 1:
|
assert (desc := txin.script_descriptor)
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||||
multisig = self._make_multisig(txin.num_sig, xpubs_and_deriv_suffixes)
|
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
else:
|
else:
|
||||||
multisig = None
|
multisig = None
|
||||||
script_type = self.get_keepkey_input_script_type(txin.script_type)
|
script_type = self.get_keepkey_input_script_type(desc.to_legacy_electrum_script_type())
|
||||||
txinputtype = self.types.TxInputType(
|
txinputtype = self.types.TxInputType(
|
||||||
script_type=script_type,
|
script_type=script_type,
|
||||||
multisig=multisig)
|
multisig=multisig)
|
||||||
@@ -418,10 +419,11 @@ class KeepKeyPlugin(HW_PluginBase):
|
|||||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'KeepKey_KeyStore'):
|
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'KeepKey_KeyStore'):
|
||||||
|
|
||||||
def create_output_by_derivation():
|
def create_output_by_derivation():
|
||||||
script_type = self.get_keepkey_output_script_type(txout.script_type)
|
assert (desc := txout.script_descriptor)
|
||||||
if len(txout.pubkeys) > 1:
|
script_type = self.get_keepkey_output_script_type(desc.to_legacy_electrum_script_type())
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||||
multisig = self._make_multisig(txout.num_sig, xpubs_and_deriv_suffixes)
|
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
else:
|
else:
|
||||||
multisig = None
|
multisig = None
|
||||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from typing import Dict, List, Optional, Sequence, Tuple
|
|||||||
|
|
||||||
|
|
||||||
from electrum import bip32, constants, ecc
|
from electrum import bip32, constants, ecc
|
||||||
|
from electrum import descriptor
|
||||||
from electrum.base_wizard import ScriptTypeNotSupported
|
from electrum.base_wizard import ScriptTypeNotSupported
|
||||||
from electrum.bip32 import BIP32Node, convert_bip32_intpath_to_strpath
|
from electrum.bip32 import BIP32Node, convert_bip32_intpath_to_strpath
|
||||||
from electrum.bitcoin import EncodeBase58Check, int_to_hex, is_b58_address, is_segwit_script_type, var_int
|
from electrum.bitcoin import EncodeBase58Check, int_to_hex, is_b58_address, is_segwit_script_type, var_int
|
||||||
@@ -16,7 +17,7 @@ from electrum.i18n import _
|
|||||||
from electrum.keystore import Hardware_KeyStore
|
from electrum.keystore import Hardware_KeyStore
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.plugin import Device, runs_in_hwd_thread
|
from electrum.plugin import Device, runs_in_hwd_thread
|
||||||
from electrum.transaction import PartialTransaction, Transaction
|
from electrum.transaction import PartialTransaction, Transaction, PartialTxInput
|
||||||
from electrum.util import bfh, UserFacingException, versiontuple
|
from electrum.util import bfh, UserFacingException, versiontuple
|
||||||
from electrum.wallet import Standard_Wallet
|
from electrum.wallet import Standard_Wallet
|
||||||
|
|
||||||
@@ -544,20 +545,25 @@ class Ledger_Client_Legacy(Ledger_Client):
|
|||||||
pin = ""
|
pin = ""
|
||||||
# prompt for the PIN before displaying the dialog if necessary
|
# prompt for the PIN before displaying the dialog if necessary
|
||||||
|
|
||||||
|
def is_txin_legacy_multisig(txin: PartialTxInput) -> bool:
|
||||||
|
desc = txin.script_descriptor
|
||||||
|
return (isinstance(desc, descriptor.SHDescriptor)
|
||||||
|
and isinstance(desc.subdescriptors[0], descriptor.MultisigDescriptor))
|
||||||
|
|
||||||
# Fetch inputs of the transaction to sign
|
# Fetch inputs of the transaction to sign
|
||||||
for txin in tx.inputs():
|
for txin in tx.inputs():
|
||||||
if txin.is_coinbase_input():
|
if txin.is_coinbase_input():
|
||||||
self.give_error("Coinbase not supported") # should never happen
|
self.give_error("Coinbase not supported") # should never happen
|
||||||
|
|
||||||
if txin.script_type in ['p2sh']:
|
if is_txin_legacy_multisig(txin):
|
||||||
p2shTransaction = True
|
p2shTransaction = True
|
||||||
|
|
||||||
if txin.script_type in ['p2wpkh-p2sh', 'p2wsh-p2sh']:
|
if txin.is_p2sh_segwit():
|
||||||
if not self.supports_segwit():
|
if not self.supports_segwit():
|
||||||
self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT)
|
self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT)
|
||||||
segwitTransaction = True
|
segwitTransaction = True
|
||||||
|
|
||||||
if txin.script_type in ['p2wpkh', 'p2wsh']:
|
if txin.is_native_segwit():
|
||||||
if not self.supports_native_segwit():
|
if not self.supports_native_segwit():
|
||||||
self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT)
|
self.give_error(MSG_NEEDS_FW_UPDATE_SEGWIT)
|
||||||
segwitTransaction = True
|
segwitTransaction = True
|
||||||
@@ -584,7 +590,7 @@ class Ledger_Client_Legacy(Ledger_Client):
|
|||||||
# Sanity check
|
# Sanity check
|
||||||
if p2shTransaction:
|
if p2shTransaction:
|
||||||
for txin in tx.inputs():
|
for txin in tx.inputs():
|
||||||
if txin.script_type != 'p2sh':
|
if not is_txin_legacy_multisig(txin):
|
||||||
self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen
|
self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen
|
||||||
|
|
||||||
txOutput = var_int(len(tx.outputs()))
|
txOutput = var_int(len(tx.outputs()))
|
||||||
@@ -1083,7 +1089,6 @@ class Ledger_Client_New(Ledger_Client):
|
|||||||
raise UserFacingException("Coinbase not supported") # should never happen
|
raise UserFacingException("Coinbase not supported") # should never happen
|
||||||
|
|
||||||
utxo = None
|
utxo = None
|
||||||
scriptcode = b""
|
|
||||||
if psbt_in.witness_utxo:
|
if psbt_in.witness_utxo:
|
||||||
utxo = psbt_in.witness_utxo
|
utxo = psbt_in.witness_utxo
|
||||||
if psbt_in.non_witness_utxo:
|
if psbt_in.non_witness_utxo:
|
||||||
@@ -1094,19 +1099,9 @@ class Ledger_Client_New(Ledger_Client):
|
|||||||
|
|
||||||
if utxo is None:
|
if utxo is None:
|
||||||
continue
|
continue
|
||||||
scriptcode = utxo.scriptPubKey
|
if (desc := electrum_txin.script_descriptor) is None:
|
||||||
if electrum_txin.script_type in ['p2sh', 'p2wpkh-p2sh']:
|
raise Exception("script_descriptor missing for txin ")
|
||||||
if len(psbt_in.redeem_script) == 0:
|
scriptcode = desc.expand().scriptcode_for_sighash
|
||||||
continue
|
|
||||||
scriptcode = psbt_in.redeem_script
|
|
||||||
elif electrum_txin.script_type in ['p2wsh', 'p2wsh-p2sh']:
|
|
||||||
if len(psbt_in.witness_script) == 0:
|
|
||||||
continue
|
|
||||||
scriptcode = psbt_in.witness_script
|
|
||||||
|
|
||||||
p2sh = False
|
|
||||||
if electrum_txin.script_type in ['p2sh', 'p2wpkh-p2sh', 'p2wsh-p2sh']:
|
|
||||||
p2sh = True
|
|
||||||
|
|
||||||
is_wit, wit_ver, __ = is_witness(psbt_in.redeem_script or utxo.scriptPubKey)
|
is_wit, wit_ver, __ = is_witness(psbt_in.redeem_script or utxo.scriptPubKey)
|
||||||
|
|
||||||
@@ -1115,7 +1110,7 @@ class Ledger_Client_New(Ledger_Client):
|
|||||||
# if it's a segwit spend (any version), make sure the witness_utxo is also present
|
# if it's a segwit spend (any version), make sure the witness_utxo is also present
|
||||||
psbt_in.witness_utxo = utxo
|
psbt_in.witness_utxo = utxo
|
||||||
|
|
||||||
if p2sh:
|
if electrum_txin.is_p2sh_segwit():
|
||||||
if wit_ver == 0:
|
if wit_ver == 0:
|
||||||
script_addrtype = AddressType.SH_WIT
|
script_addrtype = AddressType.SH_WIT
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -346,12 +346,13 @@ class SafeTPlugin(HW_PluginBase):
|
|||||||
assert isinstance(tx, PartialTransaction)
|
assert isinstance(tx, PartialTransaction)
|
||||||
assert isinstance(txin, PartialTxInput)
|
assert isinstance(txin, PartialTxInput)
|
||||||
assert keystore
|
assert keystore
|
||||||
if len(txin.pubkeys) > 1:
|
assert (desc := txin.script_descriptor)
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||||
multisig = self._make_multisig(txin.num_sig, xpubs_and_deriv_suffixes)
|
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
else:
|
else:
|
||||||
multisig = None
|
multisig = None
|
||||||
script_type = self.get_safet_input_script_type(txin.script_type)
|
script_type = self.get_safet_input_script_type(desc.to_legacy_electrum_script_type())
|
||||||
txinputtype = self.types.TxInputType(
|
txinputtype = self.types.TxInputType(
|
||||||
script_type=script_type,
|
script_type=script_type,
|
||||||
multisig=multisig)
|
multisig=multisig)
|
||||||
@@ -388,10 +389,11 @@ class SafeTPlugin(HW_PluginBase):
|
|||||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'SafeTKeyStore'):
|
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'SafeTKeyStore'):
|
||||||
|
|
||||||
def create_output_by_derivation():
|
def create_output_by_derivation():
|
||||||
script_type = self.get_safet_output_script_type(txout.script_type)
|
assert (desc := txout.script_descriptor)
|
||||||
if len(txout.pubkeys) > 1:
|
script_type = self.get_safet_output_script_type(desc.to_legacy_electrum_script_type())
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||||
multisig = self._make_multisig(txout.num_sig, xpubs_and_deriv_suffixes)
|
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
else:
|
else:
|
||||||
multisig = None
|
multisig = None
|
||||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||||
|
|||||||
@@ -417,10 +417,11 @@ class TrezorPlugin(HW_PluginBase):
|
|||||||
assert isinstance(tx, PartialTransaction)
|
assert isinstance(tx, PartialTransaction)
|
||||||
assert isinstance(txin, PartialTxInput)
|
assert isinstance(txin, PartialTxInput)
|
||||||
assert keystore
|
assert keystore
|
||||||
if len(txin.pubkeys) > 1:
|
assert (desc := txin.script_descriptor)
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||||
txinputtype.multisig = self._make_multisig(txin.num_sig, xpubs_and_deriv_suffixes)
|
txinputtype.multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
txinputtype.script_type = self.get_trezor_input_script_type(txin.script_type)
|
txinputtype.script_type = self.get_trezor_input_script_type(desc.to_legacy_electrum_script_type())
|
||||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txin)
|
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txin)
|
||||||
if full_path:
|
if full_path:
|
||||||
txinputtype.address_n = full_path
|
txinputtype.address_n = full_path
|
||||||
@@ -445,10 +446,11 @@ class TrezorPlugin(HW_PluginBase):
|
|||||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'TrezorKeyStore'):
|
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'TrezorKeyStore'):
|
||||||
|
|
||||||
def create_output_by_derivation():
|
def create_output_by_derivation():
|
||||||
script_type = self.get_trezor_output_script_type(txout.script_type)
|
assert (desc := txout.script_descriptor)
|
||||||
if len(txout.pubkeys) > 1:
|
script_type = self.get_trezor_output_script_type(desc.to_legacy_electrum_script_type())
|
||||||
|
if multi := desc.get_simple_multisig():
|
||||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||||
multisig = self._make_multisig(txout.num_sig, xpubs_and_deriv_suffixes)
|
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||||
else:
|
else:
|
||||||
multisig = None
|
multisig = None
|
||||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ def create_claim_tx(
|
|||||||
"""Create tx to either claim successful reverse-swap,
|
"""Create tx to either claim successful reverse-swap,
|
||||||
or to get refunded for timed-out forward-swap.
|
or to get refunded for timed-out forward-swap.
|
||||||
"""
|
"""
|
||||||
txin.script_type = 'p2wsh'
|
|
||||||
txin.script_sig = b''
|
txin.script_sig = b''
|
||||||
txin.witness_script = witness_script
|
txin.witness_script = witness_script
|
||||||
txout = PartialTxOutput.from_address_and_value(address, amount_sat)
|
txout = PartialTxOutput.from_address_and_value(address, amount_sat)
|
||||||
@@ -622,8 +621,6 @@ class SwapManager(Logger):
|
|||||||
return
|
return
|
||||||
preimage = swap.preimage if swap.is_reverse else 0
|
preimage = swap.preimage if swap.is_reverse else 0
|
||||||
witness_script = swap.redeem_script
|
witness_script = swap.redeem_script
|
||||||
txin.script_type = 'p2wsh'
|
|
||||||
txin.num_sig = 1 # hack so that txin not considered "is_complete"
|
|
||||||
txin.script_sig = b''
|
txin.script_sig = b''
|
||||||
txin.witness_script = witness_script
|
txin.witness_script = witness_script
|
||||||
sig_dummy = b'\x00' * 71 # DER-encoded ECDSA sig, with low S and low R
|
sig_dummy = b'\x00' * 71 # DER-encoded ECDSA sig, with low S and low R
|
||||||
@@ -637,7 +634,6 @@ class SwapManager(Logger):
|
|||||||
txin = tx.inputs()[0]
|
txin = tx.inputs()[0]
|
||||||
assert len(tx.inputs()) == 1, f"expected 1 input for swap claim tx. found {len(tx.inputs())}"
|
assert len(tx.inputs()) == 1, f"expected 1 input for swap claim tx. found {len(tx.inputs())}"
|
||||||
assert txin.prevout.txid.hex() == swap.funding_txid
|
assert txin.prevout.txid.hex() == swap.funding_txid
|
||||||
txin.script_type = 'p2wsh'
|
|
||||||
txin.script_sig = b''
|
txin.script_sig = b''
|
||||||
txin.witness_script = witness_script
|
txin.witness_script = witness_script
|
||||||
sig = bytes.fromhex(tx.sign_txin(0, swap.privkey))
|
sig = bytes.fromhex(tx.sign_txin(0, swap.privkey))
|
||||||
|
|||||||
@@ -94,9 +94,6 @@ class TestTransaction(ElectrumTestCase):
|
|||||||
script_type = 'p2pkh'
|
script_type = 'p2pkh'
|
||||||
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=script_type)
|
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=script_type)
|
||||||
tx.inputs()[0].script_descriptor = desc
|
tx.inputs()[0].script_descriptor = desc
|
||||||
tx.inputs()[0].script_type = script_type
|
|
||||||
tx.inputs()[0].pubkeys = [pubkey]
|
|
||||||
tx.inputs()[0].num_sig = 1
|
|
||||||
tx.update_signatures(signed_blob_signatures)
|
tx.update_signatures(signed_blob_signatures)
|
||||||
self.assertEqual(tx.serialize(), signed_blob)
|
self.assertEqual(tx.serialize(), signed_blob)
|
||||||
|
|
||||||
@@ -878,7 +875,6 @@ class TestTransactionTestnet(ElectrumTestCase):
|
|||||||
prevout = TxOutpoint(txid=bfh('6d500966f9e494b38a04545f0cea35fc7b3944e341a64b804fed71cdee11d434'), out_idx=1)
|
prevout = TxOutpoint(txid=bfh('6d500966f9e494b38a04545f0cea35fc7b3944e341a64b804fed71cdee11d434'), out_idx=1)
|
||||||
txin = PartialTxInput(prevout=prevout)
|
txin = PartialTxInput(prevout=prevout)
|
||||||
txin.nsequence = 2 ** 32 - 3
|
txin.nsequence = 2 ** 32 - 3
|
||||||
txin.script_type = 'p2sh'
|
|
||||||
redeem_script = bfh(construct_script([
|
redeem_script = bfh(construct_script([
|
||||||
locktime, opcodes.OP_CHECKLOCKTIMEVERIFY, opcodes.OP_DROP, pubkey, opcodes.OP_CHECKSIG,
|
locktime, opcodes.OP_CHECKLOCKTIMEVERIFY, opcodes.OP_DROP, pubkey, opcodes.OP_CHECKSIG,
|
||||||
]))
|
]))
|
||||||
@@ -940,7 +936,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
prevout = TxOutpoint(txid=bfh('6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436'), out_idx=1)
|
prevout = TxOutpoint(txid=bfh('6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436'), out_idx=1)
|
||||||
txin = PartialTxInput(prevout=prevout)
|
txin = PartialTxInput(prevout=prevout)
|
||||||
txin.nsequence=0xffffffff
|
txin.nsequence=0xffffffff
|
||||||
txin.script_type='p2sh-p2wsh'
|
|
||||||
txin.witness_script = bfh('56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae')
|
txin.witness_script = bfh('56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae')
|
||||||
txin.redeem_script = bfh('0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54')
|
txin.redeem_script = bfh('0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54')
|
||||||
txin._trusted_value_sats = 987654321
|
txin._trusted_value_sats = 987654321
|
||||||
@@ -950,7 +945,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
|
|
||||||
def test_check_sighash_types_sighash_all(self):
|
def test_check_sighash_types_sighash_all(self):
|
||||||
self.txin.sighash=Sighash.ALL
|
self.txin.sighash=Sighash.ALL
|
||||||
self.txin.pubkeys = [bfh('0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3')]
|
|
||||||
privkey = bfh('730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6')
|
privkey = bfh('730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
@@ -959,7 +953,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
|
|
||||||
def test_check_sighash_types_sighash_none(self):
|
def test_check_sighash_types_sighash_none(self):
|
||||||
self.txin.sighash=Sighash.NONE
|
self.txin.sighash=Sighash.NONE
|
||||||
self.txin.pubkeys = [bfh('03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b')]
|
|
||||||
privkey = bfh('11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3')
|
privkey = bfh('11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
@@ -968,7 +961,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
|
|
||||||
def test_check_sighash_types_sighash_single(self):
|
def test_check_sighash_types_sighash_single(self):
|
||||||
self.txin.sighash=Sighash.SINGLE
|
self.txin.sighash=Sighash.SINGLE
|
||||||
self.txin.pubkeys = [bfh('034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a')]
|
|
||||||
privkey = bfh('77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661')
|
privkey = bfh('77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
@@ -978,7 +970,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
@disable_ecdsa_r_value_grinding
|
@disable_ecdsa_r_value_grinding
|
||||||
def test_check_sighash_types_sighash_all_anyonecanpay(self):
|
def test_check_sighash_types_sighash_all_anyonecanpay(self):
|
||||||
self.txin.sighash=Sighash.ALL|Sighash.ANYONECANPAY
|
self.txin.sighash=Sighash.ALL|Sighash.ANYONECANPAY
|
||||||
self.txin.pubkeys = [bfh('033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4')]
|
|
||||||
privkey = bfh('14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49')
|
privkey = bfh('14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
@@ -988,7 +979,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
@disable_ecdsa_r_value_grinding
|
@disable_ecdsa_r_value_grinding
|
||||||
def test_check_sighash_types_sighash_none_anyonecanpay(self):
|
def test_check_sighash_types_sighash_none_anyonecanpay(self):
|
||||||
self.txin.sighash=Sighash.NONE|Sighash.ANYONECANPAY
|
self.txin.sighash=Sighash.NONE|Sighash.ANYONECANPAY
|
||||||
self.txin.pubkeys = [bfh('03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16')]
|
|
||||||
privkey = bfh('fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323')
|
privkey = bfh('fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
@@ -997,7 +987,6 @@ class TestSighashTypes(ElectrumTestCase):
|
|||||||
|
|
||||||
def test_check_sighash_types_sighash_single_anyonecanpay(self):
|
def test_check_sighash_types_sighash_single_anyonecanpay(self):
|
||||||
self.txin.sighash=Sighash.SINGLE|Sighash.ANYONECANPAY
|
self.txin.sighash=Sighash.SINGLE|Sighash.ANYONECANPAY
|
||||||
self.txin.pubkeys = [bfh('02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b')]
|
|
||||||
privkey = bfh('428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890')
|
privkey = bfh('428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890')
|
||||||
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
tx = PartialTransaction.from_io(inputs=[self.txin], outputs=[self.txout1,self.txout2], locktime=self.locktime, version=1, BIP69_sort=False)
|
||||||
sig = tx.sign_txin(0,privkey)
|
sig = tx.sign_txin(0,privkey)
|
||||||
|
|||||||
@@ -729,7 +729,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertTrue(tx.is_segwit())
|
self.assertTrue(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet1.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet1.is_mine(wallet1.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet1.is_mine(wallet1.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -749,7 +748,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertFalse(tx.is_segwit())
|
self.assertFalse(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet2.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -809,7 +807,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertFalse(tx.is_segwit())
|
self.assertFalse(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet1a.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -829,7 +826,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertFalse(tx.is_segwit())
|
self.assertFalse(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet2.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -908,7 +904,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertTrue(tx.is_segwit())
|
self.assertTrue(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet1a.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -937,7 +932,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertTrue(tx.is_segwit())
|
self.assertTrue(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet2a.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet2a.is_mine(wallet2a.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet2a.is_mine(wallet2a.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -987,7 +981,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertFalse(tx.is_segwit())
|
self.assertFalse(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet1a.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet1a.is_mine(wallet1a.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
@@ -1007,7 +1000,6 @@ class TestWalletSending(ElectrumTestCase):
|
|||||||
self.assertTrue(tx.is_complete())
|
self.assertTrue(tx.is_complete())
|
||||||
self.assertTrue(tx.is_segwit())
|
self.assertTrue(tx.is_segwit())
|
||||||
self.assertEqual(1, len(tx.inputs()))
|
self.assertEqual(1, len(tx.inputs()))
|
||||||
self.assertEqual(wallet2.txin_type, tx.inputs()[0].script_type)
|
|
||||||
tx_copy = tx_from_any(tx.serialize())
|
tx_copy = tx_from_any(tx.serialize())
|
||||||
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
self.assertTrue(wallet2.is_mine(wallet2.adb.get_txin_address(tx_copy.inputs()[0])))
|
||||||
|
|
||||||
|
|||||||
@@ -741,7 +741,6 @@ class Transaction:
|
|||||||
return ''
|
return ''
|
||||||
assert isinstance(txin, PartialTxInput)
|
assert isinstance(txin, PartialTxInput)
|
||||||
|
|
||||||
_type = txin.script_type
|
|
||||||
if not txin.is_segwit():
|
if not txin.is_segwit():
|
||||||
return construct_witness([])
|
return construct_witness([])
|
||||||
|
|
||||||
@@ -801,7 +800,7 @@ class Transaction:
|
|||||||
if script := sc.scriptcode_for_sighash:
|
if script := sc.scriptcode_for_sighash:
|
||||||
return script.hex()
|
return script.hex()
|
||||||
raise Exception(f"don't know scriptcode for descriptor: {desc.to_string()}")
|
raise Exception(f"don't know scriptcode for descriptor: {desc.to_string()}")
|
||||||
raise UnknownTxinType(f'cannot construct preimage_script for txin_type: {txin.script_type}')
|
raise UnknownTxinType(f'cannot construct preimage_script')
|
||||||
|
|
||||||
def _calc_bip143_shared_txdigest_fields(self) -> BIP143SharedTxDigestFields:
|
def _calc_bip143_shared_txdigest_fields(self) -> BIP143SharedTxDigestFields:
|
||||||
inputs = self.inputs()
|
inputs = self.inputs()
|
||||||
@@ -1188,9 +1187,6 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||||||
self._unknown = {} # type: Dict[bytes, bytes]
|
self._unknown = {} # type: Dict[bytes, bytes]
|
||||||
|
|
||||||
self.script_descriptor = None # type: Optional[Descriptor]
|
self.script_descriptor = None # type: Optional[Descriptor]
|
||||||
self.script_type = 'unknown'
|
|
||||||
self.num_sig = 0 # type: int # num req sigs for multisig
|
|
||||||
self.pubkeys = [] # type: List[bytes] # note: order matters
|
|
||||||
self._trusted_value_sats = None # type: Optional[int]
|
self._trusted_value_sats = None # type: Optional[int]
|
||||||
self._trusted_address = None # type: Optional[str]
|
self._trusted_address = None # type: Optional[str]
|
||||||
self._is_p2sh_segwit = None # type: Optional[bool] # None means unknown
|
self._is_p2sh_segwit = None # type: Optional[bool] # None means unknown
|
||||||
@@ -1223,6 +1219,12 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||||||
self._witness_utxo = value
|
self._witness_utxo = value
|
||||||
self.validate_data()
|
self.validate_data()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pubkeys(self) -> Set[bytes]:
|
||||||
|
if desc := self.script_descriptor:
|
||||||
|
return desc.get_all_pubkeys()
|
||||||
|
return set()
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
d = super().to_json()
|
d = super().to_json()
|
||||||
d.update({
|
d.update({
|
||||||
@@ -1402,22 +1404,6 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||||||
return self.witness_utxo.scriptpubkey
|
return self.witness_utxo.scriptpubkey
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_script_type(self) -> None:
|
|
||||||
if self.scriptpubkey is None:
|
|
||||||
return
|
|
||||||
type = get_script_type_from_output_script(self.scriptpubkey)
|
|
||||||
inner_type = None
|
|
||||||
if type is not None:
|
|
||||||
if type == 'p2sh':
|
|
||||||
inner_type = get_script_type_from_output_script(self.redeem_script)
|
|
||||||
elif type == 'p2wsh':
|
|
||||||
inner_type = get_script_type_from_output_script(self.witness_script)
|
|
||||||
if inner_type is not None:
|
|
||||||
type = inner_type + '-' + type
|
|
||||||
if type in ('p2pkh', 'p2wpkh-p2sh', 'p2wpkh'):
|
|
||||||
self.script_type = type
|
|
||||||
return
|
|
||||||
|
|
||||||
def is_complete(self) -> bool:
|
def is_complete(self) -> bool:
|
||||||
if self.script_sig is not None and self.witness is not None:
|
if self.script_sig is not None and self.witness is not None:
|
||||||
return True
|
return True
|
||||||
@@ -1434,6 +1420,11 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_satisfaction_progress(self) -> Tuple[int, int]:
|
||||||
|
if desc := self.script_descriptor:
|
||||||
|
return desc.get_satisfaction_progress(sigdata=self.part_sigs)
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
def finalize(self) -> None:
|
def finalize(self) -> None:
|
||||||
def clear_fields_when_finalized():
|
def clear_fields_when_finalized():
|
||||||
# BIP-174: "All other data except the UTXO and unknown fields in the
|
# BIP-174: "All other data except the UTXO and unknown fields in the
|
||||||
@@ -1547,12 +1538,15 @@ class PartialTxOutput(TxOutput, PSBTSection):
|
|||||||
self._unknown = {} # type: Dict[bytes, bytes]
|
self._unknown = {} # type: Dict[bytes, bytes]
|
||||||
|
|
||||||
self.script_descriptor = None # type: Optional[Descriptor]
|
self.script_descriptor = None # type: Optional[Descriptor]
|
||||||
self.script_type = 'unknown'
|
|
||||||
self.num_sig = 0 # num req sigs for multisig
|
|
||||||
self.pubkeys = [] # type: List[bytes] # note: order matters
|
|
||||||
self.is_mine = False # type: bool # whether the wallet considers the output to be ismine
|
self.is_mine = False # type: bool # whether the wallet considers the output to be ismine
|
||||||
self.is_change = False # type: bool # whether the wallet considers the output to be change
|
self.is_change = False # type: bool # whether the wallet considers the output to be change
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pubkeys(self) -> Set[bytes]:
|
||||||
|
if desc := self.script_descriptor:
|
||||||
|
return desc.get_all_pubkeys()
|
||||||
|
return set()
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
d = super().to_json()
|
d = super().to_json()
|
||||||
d.update({
|
d.update({
|
||||||
@@ -1947,15 +1941,12 @@ class PartialTransaction(Transaction):
|
|||||||
return all([txin.is_complete() for txin in self.inputs()])
|
return all([txin.is_complete() for txin in self.inputs()])
|
||||||
|
|
||||||
def signature_count(self) -> Tuple[int, int]:
|
def signature_count(self) -> Tuple[int, int]:
|
||||||
s = 0 # "num Sigs we have"
|
nhave, nreq = 0, 0
|
||||||
r = 0 # "Required"
|
|
||||||
for txin in self.inputs():
|
for txin in self.inputs():
|
||||||
if txin.is_coinbase_input():
|
a, b = txin.get_satisfaction_progress()
|
||||||
continue
|
nhave += a
|
||||||
signatures = list(txin.part_sigs.values())
|
nreq += b
|
||||||
s += len(signatures)
|
return nhave, nreq
|
||||||
r += txin.num_sig
|
|
||||||
return s, r
|
|
||||||
|
|
||||||
def serialize(self) -> str:
|
def serialize(self) -> str:
|
||||||
"""Returns PSBT as base64 text, or raw hex of network tx (if complete)."""
|
"""Returns PSBT as base64 text, or raw hex of network tx (if complete)."""
|
||||||
@@ -2104,14 +2095,6 @@ class PartialTransaction(Transaction):
|
|||||||
assert not self.is_complete()
|
assert not self.is_complete()
|
||||||
self.invalidate_ser_cache()
|
self.invalidate_ser_cache()
|
||||||
|
|
||||||
def update_txin_script_type(self):
|
|
||||||
"""Determine the script_type of each input by analyzing the scripts.
|
|
||||||
It updates all tx-Inputs, NOT only the wallet owned, if the
|
|
||||||
scriptpubkey is present.
|
|
||||||
"""
|
|
||||||
for txin in self.inputs():
|
|
||||||
if txin.script_type in ('unknown', 'address'):
|
|
||||||
txin.set_script_type()
|
|
||||||
|
|
||||||
def pack_bip32_root_fingerprint_and_int_path(xfp: bytes, path: Sequence[int]) -> bytes:
|
def pack_bip32_root_fingerprint_and_int_path(xfp: bytes, path: Sequence[int]) -> bytes:
|
||||||
if len(xfp) != 4:
|
if len(xfp) != 4:
|
||||||
|
|||||||
@@ -126,13 +126,6 @@ async def _append_utxos_to_inputs(
|
|||||||
txin.utxo = prev_tx
|
txin.utxo = prev_tx
|
||||||
txin.block_height = int(item['height'])
|
txin.block_height = int(item['height'])
|
||||||
txin.script_descriptor = script_descriptor
|
txin.script_descriptor = script_descriptor
|
||||||
# TODO rm as much of below (.num_sig / .pubkeys) as possible
|
|
||||||
# TODO need unit tests for other scripts (only have p2pk atm)
|
|
||||||
txin.script_type = txin_type
|
|
||||||
txin.pubkeys = [bfh(pubkey)]
|
|
||||||
txin.num_sig = 1
|
|
||||||
if txin_type == 'p2wpkh-p2sh':
|
|
||||||
txin.redeem_script = bfh(bitcoin.p2wpkh_nested_script(pubkey))
|
|
||||||
inputs.append(txin)
|
inputs.append(txin)
|
||||||
|
|
||||||
u = await network.listunspent_for_scripthash(scripthash)
|
u = await network.listunspent_for_scripthash(scripthash)
|
||||||
@@ -2151,10 +2144,6 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
tx_new.add_info_from_wallet(self)
|
tx_new.add_info_from_wallet(self)
|
||||||
return tx_new
|
return tx_new
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def _add_input_sig_info(self, txin: PartialTxInput, address: str, *, only_der_suffix: bool) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _add_txinout_derivation_info(self, txinout: Union[PartialTxInput, PartialTxOutput],
|
def _add_txinout_derivation_info(self, txinout: Union[PartialTxInput, PartialTxOutput],
|
||||||
address: str, *, only_der_suffix: bool) -> None:
|
address: str, *, only_der_suffix: bool) -> None:
|
||||||
pass # implemented by subclasses
|
pass # implemented by subclasses
|
||||||
@@ -2208,22 +2197,19 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
self.lnworker.swap_manager.add_txin_info(txin)
|
self.lnworker.swap_manager.add_txin_info(txin)
|
||||||
return
|
return
|
||||||
txin.script_descriptor = self._get_script_descriptor_for_address(address)
|
txin.script_descriptor = self._get_script_descriptor_for_address(address)
|
||||||
# set script_type first, as later checks might rely on it: # TODO rm most of below in favour of osd
|
if txin.redeem_script is None: # FIXME should be set in transaction.py instead, based on the script desc
|
||||||
txin.script_type = self.get_txin_type(address)
|
|
||||||
txin.num_sig = self.m if isinstance(self, Multisig_Wallet) else 1
|
|
||||||
if txin.redeem_script is None:
|
|
||||||
try:
|
try:
|
||||||
redeem_script_hex = self.get_redeem_script(address)
|
redeem_script_hex = self.get_redeem_script(address)
|
||||||
txin.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
|
txin.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
|
||||||
except UnknownTxinType:
|
except UnknownTxinType:
|
||||||
pass
|
pass
|
||||||
if txin.witness_script is None:
|
if txin.witness_script is None: # FIXME should be set in transaction.py instead, based on the script desc
|
||||||
try:
|
try:
|
||||||
witness_script_hex = self.get_witness_script(address)
|
witness_script_hex = self.get_witness_script(address)
|
||||||
txin.witness_script = bfh(witness_script_hex) if witness_script_hex else None
|
txin.witness_script = bfh(witness_script_hex) if witness_script_hex else None
|
||||||
except UnknownTxinType:
|
except UnknownTxinType:
|
||||||
pass
|
pass
|
||||||
self._add_input_sig_info(txin, address, only_der_suffix=only_der_suffix)
|
self._add_txinout_derivation_info(txin, address, only_der_suffix=only_der_suffix)
|
||||||
txin.block_height = self.adb.get_tx_height(txin.prevout.txid.hex()).height
|
txin.block_height = self.adb.get_tx_height(txin.prevout.txid.hex()).height
|
||||||
|
|
||||||
def _get_script_descriptor_for_address(self, address: str) -> Optional[Descriptor]:
|
def _get_script_descriptor_for_address(self, address: str) -> Optional[Descriptor]:
|
||||||
@@ -2306,19 +2292,16 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
if not is_mine:
|
if not is_mine:
|
||||||
return
|
return
|
||||||
txout.script_descriptor = self._get_script_descriptor_for_address(address)
|
txout.script_descriptor = self._get_script_descriptor_for_address(address)
|
||||||
txout.script_type = self.get_txin_type(address)
|
|
||||||
txout.is_mine = True
|
txout.is_mine = True
|
||||||
txout.is_change = self.is_change(address)
|
txout.is_change = self.is_change(address)
|
||||||
if isinstance(self, Multisig_Wallet):
|
|
||||||
txout.num_sig = self.m
|
|
||||||
self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix)
|
self._add_txinout_derivation_info(txout, address, only_der_suffix=only_der_suffix)
|
||||||
if txout.redeem_script is None:
|
if txout.redeem_script is None: # FIXME should be set in transaction.py instead, based on the script desc
|
||||||
try:
|
try:
|
||||||
redeem_script_hex = self.get_redeem_script(address)
|
redeem_script_hex = self.get_redeem_script(address)
|
||||||
txout.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
|
txout.redeem_script = bfh(redeem_script_hex) if redeem_script_hex else None
|
||||||
except UnknownTxinType:
|
except UnknownTxinType:
|
||||||
pass
|
pass
|
||||||
if txout.witness_script is None:
|
if txout.witness_script is None: # FIXME should be set in transaction.py instead, based on the script desc
|
||||||
try:
|
try:
|
||||||
witness_script_hex = self.get_witness_script(address)
|
witness_script_hex = self.get_witness_script(address)
|
||||||
txout.witness_script = bfh(witness_script_hex) if witness_script_hex else None
|
txout.witness_script = bfh(witness_script_hex) if witness_script_hex else None
|
||||||
@@ -3189,20 +3172,6 @@ class Imported_Wallet(Simple_Wallet):
|
|||||||
if addr != bitcoin.pubkey_to_address(txin_type, pubkey):
|
if addr != bitcoin.pubkey_to_address(txin_type, pubkey):
|
||||||
raise InternalAddressCorruption()
|
raise InternalAddressCorruption()
|
||||||
|
|
||||||
def _add_input_sig_info(self, txin, address, *, only_der_suffix):
|
|
||||||
if not self.is_mine(address):
|
|
||||||
return
|
|
||||||
if txin.script_type in ('unknown', 'address'):
|
|
||||||
return
|
|
||||||
elif txin.script_type in ('p2pkh', 'p2wpkh', 'p2wpkh-p2sh'):
|
|
||||||
pubkey = self.get_public_key(address)
|
|
||||||
if not pubkey:
|
|
||||||
return
|
|
||||||
txin.pubkeys = [bfh(pubkey)]
|
|
||||||
else:
|
|
||||||
raise Exception(f'Unexpected script type: {txin.script_type}. '
|
|
||||||
f'Imported wallets are not implemented to handle this.')
|
|
||||||
|
|
||||||
def pubkeys_to_address(self, pubkeys):
|
def pubkeys_to_address(self, pubkeys):
|
||||||
pubkey = pubkeys[0]
|
pubkey = pubkeys[0]
|
||||||
# FIXME This is slow.
|
# FIXME This is slow.
|
||||||
@@ -3330,14 +3299,10 @@ class Deterministic_Wallet(Abstract_Wallet):
|
|||||||
return {k.derive_pubkey(*der_suffix): (k, der_suffix)
|
return {k.derive_pubkey(*der_suffix): (k, der_suffix)
|
||||||
for k in self.get_keystores()}
|
for k in self.get_keystores()}
|
||||||
|
|
||||||
def _add_input_sig_info(self, txin, address, *, only_der_suffix):
|
|
||||||
self._add_txinout_derivation_info(txin, address, only_der_suffix=only_der_suffix)
|
|
||||||
|
|
||||||
def _add_txinout_derivation_info(self, txinout, address, *, only_der_suffix):
|
def _add_txinout_derivation_info(self, txinout, address, *, only_der_suffix):
|
||||||
if not self.is_mine(address):
|
if not self.is_mine(address):
|
||||||
return
|
return
|
||||||
pubkey_deriv_info = self.get_public_keys_with_deriv_info(address)
|
pubkey_deriv_info = self.get_public_keys_with_deriv_info(address)
|
||||||
txinout.pubkeys = sorted([pk for pk in list(pubkey_deriv_info)])
|
|
||||||
for pubkey in pubkey_deriv_info:
|
for pubkey in pubkey_deriv_info:
|
||||||
ks, der_suffix = pubkey_deriv_info[pubkey]
|
ks, der_suffix = pubkey_deriv_info[pubkey]
|
||||||
fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix,
|
fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix,
|
||||||
|
|||||||
Reference in New Issue
Block a user