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 .invoices import PR_PAID
from .bitcoin import redeem_script_to_address from .bitcoin import redeem_script_to_address
from .crypto import sha256, sha256d from .crypto import sha256, sha256d
from .transaction import Transaction, PartialTransaction, TxInput from .transaction import Transaction, PartialTransaction, TxInput, Sighash
from .logging import Logger from .logging import Logger
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailure from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailure
from . import lnutil from . import lnutil
@ -1101,7 +1101,7 @@ class Channel(AbstractChannel):
data = self.config[LOCAL].current_htlc_signatures data = self.config[LOCAL].current_htlc_signatures
htlc_sigs = list(chunks(data, 64)) htlc_sigs = list(chunks(data, 64))
htlc_sig = htlc_sigs[htlc_relative_idx] 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 return remote_htlc_sig
def revoke_current_commitment(self): def revoke_current_commitment(self):
@ -1554,7 +1554,7 @@ class Channel(AbstractChannel):
assert self.signature_fits(tx) assert self.signature_fits(tx)
tx.sign({bh2u(self.config[LOCAL].multisig_key.pubkey): (self.config[LOCAL].multisig_key.privkey, True)}) 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 = 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, tx.add_signature_to_txin(txin_idx=0,
signing_pubkey=self.config[REMOTE].multisig_key.pubkey.hex(), signing_pubkey=self.config[REMOTE].multisig_key.pubkey.hex(),
sig=remote_sig.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) UnrelatedTransactionException)
from . import transaction from . import transaction
from .bitcoin import make_op_return 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 .logging import Logger
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment, from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure, process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
@ -2039,8 +2039,8 @@ class Peer(Logger):
assert our_scriptpubkey assert our_scriptpubkey
# estimate fee of closing tx # estimate fee of closing tx
dummy_sig, dummy_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0) dummy_sig, dummy_tx = chan.make_closing_tx(our_scriptpubkey, their_scriptpubkey, fee_sat=0)
our_sig = None our_sig = None # type: Optional[bytes]
closing_tx = None closing_tx = None # type: Optional[PartialTransaction]
is_initiator = chan.constraints.is_initiator is_initiator = chan.constraints.is_initiator
our_fee, our_fee_range = self.get_shutdown_fee_range(chan, dummy_tx, is_local) 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( closing_tx.add_signature_to_txin(
txin_idx=0, txin_idx=0,
signing_pubkey=chan.config[LOCAL].multisig_key.pubkey.hex(), 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( closing_tx.add_signature_to_txin(
txin_idx=0, txin_idx=0,
signing_pubkey=chan.config[REMOTE].multisig_key.pubkey.hex(), 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 # save local transaction and set state
try: try:
self.lnworker.wallet.adb.add_transaction(closing_tx) 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 import bip32, constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.keystore import Hardware_KeyStore 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.wallet import Standard_Wallet, Multisig_Wallet, Deterministic_Wallet
from electrum.util import bh2u, UserFacingException from electrum.util import bh2u, UserFacingException
from electrum.base_wizard import ScriptTypeNotSupported, BaseWizard from electrum.base_wizard import ScriptTypeNotSupported, BaseWizard
@ -523,7 +523,8 @@ class BitBox02Client(HardwareClientBase):
# Fill signatures # Fill signatures
if len(sigs) != len(tx.inputs()): if len(sigs) != len(tx.inputs()):
raise Exception("Incorrect number of inputs signed.") # Should never occur 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) tx.update_signatures(signatures)
def sign_message(self, keypath: str, message: bytes, script_type: str) -> bytes: 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.ecc import msg_magic
from electrum.wallet import Standard_Wallet from electrum.wallet import Standard_Wallet
from electrum import constants 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.i18n import _
from electrum.keystore import Hardware_KeyStore from electrum.keystore import Hardware_KeyStore
from electrum.util import to_string, UserCancelled, UserFacingException, bfh 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_r = int(signed['sig'][:64], 16)
sig_s = 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 = 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) tx.add_signature_to_txin(txin_idx=i, signing_pubkey=pubkey_bytes.hex(), sig=sig)
except UserCancelled: except UserCancelled:
raise 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.bip32 import BIP32Node
from electrum import constants from electrum import constants
from electrum.i18n import _ 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.keystore import Hardware_KeyStore
from electrum.plugin import Device, runs_in_hwd_thread from electrum.plugin import Device, runs_in_hwd_thread
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported
@ -330,7 +330,8 @@ class KeepKeyPlugin(HW_PluginBase):
outputs = self.tx_outputs(tx, keystore=keystore) outputs = self.tx_outputs(tx, keystore=keystore)
signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, signatures = client.sign_tx(self.get_coin_name(), inputs, outputs,
lock_time=tx.locktime, version=tx.version)[0] 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) tx.update_signatures(signatures)
@runs_in_hwd_thread @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 import constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import Device, runs_in_hwd_thread 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.keystore import Hardware_KeyStore
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported
@ -300,7 +300,8 @@ class SafeTPlugin(HW_PluginBase):
outputs = self.tx_outputs(tx, keystore=keystore) outputs = self.tx_outputs(tx, keystore=keystore)
signatures = client.sign_tx(self.get_coin_name(), inputs, outputs, signatures = client.sign_tx(self.get_coin_name(), inputs, outputs,
lock_time=tx.locktime, version=tx.version)[0] 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) tx.update_signatures(signatures)
@runs_in_hwd_thread @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 import constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import Device, runs_in_hwd_thread 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.keystore import Hardware_KeyStore
from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET
from electrum.logging import get_logger from electrum.logging import get_logger
@ -370,7 +370,8 @@ class TrezorPlugin(HW_PluginBase):
amount_unit=self.get_trezor_amount_unit(), amount_unit=self.get_trezor_amount_unit(),
serialize=False, serialize=False,
prev_txes=prev_tx) 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) tx.update_signatures(signatures)
@runs_in_hwd_thread @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, ScriptHtlc, extract_nodeid, calc_fees_for_commitment_tx, UpdateAddHtlc, LnFeatures,
ln_compare_features, IncompatibleLightningFeatures, ChannelType) ln_compare_features, IncompatibleLightningFeatures, ChannelType)
from electrum.util import bh2u, bfh, MyEncoder 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 electrum.lnworker import LNWallet
from . import ElectrumTestCase from . import ElectrumTestCase
@ -725,7 +725,8 @@ class TestLNUtil(ElectrumTestCase):
assert len(pubkey) == 33 assert len(pubkey) == 33
assert len(privkey) == 33 assert len(privkey) == 33
tx.sign({bh2u(pubkey): (privkey[:-1], True)}) 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): 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', 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): class Sighash(IntEnum):
# note: this is not an IntFlag, as ALL|NONE != SINGLE
ALL = 1 ALL = 1
NONE = 2 NONE = 2
SINGLE = 3 SINGLE = 3
ANYONECANPAY = 0x80 ANYONECANPAY = 0x80
@classmethod @classmethod
def is_valid(cls, sighash) -> bool: def is_valid(cls, sighash: int) -> bool:
for flag in Sighash: for flag in Sighash:
for base_flag in [Sighash.ALL, Sighash.NONE, Sighash.SINGLE]: for base_flag in [Sighash.ALL, Sighash.NONE, Sighash.SINGLE]:
if (flag & ~0x1f | base_flag) == sighash: if (flag & ~0x1f | base_flag) == sighash:
return True return True
return False return False
@classmethod
def to_sigbytes(cls, sighash: int) -> bytes:
return sighash.to_bytes(length=1, byteorder="big")
class TxOutput: class TxOutput:
scriptpubkey: bytes scriptpubkey: bytes
@ -1940,7 +1946,7 @@ class PartialTransaction(Transaction):
txin = inputs[txin_index] txin = inputs[txin_index]
sighash = txin.sighash if txin.sighash is not None else Sighash.ALL sighash = txin.sighash if txin.sighash is not None else Sighash.ALL
if not Sighash.is_valid(sighash): 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) nHashType = int_to_hex(sighash, 4)
preimage_script = self.get_preimage_script(txin) preimage_script = self.get_preimage_script(txin)
if txin.is_segwit(): if txin.is_segwit():
@ -1966,6 +1972,8 @@ class PartialTransaction(Transaction):
nSequence = int_to_hex(txin.nsequence, 4) nSequence = int_to_hex(txin.nsequence, 4)
preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType
else: 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() 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)) for k, txin in enumerate(inputs))
txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs) 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 = self.inputs()[txin_index]
txin.validate_data(for_signing=True) txin.validate_data(for_signing=True)
sighash = txin.sighash if txin.sighash is not None else Sighash.ALL 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, pre_hash = sha256d(bfh(self.serialize_preimage(txin_index,
bip143_shared_txdigest_fields=bip143_shared_txdigest_fields))) bip143_shared_txdigest_fields=bip143_shared_txdigest_fields)))
privkey = ecc.ECPrivkey(privkey_bytes) privkey = ecc.ECPrivkey(privkey_bytes)
sig = privkey.sign_transaction(pre_hash) sig = privkey.sign_transaction(pre_hash)
sig = bh2u(sig) + sighash_type sig = bh2u(sig) + Sighash.to_sigbytes(sighash).hex()
return sig return sig
def is_complete(self) -> bool: def is_complete(self) -> bool:

Loading…
Cancel
Save