Browse Source

transaction: tx.sign API change: rm hex usage

master
SomberNight 2 years ago
parent
commit
13d9677e53
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 8
      electrum/commands.py
  2. 22
      electrum/gui/qt/main_window.py
  3. 4
      electrum/gui/qt/send_tab.py
  4. 6
      electrum/gui/qt/transaction_dialog.py
  5. 14
      electrum/keystore.py
  6. 2
      electrum/lnchannel.py
  7. 6
      electrum/lnsweep.py
  8. 2
      electrum/lnutil.py
  9. 15
      electrum/transaction.py
  10. 18
      electrum/wallet.py
  11. 4
      tests/test_lnutil.py

8
electrum/commands.py

@ -422,9 +422,9 @@ class Commands:
sec = txin_dict.get('privkey')
if sec:
txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)
pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed)
keypairs[pubkey] = privkey, compressed
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type)
pubkey = ecc.ECPrivkey(privkey).get_public_key_bytes(compressed=compressed)
keypairs[pubkey] = privkey
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=txin_type)
txin.script_descriptor = desc
inputs.append(txin)
@ -465,7 +465,7 @@ class Commands:
if address in txins_dict.keys():
for txin in txins_dict[address]:
txin.script_descriptor = desc
tx.sign({pubkey.hex(): (priv2, compressed)})
tx.sign({pubkey: priv2})
return tx.serialize()

22
electrum/gui/qt/main_window.py

@ -34,7 +34,7 @@ import base64
from functools import partial
import queue
import asyncio
from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set
from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set, Mapping
import concurrent.futures
from PyQt5.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont, QFontMetrics
@ -1106,7 +1106,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self,
tx: Transaction,
*,
external_keypairs=None,
external_keypairs: Mapping[bytes, bytes] = None,
payment_identifier: PaymentIdentifier = None,
):
show_transaction(tx, parent=self, external_keypairs=external_keypairs, payment_identifier=payment_identifier)
@ -1269,10 +1269,24 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.send_tab.broadcast_transaction(tx, payment_identifier=payment_identifier)
@protected
def sign_tx(self, tx, *, callback, external_keypairs, password):
def sign_tx(
self,
tx: PartialTransaction,
*,
callback,
external_keypairs: Optional[Mapping[bytes, bytes]],
password,
):
self.sign_tx_with_password(tx, callback=callback, password=password, external_keypairs=external_keypairs)
def sign_tx_with_password(self, tx: PartialTransaction, *, callback, password, external_keypairs=None):
def sign_tx_with_password(
self,
tx: PartialTransaction,
*,
callback,
password,
external_keypairs: Mapping[bytes, bytes] = None,
):
'''Sign the transaction in a separate thread. When done, calls
the callback with a success code of True or False.
'''

4
electrum/gui/qt/send_tab.py

@ -3,7 +3,7 @@
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
from decimal import Decimal
from typing import Optional, TYPE_CHECKING, Sequence, List, Callable, Union
from typing import Optional, TYPE_CHECKING, Sequence, List, Callable, Union, Mapping
from PyQt5.QtCore import pyqtSignal, QPoint, QSize, Qt
from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QGridLayout, QHBoxLayout,
QWidget, QToolTip, QPushButton, QApplication)
@ -295,7 +295,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
outputs: List[PartialTxOutput],
*,
nonlocal_only=False,
external_keypairs=None,
external_keypairs: Mapping[bytes, bytes] = None,
get_coins: Callable[..., Sequence[PartialTxInput]] = None,
invoice: Optional[Invoice] = None
) -> None:

6
electrum/gui/qt/transaction_dialog.py

@ -30,7 +30,7 @@ import copy
import datetime
import traceback
import time
from typing import TYPE_CHECKING, Callable, Optional, List, Union, Tuple
from typing import TYPE_CHECKING, Callable, Optional, List, Union, Tuple, Mapping
from functools import partial
from decimal import Decimal
@ -410,7 +410,7 @@ def show_transaction(
*,
parent: 'ElectrumWindow',
prompt_if_unsaved: bool = False,
external_keypairs=None,
external_keypairs: Mapping[bytes, bytes] = None,
payment_identifier: 'PaymentIdentifier' = None,
):
try:
@ -438,7 +438,7 @@ class TxDialog(QDialog, MessageBoxMixin):
*,
parent: 'ElectrumWindow',
prompt_if_unsaved: bool,
external_keypairs=None,
external_keypairs: Mapping[bytes, bytes] = None,
payment_identifier: 'PaymentIdentifier' = None,
):
'''Transactions in the wallet will show their description.

14
electrum/keystore.py

@ -107,13 +107,13 @@ class KeyStore(Logger, ABC):
"""Returns whether the keystore can be encrypted with a password."""
pass
def _get_tx_derivations(self, tx: 'PartialTransaction') -> Dict[str, Union[Sequence[int], str]]:
def _get_tx_derivations(self, tx: 'PartialTransaction') -> Dict[bytes, Union[Sequence[int], str]]:
keypairs = {}
for txin in tx.inputs():
keypairs.update(self._get_txin_derivations(txin))
return keypairs
def _get_txin_derivations(self, txin: 'PartialTxInput') -> Dict[str, Union[Sequence[int], str]]:
def _get_txin_derivations(self, txin: 'PartialTxInput') -> Dict[bytes, Union[Sequence[int], str]]:
if txin.is_complete():
return {}
keypairs = {}
@ -124,7 +124,7 @@ class KeyStore(Logger, ABC):
derivation = self.get_pubkey_derivation(pubkey, txin)
if not derivation:
continue
keypairs[pubkey.hex()] = derivation
keypairs[pubkey] = derivation
return keypairs
def can_sign(self, tx: 'Transaction', *, ignore_watching_only=False) -> bool:
@ -237,9 +237,11 @@ class Software_KeyStore(KeyStore):
# Raise if password is not correct.
self.check_password(password)
# Add private keys
keypairs = self._get_tx_derivations(tx)
for k, v in keypairs.items():
keypairs[k] = self.get_private_key(v, password)
keypairs = {}
pubkey_to_deriv_map = self._get_tx_derivations(tx)
for pubkey, deriv in pubkey_to_deriv_map.items():
privkey, is_compressed = self.get_private_key(deriv, password)
keypairs[pubkey] = privkey
# Sign
if keypairs:
tx.sign(keypairs)

2
electrum/lnchannel.py

@ -1614,7 +1614,7 @@ class Channel(AbstractChannel):
def force_close_tx(self) -> PartialTransaction:
tx = self.get_latest_commitment(LOCAL)
assert self.signature_fits(tx)
tx.sign({self.config[LOCAL].multisig_key.pubkey.hex(): (self.config[LOCAL].multisig_key.privkey, True)})
tx.sign({self.config[LOCAL].multisig_key.pubkey: self.config[LOCAL].multisig_key.privkey})
remote_sig = self.config[LOCAL].current_commitment_signature
remote_sig = ecc.ecdsa_der_sig_from_ecdsa_sig64(remote_sig) + Sighash.to_sigbytes(Sighash.ALL)
tx.add_signature_to_txin(txin_idx=0,

6
electrum/lnsweep.py

@ -511,12 +511,12 @@ def create_sweeptx_their_ctx_to_remote(
sweep_address: str, ctx: Transaction, output_idx: int,
our_payment_privkey: ecc.ECPrivkey,
config: SimpleConfig) -> Optional[PartialTransaction]:
our_payment_pubkey = our_payment_privkey.get_public_key_hex(compressed=True)
our_payment_pubkey = our_payment_privkey.get_public_key_bytes(compressed=True)
val = ctx.outputs()[output_idx].value
prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx)
txin = PartialTxInput(prevout=prevout)
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.hex(), script_type='p2wpkh')
txin.script_descriptor = desc
sweep_inputs = [txin]
tx_size_bytes = 110 # approx size of p2wpkh->p2wpkh
@ -526,7 +526,7 @@ def create_sweeptx_their_ctx_to_remote(
sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
sweep_tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs)
sweep_tx.set_rbf(True)
sweep_tx.sign({our_payment_pubkey: (our_payment_privkey.get_secret_bytes(), True)})
sweep_tx.sign({our_payment_pubkey: our_payment_privkey.get_secret_bytes()})
if not sweep_tx.is_complete():
raise Exception('channel close sweep tx is not complete')
return sweep_tx

2
electrum/lnutil.py

@ -1084,7 +1084,7 @@ def make_commitment_output_to_remote_address(remote_payment_pubkey: bytes) -> st
return bitcoin.pubkey_to_address('p2wpkh', remote_payment_pubkey.hex())
def sign_and_get_sig_string(tx: PartialTransaction, local_config, remote_config):
tx.sign({local_config.multisig_key.pubkey.hex(): (local_config.multisig_key.privkey, True)})
tx.sign({local_config.multisig_key.pubkey: local_config.multisig_key.privkey})
sig = tx.inputs()[0].part_sigs[local_config.multisig_key.pubkey]
sig_64 = ecdsa_sig64_from_der_sig(sig[:-1])
return sig_64

15
electrum/transaction.py

@ -33,7 +33,7 @@ import sys
import io
import base64
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
Callable, List, Dict, Set, TYPE_CHECKING)
Callable, List, Dict, Set, TYPE_CHECKING, Mapping)
from collections import defaultdict
from enum import IntEnum
import itertools
@ -2130,22 +2130,21 @@ class PartialTransaction(Transaction):
preimage = nVersion + txins + txouts + nLocktime + nHashType
return preimage
def sign(self, keypairs) -> None:
# keypairs: pubkey_hex -> (secret_bytes, is_compressed)
def sign(self, keypairs: Mapping[bytes, bytes]) -> None:
# keypairs: pubkey_bytes -> secret_bytes
bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields()
for i, txin in enumerate(self.inputs()):
pubkeys = [pk.hex() for pk in txin.pubkeys]
for pubkey in pubkeys:
for pubkey in txin.pubkeys:
if txin.is_complete():
break
if pubkey not in keypairs:
continue
_logger.info(f"adding signature for {pubkey}. spending utxo {txin.prevout.to_str()}")
sec, compressed = keypairs[pubkey]
sec = keypairs[pubkey]
sig = self.sign_txin(i, sec, bip143_shared_txdigest_fields=bip143_shared_txdigest_fields)
self.add_signature_to_txin(txin_idx=i, signing_pubkey=bfh(pubkey), sig=sig)
self.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey, sig=sig)
_logger.debug(f"is_complete {self.is_complete()}")
_logger.debug(f"tx.sign() finished. is_complete={self.is_complete()}")
self.invalidate_ser_cache()
def sign_txin(

18
electrum/wallet.py

@ -40,7 +40,7 @@ from functools import partial
from collections import defaultdict
from numbers import Number
from decimal import Decimal
from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set, Iterable
from typing import TYPE_CHECKING, List, Optional, Tuple, Union, NamedTuple, Sequence, Dict, Any, Set, Iterable, Mapping
from abc import ABC, abstractmethod
import itertools
import threading
@ -136,20 +136,22 @@ async def _append_utxos_to_inputs(
await group.spawn(append_single_utxo(item))
async def sweep_preparations(privkeys, network: 'Network', imax=100):
async def sweep_preparations(
privkeys: Iterable[str], network: 'Network', imax=100,
) -> Tuple[Sequence[PartialTxInput], Mapping[bytes, bytes]]:
async def find_utxos_for_privkey(txin_type, privkey, compressed):
pubkey = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed)
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey, script_type=txin_type)
async def find_utxos_for_privkey(txin_type: str, privkey: bytes, compressed: bool):
pubkey = ecc.ECPrivkey(privkey).get_public_key_bytes(compressed=compressed)
desc = descriptor.get_singlesig_descriptor_from_legacy_leaf(pubkey=pubkey.hex(), script_type=txin_type)
await _append_utxos_to_inputs(
inputs=inputs,
network=network,
script_descriptor=desc,
imax=imax)
keypairs[pubkey] = privkey, compressed
keypairs[pubkey] = privkey
inputs = [] # type: List[PartialTxInput]
keypairs = {}
keypairs = {} # type: Dict[bytes, bytes]
async with OldTaskGroup() as group:
for sec in privkeys:
txin_type, privkey, compressed = bitcoin.deserialize_privkey(sec)
@ -169,7 +171,7 @@ async def sweep_preparations(privkeys, network: 'Network', imax=100):
async def sweep(
privkeys,
privkeys: Iterable[str],
*,
network: 'Network',
config: 'SimpleConfig',

4
tests/test_lnutil.py

@ -725,7 +725,7 @@ class TestLNUtil(ElectrumTestCase):
ref_commit_tx_str = '02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8002c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de84311054a56a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0400473044022051b75c73198c6deee1a875871c3961832909acd297c6b908d59e3319e5185a46022055c419379c5051a78d00dbbce11b5b664a0c22815fbcc6fcef6b1937c383693901483045022100f51d2e566a70ba740fc5d8c0f07b9b93d2ed741c3c0860c613173de7d39e7968022041376d520e9c0e1ad52248ddf4b22e12be8763007df977253ef45a4ca3bdb7c001475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220'
self.assertEqual(str(our_commit_tx), ref_commit_tx_str)
def sign_and_insert_remote_sig(self, tx: PartialTransaction, remote_pubkey, remote_signature, pubkey, privkey):
def sign_and_insert_remote_sig(self, tx: PartialTransaction, remote_pubkey: bytes, remote_signature: bytes, pubkey: bytes, privkey: bytes):
assert type(remote_pubkey) is bytes
assert len(remote_pubkey) == 33
assert type(remote_signature) is bytes
@ -733,7 +733,7 @@ class TestLNUtil(ElectrumTestCase):
assert type(privkey) is bytes
assert len(pubkey) == 33
assert len(privkey) == 33
tx.sign({pubkey.hex(): (privkey[:-1], True)})
tx.sign({pubkey: privkey[:-1]})
sighash = Sighash.to_sigbytes(Sighash.ALL)
tx.add_signature_to_txin(txin_idx=0, signing_pubkey=remote_pubkey, sig=remote_signature + sighash)

Loading…
Cancel
Save