Browse Source

wallet/keystore: add apis for "add_slip_19_ownership_proofs_to_tx"

- implement it specifically for the "singlesig trezor" case
- aimed to be generic enough that support for more complex scripts
  and other keystores could be added later
master
SomberNight 2 years ago
parent
commit
0925f15280
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 6
      electrum/keystore.py
  2. 10
      electrum/plugins/trezor/clientbase.py
  3. 41
      electrum/plugins/trezor/trezor.py
  4. 10
      electrum/transaction.py
  5. 13
      electrum/wallet.py

6
electrum/keystore.py

@ -202,6 +202,12 @@ class KeyStore(Logger, ABC):
def can_have_deterministic_lightning_xprv(self) -> bool:
return False
def has_support_for_slip_19_ownership_proofs(self) -> bool:
return False
def add_slip_19_ownership_proofs_to_tx(self, tx: 'PartialTransaction', *, password) -> None:
raise NotImplementedError()
class Software_KeyStore(KeyStore):

10
electrum/plugins/trezor/clientbase.py

@ -258,6 +258,16 @@ class TrezorClientBase(HardwareClientBase, Logger):
with self.run_flow():
return trezorlib.btc.sign_tx(self.client, *args, **kwargs)
@runs_in_hwd_thread
def get_ownership_id(self, *args, **kwargs):
with self.run_flow():
return trezorlib.btc.get_ownership_id(self.client, *args, **kwargs)
@runs_in_hwd_thread
def get_ownership_proof(self, *args, **kwargs):
with self.run_flow():
return trezorlib.btc.get_ownership_proof(self.client, *args, **kwargs)
@runs_in_hwd_thread
def reset_device(self, *args, **kwargs):
with self.run_flow():

41
electrum/plugins/trezor/trezor.py

@ -34,6 +34,8 @@ try:
TxInputType, TxOutputType, TxOutputBinType, TransactionType, AmountUnit)
from trezorlib.client import PASSPHRASE_ON_DEVICE
import trezorlib.log
#trezorlib.log.enable_debug_output()
TREZORLIB = True
except Exception as e:
@ -95,6 +97,39 @@ class TrezorKeyStore(Hardware_KeyStore):
self.plugin.sign_transaction(self, tx, prev_tx)
def has_support_for_slip_19_ownership_proofs(self) -> bool:
return True
def add_slip_19_ownership_proofs_to_tx(self, tx: 'PartialTransaction', password) -> None:
assert isinstance(tx, PartialTransaction)
client = self.get_client()
assert isinstance(client, TrezorClientBase), client
for txin in tx.inputs():
if txin.is_coinbase_input():
continue
if txin.is_complete() or not txin.is_mine:
continue
assert txin.scriptpubkey
desc = txin.script_descriptor
assert desc
trezor_multisig = None
if multi := desc.get_simple_multisig():
# trezor_multisig = self._make_multisig(multi)
raise Exception("multisig not supported for slip-19 ownership proof")
trezor_script_type = self.plugin.get_trezor_input_script_type(desc.to_legacy_electrum_script_type())
my_pubkey, full_path = self.find_my_pubkey_in_txinout(txin)
if full_path:
trezor_address_n = full_path
else:
continue
proof, _proof_sig = client.get_ownership_proof(
coin_name=self.plugin.get_coin_name(),
n=trezor_address_n,
multisig=trezor_multisig,
script_type=trezor_script_type,
)
txin.slip_19_ownership_proof = proof
class TrezorInitSettings(NamedTuple):
word_count: int
@ -352,11 +387,13 @@ class TrezorPlugin(HW_PluginBase):
assert isinstance(tx, PartialTransaction)
assert isinstance(txin, PartialTxInput)
assert keystore
if txin.is_complete() or not txin.is_mine:
if txin.is_complete() or not txin.is_mine: # we don't sign
txinputtype.script_type = InputScriptType.EXTERNAL
assert txin.scriptpubkey
txinputtype.script_pubkey = txin.scriptpubkey
else:
if not txin.is_mine and txin.slip_19_ownership_proof:
txinputtype.ownership_proof = txin.slip_19_ownership_proof
else: # we sign
desc = txin.script_descriptor
assert desc
if multi := desc.get_simple_multisig():

10
electrum/transaction.py

@ -1289,6 +1289,7 @@ class PSBTInputType(IntEnum):
BIP32_DERIVATION = 6
FINAL_SCRIPTSIG = 7
FINAL_SCRIPTWITNESS = 8
SLIP19_OWNERSHIP_PROOF = 0x19
class PSBTOutputType(IntEnum):
@ -1386,6 +1387,7 @@ class PartialTxInput(TxInput, PSBTSection):
self.bip32_paths = {} # type: Dict[bytes, Tuple[bytes, Sequence[int]]] # pubkey -> (xpub_fingerprint, path)
self.redeem_script = None # type: Optional[bytes]
self.witness_script = None # type: Optional[bytes]
self.slip_19_ownership_proof = None # type: Optional[bytes]
self._unknown = {} # type: Dict[bytes, bytes]
self._script_descriptor = None # type: Optional[Descriptor]
@ -1439,6 +1441,7 @@ class PartialTxInput(TxInput, PSBTSection):
'part_sigs': {pubkey.hex(): sig.hex() for pubkey, sig in self.part_sigs.items()},
'bip32_paths': {pubkey.hex(): (xfp.hex(), bip32.convert_bip32_intpath_to_strpath(path))
for pubkey, (xfp, path) in self.bip32_paths.items()},
'slip_19_ownership_proof': self.slip_19_ownership_proof.hex() if self.slip_19_ownership_proof else None,
'unknown_psbt_fields': {key.hex(): val.hex() for key, val in self._unknown.items()},
})
return d
@ -1553,6 +1556,11 @@ class PartialTxInput(TxInput, PSBTSection):
raise SerializationError(f"duplicate key: {repr(kt)}")
self.witness = val
if key: raise SerializationError(f"key for {repr(kt)} must be empty")
elif kt == PSBTInputType.SLIP19_OWNERSHIP_PROOF:
if self.slip_19_ownership_proof is not None:
raise SerializationError(f"duplicate key: {repr(kt)}")
self.slip_19_ownership_proof = val
if key: raise SerializationError(f"key for {repr(kt)} must be empty")
else:
full_key = self.get_fullkey_from_keytype_and_key(kt, key)
if full_key in self._unknown:
@ -1579,6 +1587,8 @@ class PartialTxInput(TxInput, PSBTSection):
wr(PSBTInputType.FINAL_SCRIPTSIG, self.script_sig)
if self.witness is not None:
wr(PSBTInputType.FINAL_SCRIPTWITNESS, self.witness)
if self.slip_19_ownership_proof:
wr(PSBTInputType.SLIP19_OWNERSHIP_PROOF, self.slip_19_ownership_proof)
for full_key, val in sorted(self._unknown.items()):
key_type, key = self.get_keytype_and_key_from_fullkey(full_key)
wr(key_type, val, key=key)

13
electrum/wallet.py

@ -2447,6 +2447,12 @@ class Abstract_Wallet(ABC, Logger, EventListener):
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
def has_support_for_slip_19_ownership_proofs(self) -> bool:
return False
def add_slip_19_ownership_proofs_to_tx(self, tx: PartialTransaction) -> None:
raise NotImplementedError()
def get_script_descriptor_for_address(self, address: str) -> Optional[Descriptor]:
if not self.is_mine(address):
return None
@ -3765,6 +3771,13 @@ class Standard_Wallet(Simple_Wallet, Deterministic_Wallet):
pubkey = pubkeys[0]
return bitcoin.pubkey_to_address(self.txin_type, pubkey)
def has_support_for_slip_19_ownership_proofs(self) -> bool:
return self.keystore.has_support_for_slip_19_ownership_proofs()
def add_slip_19_ownership_proofs_to_tx(self, tx: PartialTransaction) -> None:
tx.add_info_from_wallet(self)
self.keystore.add_slip_19_ownership_proofs_to_tx(tx=tx, password=None)
class Multisig_Wallet(Deterministic_Wallet):
# generic m of n

Loading…
Cancel
Save