Browse Source

transaction: rm hardcoded sighash magic numbers

master
SomberNight 3 years ago
parent
commit
1ce37c8bb1
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 6
      electrum/lnchannel.py
  2. 10
      electrum/lnpeer.py
  3. 5
      electrum/plugins/bitbox02/bitbox02.py
  4. 4
      electrum/plugins/digitalbitbox/digitalbitbox.py
  5. 5
      electrum/plugins/keepkey/keepkey.py
  6. 5
      electrum/plugins/safe_t/safe_t.py
  7. 5
      electrum/plugins/trezor/trezor.py
  8. 5
      electrum/tests/test_lnutil.py
  9. 15
      electrum/transaction.py

6
electrum/lnchannel.py

@ -39,7 +39,7 @@ from .util import bfh, bh2u, chunks, TxMinedInfo
from .invoices import PR_PAID
from .bitcoin import redeem_script_to_address
from .crypto import sha256, sha256d
from .transaction import Transaction, PartialTransaction, TxInput
from .transaction import Transaction, PartialTransaction, TxInput, Sighash
from .logging import Logger
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailure
from . import lnutil
@ -1101,7 +1101,7 @@ class Channel(AbstractChannel):
data = self.config[LOCAL].current_htlc_signatures
htlc_sigs = list(chunks(data, 64))
htlc_sig = htlc_sigs[htlc_relative_idx]
remote_htlc_sig = ecc.der_sig_from_sig_string(htlc_sig) + b'\x01'
remote_htlc_sig = ecc.der_sig_from_sig_string(htlc_sig) + Sighash.to_sigbytes(Sighash.ALL)
return remote_htlc_sig
def revoke_current_commitment(self):
@ -1554,7 +1554,7 @@ class Channel(AbstractChannel):
assert self.signature_fits(tx)
tx.sign({bh2u(self.config[LOCAL].multisig_key.pubkey): (self.config[LOCAL].multisig_key.privkey, True)})
remote_sig = self.config[LOCAL].current_commitment_signature
remote_sig = ecc.der_sig_from_sig_string(remote_sig) + b"\x01"
remote_sig = ecc.der_sig_from_sig_string(remote_sig) + Sighash.to_sigbytes(Sighash.ALL)
tx.add_signature_to_txin(txin_idx=0,
signing_pubkey=self.config[REMOTE].multisig_key.pubkey.hex(),
sig=remote_sig.hex())

10
electrum/lnpeer.py

@ -25,7 +25,7 @@ from .util import (bh2u, bfh, log_exceptions, ignore_exceptions, chunks, OldTask
UnrelatedTransactionException)
from . import transaction
from .bitcoin import make_op_return
from .transaction import PartialTxOutput, match_script_against_template
from .transaction import PartialTxOutput, match_script_against_template, Sighash
from .logging import Logger
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
@ -2039,8 +2039,8 @@ class Peer(Logger):
assert our_scriptpubkey
# estimate fee of closing tx
dummy_sig, dummy_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0)
our_sig = None
closing_tx = None
our_sig = None # type: Optional[bytes]
closing_tx = None # type: Optional[PartialTransaction]
is_initiator = chan.constraints.is_initiator
our_fee, our_fee_range = self.get_shutdown_fee_range(chan, dummy_tx, is_local)
@ -2185,11 +2185,11 @@ class Peer(Logger):
closing_tx.add_signature_to_txin(
txin_idx=0,
signing_pubkey=chan.config[LOCAL].multisig_key.pubkey.hex(),
sig=bh2u(der_sig_from_sig_string(our_sig) + b'\x01'))
sig=bh2u(der_sig_from_sig_string(our_sig) + Sighash.to_sigbytes(Sighash.ALL)))
closing_tx.add_signature_to_txin(
txin_idx=0,
signing_pubkey=chan.config[REMOTE].multisig_key.pubkey.hex(),
sig=bh2u(der_sig_from_sig_string(their_sig) + b'\x01'))
sig=bh2u(der_sig_from_sig_string(their_sig) + Sighash.to_sigbytes(Sighash.ALL)))
# save local transaction and set state
try:
self.lnworker.wallet.adb.add_transaction(closing_tx)

5
electrum/plugins/bitbox02/bitbox02.py

@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Dict, Tuple, Optional, List, Any, Callable
from electrum import bip32, constants
from electrum.i18n import _
from electrum.keystore import Hardware_KeyStore
from electrum.transaction import PartialTransaction
from electrum.transaction import PartialTransaction, Sighash
from electrum.wallet import Standard_Wallet, Multisig_Wallet, Deterministic_Wallet
from electrum.util import bh2u, UserFacingException
from electrum.base_wizard import ScriptTypeNotSupported, BaseWizard
@ -523,7 +523,8 @@ class BitBox02Client(HardwareClientBase):
# Fill signatures
if len(sigs) != len(tx.inputs()):
raise Exception("Incorrect number of inputs signed.") # Should never occur
signatures = [bh2u(ecc.der_sig_from_sig_string(x[1])) + "01" for x in sigs]
sighash = Sighash.to_sigbytes(Sighash.ALL).hex()
signatures = [bh2u(ecc.der_sig_from_sig_string(x[1])) + sighash for x in sigs]
tx.update_signatures(signatures)
def sign_message(self, keypath: str, message: bytes, script_type: str) -> bytes:

4
electrum/plugins/digitalbitbox/digitalbitbox.py

@ -23,7 +23,7 @@ from electrum import ecc
from electrum.ecc import msg_magic
from electrum.wallet import Standard_Wallet
from electrum import constants
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, Sighash
from electrum.i18n import _
from electrum.keystore import Hardware_KeyStore
from electrum.util import to_string, UserCancelled, UserFacingException, bfh
@ -645,7 +645,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
sig_r = int(signed['sig'][:64], 16)
sig_s = int(signed['sig'][64:], 16)
sig = ecc.der_sig_from_r_and_s(sig_r, sig_s)
sig = to_hexstr(sig) + '01'
sig = to_hexstr(sig) + Sighash.to_sigbytes(Sighash.ALL).hex()
tx.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_bytes.hex(), sig=sig)
except UserCancelled:
raise

5
electrum/plugins/keepkey/keepkey.py

@ -7,7 +7,7 @@ from electrum.util import bfh, bh2u, UserCancelled, UserFacingException
from electrum.bip32 import BIP32Node
from electrum import constants
from electrum.i18n import _
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput, Sighash
from electrum.keystore import Hardware_KeyStore
from electrum.plugin import Device, runs_in_hwd_thread
from electrum.base_wizard import ScriptTypeNotSupported
@ -330,7 +330,8 @@ class KeepKeyPlugin(HW_PluginBase):
outputs = self.tx_outputs(tx, keystore=keystore)
signatures = client.sign_tx(self.get_coin_name(), inputs, outputs,
lock_time=tx.locktime, version=tx.version)[0]
signatures = [(bh2u(x) + '01') for x in signatures]
sighash = Sighash.to_sigbytes(Sighash.ALL).hex()
signatures = [(bh2u(x) + sighash) for x in signatures]
tx.update_signatures(signatures)
@runs_in_hwd_thread

5
electrum/plugins/safe_t/safe_t.py

@ -8,7 +8,7 @@ from electrum.bip32 import BIP32Node
from electrum import constants
from electrum.i18n import _
from electrum.plugin import Device, runs_in_hwd_thread
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput, Sighash
from electrum.keystore import Hardware_KeyStore
from electrum.base_wizard import ScriptTypeNotSupported
@ -300,7 +300,8 @@ class SafeTPlugin(HW_PluginBase):
outputs = self.tx_outputs(tx, keystore=keystore)
signatures = client.sign_tx(self.get_coin_name(), inputs, outputs,
lock_time=tx.locktime, version=tx.version)[0]
signatures = [(bh2u(x) + '01') for x in signatures]
sighash = Sighash.to_sigbytes(Sighash.ALL).hex()
signatures = [(bh2u(x) + sighash) for x in signatures]
tx.update_signatures(signatures)
@runs_in_hwd_thread

5
electrum/plugins/trezor/trezor.py

@ -7,7 +7,7 @@ from electrum.bip32 import BIP32Node, convert_bip32_path_to_list_of_uint32 as pa
from electrum import constants
from electrum.i18n import _
from electrum.plugin import Device, runs_in_hwd_thread
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput, Sighash
from electrum.keystore import Hardware_KeyStore
from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET
from electrum.logging import get_logger
@ -370,7 +370,8 @@ class TrezorPlugin(HW_PluginBase):
amount_unit=self.get_trezor_amount_unit(),
serialize=False,
prev_txes=prev_tx)
signatures = [(bh2u(x) + '01') for x in signatures]
sighash = Sighash.to_sigbytes(Sighash.ALL).hex()
signatures = [(bh2u(x) + sighash) for x in signatures]
tx.update_signatures(signatures)
@runs_in_hwd_thread

5
electrum/tests/test_lnutil.py

@ -11,7 +11,7 @@ from electrum.lnutil import (RevocationStore, get_per_commitment_secret_from_see
ScriptHtlc, extract_nodeid, calc_fees_for_commitment_tx, UpdateAddHtlc, LnFeatures,
ln_compare_features, IncompatibleLightningFeatures, ChannelType)
from electrum.util import bh2u, bfh, MyEncoder
from electrum.transaction import Transaction, PartialTransaction
from electrum.transaction import Transaction, PartialTransaction, Sighash
from electrum.lnworker import LNWallet
from . import ElectrumTestCase
@ -725,7 +725,8 @@ class TestLNUtil(ElectrumTestCase):
assert len(pubkey) == 33
assert len(privkey) == 33
tx.sign({bh2u(pubkey): (privkey[:-1], True)})
tx.add_signature_to_txin(txin_idx=0, signing_pubkey=remote_pubkey.hex(), sig=remote_signature + "01")
sighash = Sighash.to_sigbytes(Sighash.ALL).hex()
tx.add_signature_to_txin(txin_idx=0, signing_pubkey=remote_pubkey.hex(), sig=remote_signature + sighash)
def test_get_compressed_pubkey_from_bech32(self):
self.assertEqual(b'\x03\x84\xef\x87\xd9d\xa2\xaaa7=\xff\xb8\xfe=t8[}>;\n\x13\xa8e\x8eo:\xf5Mi\xb5H',

15
electrum/transaction.py

@ -90,19 +90,25 @@ class MissingTxInputAmount(Exception):
class Sighash(IntEnum):
# note: this is not an IntFlag, as ALL|NONE != SINGLE
ALL = 1
NONE = 2
SINGLE = 3
ANYONECANPAY = 0x80
@classmethod
def is_valid(cls, sighash) -> bool:
def is_valid(cls, sighash: int) -> bool:
for flag in Sighash:
for base_flag in [Sighash.ALL, Sighash.NONE, Sighash.SINGLE]:
if (flag & ~0x1f | base_flag) == sighash:
return True
return False
@classmethod
def to_sigbytes(cls, sighash: int) -> bytes:
return sighash.to_bytes(length=1, byteorder="big")
class TxOutput:
scriptpubkey: bytes
@ -1940,7 +1946,7 @@ class PartialTransaction(Transaction):
txin = inputs[txin_index]
sighash = txin.sighash if txin.sighash is not None else Sighash.ALL
if not Sighash.is_valid(sighash):
raise Exception("SIGHASH_FLAG not supported!")
raise Exception(f"SIGHASH_FLAG ({sighash}) not supported!")
nHashType = int_to_hex(sighash, 4)
preimage_script = self.get_preimage_script(txin)
if txin.is_segwit():
@ -1966,6 +1972,8 @@ class PartialTransaction(Transaction):
nSequence = int_to_hex(txin.nsequence, 4)
preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType
else:
if sighash != Sighash.ALL:
raise Exception(f"SIGHASH_FLAG ({sighash}) not supported! (for legacy sighash)")
txins = var_int(len(inputs)) + ''.join(txin.serialize_to_network(script_sig=bfh(preimage_script) if txin_index==k else b"").hex()
for k, txin in enumerate(inputs))
txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs)
@ -1994,12 +2002,11 @@ class PartialTransaction(Transaction):
txin = self.inputs()[txin_index]
txin.validate_data(for_signing=True)
sighash = txin.sighash if txin.sighash is not None else Sighash.ALL
sighash_type = sighash.to_bytes(length=1, byteorder="big").hex()
pre_hash = sha256d(bfh(self.serialize_preimage(txin_index,
bip143_shared_txdigest_fields=bip143_shared_txdigest_fields)))
privkey = ecc.ECPrivkey(privkey_bytes)
sig = privkey.sign_transaction(pre_hash)
sig = bh2u(sig) + sighash_type
sig = bh2u(sig) + Sighash.to_sigbytes(sighash).hex()
return sig
def is_complete(self) -> bool:

Loading…
Cancel
Save