transaction: tx.sign API change: rm hex usage
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -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.
|
||||
'''
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user