tMerge pull request #5494 from SomberNight/tx_signing_perf_20190708 - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 261c492c37d45193b808b9c91c6b3e5549dc9c42 DIR parent a14016275b028bb53e9a2151bb27bdb84d063d07 HTML Author: ThomasV <thomasv@electrum.org> Date: Tue, 9 Jul 2019 17:41:56 +0200 Merge pull request #5494 from SomberNight/tx_signing_perf_20190708 ttransaction: segwit input signing was doing quadratic hashing Diffstat: M electrum/transaction.py | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) --- DIR diff --git a/electrum/transaction.py b/electrum/transaction.py t@@ -86,6 +86,12 @@ class TxOutputHwInfo(NamedTuple): script_type: str +class BIP143SharedTxDigestFields(NamedTuple): + hashPrevouts: str + hashSequence: str + hashOutputs: str + + class BCDataStream(object): """Workalike python implementation of Bitcoin's CDataStream class.""" t@@ -958,7 +964,18 @@ class Transaction: s += script return s - def serialize_preimage(self, txin_index: int) -> str: + def _calc_bip143_shared_txdigest_fields(self) -> BIP143SharedTxDigestFields: + inputs = self.inputs() + outputs = self.outputs() + hashPrevouts = bh2u(sha256d(bfh(''.join(self.serialize_outpoint(txin) for txin in inputs)))) + hashSequence = bh2u(sha256d(bfh(''.join(int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) for txin in inputs)))) + hashOutputs = bh2u(sha256d(bfh(''.join(self.serialize_output(o) for o in outputs)))) + return BIP143SharedTxDigestFields(hashPrevouts=hashPrevouts, + hashSequence=hashSequence, + hashOutputs=hashOutputs) + + def serialize_preimage(self, txin_index: int, *, + bip143_shared_txdigest_fields: BIP143SharedTxDigestFields = None) -> str: nVersion = int_to_hex(self.version, 4) nHashType = int_to_hex(1, 4) # SIGHASH_ALL nLocktime = int_to_hex(self.locktime, 4) t@@ -966,9 +983,11 @@ class Transaction: outputs = self.outputs() txin = inputs[txin_index] if self.is_segwit_input(txin): - hashPrevouts = bh2u(sha256d(bfh(''.join(self.serialize_outpoint(txin) for txin in inputs)))) - hashSequence = bh2u(sha256d(bfh(''.join(int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) for txin in inputs)))) - hashOutputs = bh2u(sha256d(bfh(''.join(self.serialize_output(o) for o in outputs)))) + if bip143_shared_txdigest_fields is None: + bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields() + hashPrevouts = bip143_shared_txdigest_fields.hashPrevouts + hashSequence = bip143_shared_txdigest_fields.hashSequence + hashOutputs = bip143_shared_txdigest_fields.hashOutputs outpoint = self.serialize_outpoint(txin) preimage_script = self.get_preimage_script(txin) scriptCode = var_int(len(preimage_script) // 2) + preimage_script t@@ -1129,6 +1148,7 @@ class Transaction: def sign(self, keypairs) -> None: # keypairs: (x_)pubkey -> secret_bytes + bip143_shared_txdigest_fields = self._calc_bip143_shared_txdigest_fields() for i, txin in enumerate(self.inputs()): pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) for j, (pubkey, x_pubkey) in enumerate(zip(pubkeys, x_pubkeys)): t@@ -1142,14 +1162,15 @@ class Transaction: continue _logger.info(f"adding signature for {_pubkey}") sec, compressed = keypairs.get(_pubkey) - sig = self.sign_txin(i, sec) + sig = self.sign_txin(i, sec, bip143_shared_txdigest_fields=bip143_shared_txdigest_fields) self.add_signature_to_txin(i, j, sig) _logger.info(f"is_complete {self.is_complete()}") self.raw = self.serialize() - def sign_txin(self, txin_index, privkey_bytes) -> str: - pre_hash = sha256d(bfh(self.serialize_preimage(txin_index))) + def sign_txin(self, txin_index, privkey_bytes, *, bip143_shared_txdigest_fields=None) -> str: + 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) + '01'