Browse Source

transaction: change Transaction.is_segwit_input(txin) to txin.is_segwit()

patch-4
SomberNight 5 years ago
parent
commit
eefb68c82b
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 2
      electrum/coinchooser.py
  2. 2
      electrum/plugins/keepkey/keepkey.py
  3. 2
      electrum/plugins/ledger/ledger.py
  4. 2
      electrum/plugins/safe_t/safe_t.py
  5. 50
      electrum/transaction.py
  6. 4
      electrum/wallet.py

2
electrum/coinchooser.py

@ -120,7 +120,7 @@ class CoinChooserBase(Logger):
constant_fee = fee_estimator_vb(2000) == fee_estimator_vb(200) constant_fee = fee_estimator_vb(2000) == fee_estimator_vb(200)
def make_Bucket(desc: str, coins: List[PartialTxInput]): def make_Bucket(desc: str, coins: List[PartialTxInput]):
witness = any(Transaction.is_segwit_input(coin, guess_for_address=True) for coin in coins) witness = any(coin.is_segwit(guess_for_address=True) for coin in coins)
# note that we're guessing whether the tx uses segwit based # note that we're guessing whether the tx uses segwit based
# on this single bucket # on this single bucket
weight = sum(Transaction.estimated_input_weight(coin, witness) weight = sum(Transaction.estimated_input_weight(coin, witness)

2
electrum/plugins/keepkey/keepkey.py

@ -53,7 +53,7 @@ class KeepKey_KeyStore(Hardware_KeyStore):
prev_tx = {} prev_tx = {}
for txin in tx.inputs(): for txin in tx.inputs():
tx_hash = txin.prevout.txid.hex() tx_hash = txin.prevout.txid.hex()
if txin.utxo is None and not Transaction.is_segwit_input(txin): if txin.utxo is None and not txin.is_segwit():
raise UserFacingException(_('Missing previous tx for legacy input.')) raise UserFacingException(_('Missing previous tx for legacy input.'))
prev_tx[tx_hash] = txin.utxo prev_tx[tx_hash] = txin.utxo

2
electrum/plugins/ledger/ledger.py

@ -384,7 +384,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
redeemScript = Transaction.get_preimage_script(txin) redeemScript = Transaction.get_preimage_script(txin)
txin_prev_tx = txin.utxo txin_prev_tx = txin.utxo
if txin_prev_tx is None and not Transaction.is_segwit_input(txin): if txin_prev_tx is None and not txin.is_segwit():
raise UserFacingException(_('Missing previous tx for legacy input.')) raise UserFacingException(_('Missing previous tx for legacy input.'))
txin_prev_tx_raw = txin_prev_tx.serialize() if txin_prev_tx else None txin_prev_tx_raw = txin_prev_tx.serialize() if txin_prev_tx else None
inputs.append([txin_prev_tx_raw, inputs.append([txin_prev_tx_raw,

2
electrum/plugins/safe_t/safe_t.py

@ -51,7 +51,7 @@ class SafeTKeyStore(Hardware_KeyStore):
prev_tx = {} prev_tx = {}
for txin in tx.inputs(): for txin in tx.inputs():
tx_hash = txin.prevout.txid.hex() tx_hash = txin.prevout.txid.hex()
if txin.utxo is None and not Transaction.is_segwit_input(txin): if txin.utxo is None and not txin.is_segwit():
raise UserFacingException(_('Missing previous tx for legacy input.')) raise UserFacingException(_('Missing previous tx for legacy input.'))
prev_tx[tx_hash] = txin.utxo prev_tx[tx_hash] = txin.utxo

50
electrum/transaction.py

@ -243,6 +243,11 @@ class TxInput:
n = vds.read_compact_size() n = vds.read_compact_size()
return list(vds.read_bytes(vds.read_compact_size()) for i in range(n)) return list(vds.read_bytes(vds.read_compact_size()) for i in range(n))
def is_segwit(self, *, guess_for_address=False) -> bool:
if self.witness not in (b'\x00', b'', None):
return True
return False
class BCDataStream(object): class BCDataStream(object):
"""Workalike python implementation of Bitcoin's CDataStream class.""" """Workalike python implementation of Bitcoin's CDataStream class."""
@ -635,7 +640,7 @@ class Transaction:
assert isinstance(txin, PartialTxInput) assert isinstance(txin, PartialTxInput)
_type = txin.script_type _type = txin.script_type
if not cls.is_segwit_input(txin): if not txin.is_segwit():
return construct_witness([]) return construct_witness([])
if _type in ('address', 'unknown') and estimate_size: if _type in ('address', 'unknown') and estimate_size:
@ -650,23 +655,6 @@ class Transaction:
return construct_witness([]) return construct_witness([])
raise UnknownTxinType(f'cannot construct witness for txin_type: {_type}') raise UnknownTxinType(f'cannot construct witness for txin_type: {_type}')
@classmethod
def is_segwit_input(cls, txin: 'TxInput', *, guess_for_address=False) -> bool:
if txin.witness not in (b'\x00', b'', None):
return True
if not isinstance(txin, PartialTxInput):
return False
if txin.is_native_segwit() or txin.is_p2sh_segwit():
return True
if txin.is_native_segwit() is False and txin.is_p2sh_segwit() is False:
return False
if txin.witness_script:
return True
_type = txin.script_type
if _type == 'address' and guess_for_address:
_type = cls.guess_txintype_from_address(txin.address)
return is_segwit_script_type(_type)
@classmethod @classmethod
def guess_txintype_from_address(cls, addr: Optional[str]) -> str: def guess_txintype_from_address(cls, addr: Optional[str]) -> str:
# It's not possible to tell the script type in general # It's not possible to tell the script type in general
@ -771,7 +759,7 @@ class Transaction:
hashOutputs=hashOutputs) hashOutputs=hashOutputs)
def is_segwit(self, *, guess_for_address=False): def is_segwit(self, *, guess_for_address=False):
return any(self.is_segwit_input(txin, guess_for_address=guess_for_address) return any(txin.is_segwit(guess_for_address=guess_for_address)
for txin in self.inputs()) for txin in self.inputs())
def invalidate_ser_cache(self): def invalidate_ser_cache(self):
@ -829,7 +817,7 @@ class Transaction:
def txid(self) -> Optional[str]: def txid(self) -> Optional[str]:
if self._cached_txid is None: if self._cached_txid is None:
self.deserialize() self.deserialize()
all_segwit = all(self.is_segwit_input(x) for x in self.inputs()) all_segwit = all(txin.is_segwit() for txin in self.inputs())
if not all_segwit and not self.is_complete(): if not all_segwit and not self.is_complete():
return None return None
try: try:
@ -873,7 +861,7 @@ class Transaction:
script = cls.input_script(txin, estimate_size=True) script = cls.input_script(txin, estimate_size=True)
input_size = len(cls.serialize_input(txin, script)) // 2 input_size = len(cls.serialize_input(txin, script)) // 2
if cls.is_segwit_input(txin, guess_for_address=True): if txin.is_segwit(guess_for_address=True):
witness_size = len(cls.serialize_witness(txin, estimate_size=True)) // 2 witness_size = len(cls.serialize_witness(txin, estimate_size=True)) // 2
else: else:
witness_size = 1 if is_segwit_tx else 0 witness_size = 1 if is_segwit_tx else 0
@ -1200,7 +1188,7 @@ class PartialTxInput(TxInput, PSBTSection):
# without verifying the input amount. This means, given a maliciously modified PSBT, # without verifying the input amount. This means, given a maliciously modified PSBT,
# for non-segwit inputs, we might end up burning coins as miner fees. # for non-segwit inputs, we might end up burning coins as miner fees.
if for_signing and False: if for_signing and False:
if not Transaction.is_segwit_input(self) and self.witness_utxo: if not self.is_segwit() and self.witness_utxo:
raise PSBTInputConsistencyFailure(f"PSBT input validation: " raise PSBTInputConsistencyFailure(f"PSBT input validation: "
f"If a witness UTXO is provided, no non-witness signature may be created") f"If a witness UTXO is provided, no non-witness signature may be created")
if self.redeem_script and self.address: if self.redeem_script and self.address:
@ -1340,7 +1328,7 @@ class PartialTxInput(TxInput, PSBTSection):
return True return True
if self.is_coinbase_input(): if self.is_coinbase_input():
return True return True
if self.script_sig is not None and not Transaction.is_segwit_input(self): if self.script_sig is not None and not self.is_segwit():
return True return True
signatures = list(self.part_sigs.values()) signatures = list(self.part_sigs.values())
s = len(signatures) s = len(signatures)
@ -1442,6 +1430,20 @@ class PartialTxInput(TxInput, PSBTSection):
self._is_p2sh_segwit = calc_if_p2sh_segwit_now() self._is_p2sh_segwit = calc_if_p2sh_segwit_now()
return self._is_p2sh_segwit return self._is_p2sh_segwit
def is_segwit(self, *, guess_for_address=False) -> bool:
if super().is_segwit():
return True
if self.is_native_segwit() or self.is_p2sh_segwit():
return True
if self.is_native_segwit() is False and self.is_p2sh_segwit() is False:
return False
if self.witness_script:
return True
_type = self.script_type
if _type == 'address' and guess_for_address:
_type = Transaction.guess_txintype_from_address(self.address)
return is_segwit_script_type(_type)
def already_has_some_signatures(self) -> bool: def already_has_some_signatures(self) -> bool:
"""Returns whether progress has been made towards completing this input.""" """Returns whether progress has been made towards completing this input."""
return (self.part_sigs return (self.part_sigs
@ -1790,7 +1792,7 @@ class PartialTransaction(Transaction):
raise Exception("only SIGHASH_ALL signing is supported!") raise Exception("only SIGHASH_ALL signing is 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 self.is_segwit_input(txin): if txin.is_segwit():
if bip143_shared_txdigest_fields is None: if bip143_shared_txdigest_fields is None:
bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields() bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields()
hashPrevouts = bip143_shared_txdigest_fields.hashPrevouts hashPrevouts = bip143_shared_txdigest_fields.hashPrevouts

4
electrum/wallet.py

@ -2162,7 +2162,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
if all([txin.utxo for txin in tx.inputs()]): if all([txin.utxo for txin in tx.inputs()]):
return None return None
# a single segwit input -> fine # a single segwit input -> fine
if len(tx.inputs()) == 1 and Transaction.is_segwit_input(tx.inputs()[0]) and tx.inputs()[0].witness_utxo: if len(tx.inputs()) == 1 and tx.inputs()[0].is_segwit() and tx.inputs()[0].witness_utxo:
return None return None
# coinjoin or similar # coinjoin or similar
if any([not self.is_mine(txin.address) for txin in tx.inputs()]): if any([not self.is_mine(txin.address) for txin in tx.inputs()]):
@ -2170,7 +2170,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
+ _("The input amounts could not be verified as the previous transactions are missing.\n" + _("The input amounts could not be verified as the previous transactions are missing.\n"
"The amount of money being spent CANNOT be verified.")) "The amount of money being spent CANNOT be verified."))
# some inputs are legacy # some inputs are legacy
if any([not Transaction.is_segwit_input(txin) for txin in tx.inputs()]): if any([not txin.is_segwit() for txin in tx.inputs()]):
return (_("Warning") + ": " return (_("Warning") + ": "
+ _("The fee could not be verified. Signing non-segwit inputs is risky:\n" + _("The fee could not be verified. Signing non-segwit inputs is risky:\n"
"if this transaction was maliciously modified before you sign,\n" "if this transaction was maliciously modified before you sign,\n"

Loading…
Cancel
Save