electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 1d6f000868caa02607129a99cad7bed8ec3f28df
parent b3110b3b468e90cd6bcc3dfc5e83d7c991dc23fa
Author: SomberNight <somber.night@protonmail.com>
Date:   Wed, 18 Apr 2018 19:00:14 +0200

transaction.py: shortcut witness/scriptSig serialisation

Diffstat:
Mlib/bitcoin.py | 7+++++++
Mlib/transaction.py | 67+++++++++++++++++++++++++++++++++++++++++--------------------------
Mplugins/digitalbitbox/digitalbitbox.py | 3++-
Mplugins/ledger/ledger.py | 2+-
4 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -188,6 +188,13 @@ def var_int(i): return "ff"+int_to_hex(i,8) +def witness_push(item): + """ Returns data in the form it should be present in the witness. + hex -> hex + """ + return var_int(len(item) // 2) + item + + def op_push(i): if i<0x4c: # OP_PUSHDATA1 return int_to_hex(i) diff --git a/lib/transaction.py b/lib/transaction.py @@ -359,8 +359,9 @@ def parse_scriptSig(d, _bytes): match = [ opcodes.OP_0 ] + [ opcodes.OP_PUSHDATA4 ] * (len(decoded) - 1) if match_decoded(decoded, match): x_sig = [bh2u(x[1]) for x in decoded[1:-1]] + redeem_script = bh2u(decoded[-1][1]) try: - m, n, x_pubkeys, pubkeys, redeemScript = parse_redeemScript(decoded[-1][1]) + m, n, x_pubkeys, pubkeys = parse_redeemScript_multisig(bfh(redeem_script)) except NotRecognizedRedeemScript: print_error("parse_scriptSig: cannot find address in input script (p2sh?)", bh2u(_bytes)) @@ -373,16 +374,16 @@ def parse_scriptSig(d, _bytes): d['signatures'] = parse_sig(x_sig) d['x_pubkeys'] = x_pubkeys d['pubkeys'] = pubkeys - d['redeemScript'] = redeemScript - d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript))) + d['redeem_script'] = redeem_script + d['address'] = hash160_to_p2sh(hash_160(bfh(redeem_script))) return print_error("parse_scriptSig: cannot find address in input script (unknown)", bh2u(_bytes)) -def parse_redeemScript(s): - dec2 = [ x for x in script_GetOp(s) ] +def parse_redeemScript_multisig(redeem_script): + dec2 = [ x for x in script_GetOp(redeem_script) ] try: m = dec2[0][0] - opcodes.OP_1 + 1 n = dec2[-2][0] - opcodes.OP_1 + 1 @@ -395,8 +396,10 @@ def parse_redeemScript(s): raise NotRecognizedRedeemScript() x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]] pubkeys = [safe_parse_pubkey(x) for x in x_pubkeys] - redeemScript = multisig_script(pubkeys, m) - return m, n, x_pubkeys, pubkeys, redeemScript + redeem_script2 = bfh(multisig_script(pubkeys, m)) + if redeem_script2 != redeem_script: + raise NotRecognizedRedeemScript() + return m, n, x_pubkeys, pubkeys def get_address_from_output_script(_bytes, *, net=None): @@ -471,8 +474,7 @@ def parse_witness(vds, txin): # now 'n' is the number of items in the witness w = list(bh2u(vds.read_bytes(vds.read_compact_size())) for i in range(n)) - add_w = lambda x: var_int(len(x) // 2) + x - txin['witness'] = var_int(n) + ''.join(add_w(i) for i in w) + txin['witness'] = var_int(n) + ''.join(witness_push(i) for i in w) # FIXME: witness version > 0 will probably fail here. # For native segwit, we would need the scriptPubKey of the parent txn @@ -487,18 +489,19 @@ def parse_witness(vds, txin): if txin['type'] == 'coinbase': pass elif txin['type'] == 'p2wsh-p2sh' or n > 2: + witness_script = w[-1] try: - m, n, x_pubkeys, pubkeys, witnessScript = parse_redeemScript(bfh(w[-1])) + m, n, x_pubkeys, pubkeys = parse_redeemScript_multisig(bfh(witness_script)) except NotRecognizedRedeemScript: raise UnknownTxinType() txin['signatures'] = parse_sig(w[1:-1]) txin['num_sig'] = m txin['x_pubkeys'] = x_pubkeys txin['pubkeys'] = pubkeys - txin['witnessScript'] = witnessScript + txin['witness_script'] = witness_script if not txin.get('scriptSig'): # native segwit script txin['type'] = 'p2wsh' - txin['address'] = bitcoin.script_to_p2wsh(txin['witnessScript']) + txin['address'] = bitcoin.script_to_p2wsh(witness_script) elif txin['type'] == 'p2wpkh-p2sh' or n == 2: txin['num_sig'] = 1 txin['x_pubkeys'] = [w[1]] @@ -641,12 +644,18 @@ class Transaction: public_key.verify_digest(sig_string, pre_hash, sigdecode = ecdsa.util.sigdecode_string) j = pubkeys.index(pubkey) print_error("adding sig", i, j, pubkey, sig) - self._inputs[i]['signatures'][j] = sig + self.add_signature_to_txin(self._inputs[i], j, sig) #self._inputs[i]['x_pubkeys'][j] = pubkey break # redo raw self.raw = self.serialize() + @classmethod + def add_signature_to_txin(cls, txin, signingPos, sig): + txin['signatures'][signingPos] = sig + txin['scriptSig'] = None # force re-serialization + txin['witness'] = None # force re-serialization + def deserialize(self): if self.raw is None: return @@ -736,17 +745,17 @@ class Transaction: return '00' if txin['type'] == 'coinbase': return txin['witness'] - pubkeys, sig_list = self.get_siglist(txin, estimate_size) - add_w = lambda x: var_int(len(x) // 2) + x - if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']: - witness = var_int(2) + add_w(sig_list[0]) + add_w(pubkeys[0]) - elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']: - n = len(sig_list) + 2 - witness_script = multisig_script(pubkeys, txin['num_sig']) - witness = var_int(n) + '00' + ''.join(add_w(x) for x in sig_list) + add_w(witness_script) - else: - witness = txin.get('witness', None) - if not witness: + + witness = txin.get('witness', None) + if witness is None: + pubkeys, sig_list = self.get_siglist(txin, estimate_size) + if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']: + witness = var_int(2) + witness_push(sig_list[0]) + witness_push(pubkeys[0]) + elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']: + n = len(sig_list) + 2 + witness_script = multisig_script(pubkeys, txin['num_sig']) + witness = var_int(n) + '00' + ''.join(witness_push(x) for x in sig_list) + witness_push(witness_script) + else: raise Exception('wrong txin type:', txin['type']) if self.is_txin_complete(txin) or estimate_size: value_field = '' @@ -756,7 +765,7 @@ class Transaction: @classmethod def is_segwit_input(cls, txin): - has_nonzero_witness = txin.get('witness', '00') != '00' + has_nonzero_witness = txin.get('witness', '00') not in ('00', None) return cls.is_segwit_inputtype(txin['type']) or has_nonzero_witness @classmethod @@ -768,6 +777,11 @@ class Transaction: _type = txin['type'] if _type == 'coinbase': return txin['scriptSig'] + + script_sig = txin.get('scriptSig', None) + if script_sig is not None: + return script_sig + pubkeys, sig_list = self.get_siglist(txin, estimate_size) script = ''.join(push_script(x) for x in sig_list) if _type == 'p2pk': @@ -1035,7 +1049,8 @@ class Transaction: sig = private_key.sign_digest_deterministic(pre_hash, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der) if not public_key.verify_digest(sig, pre_hash, sigdecode = ecdsa.util.sigdecode_der): raise Exception('Sanity check verifying our own signature failed.') - txin['signatures'][j] = bh2u(sig) + '01' + sig = bh2u(sig) + '01' + self.add_signature_to_txin(txin, j, sig) #txin['x_pubkeys'][j] = pubkey txin['pubkeys'][j] = pubkey # needed for fd keys self._inputs[i] = txin diff --git a/plugins/digitalbitbox/digitalbitbox.py b/plugins/digitalbitbox/digitalbitbox.py @@ -643,7 +643,8 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) - txin['signatures'][ii] = to_hexstr(sig) + '01' + sig = to_hexstr(sig) + '01' + Transaction.add_signature_to_txin(txin, ii, sig) tx._inputs[i] = txin except UserCancelled: raise diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py @@ -498,7 +498,7 @@ class Ledger_KeyStore(Hardware_KeyStore): for i, txin in enumerate(tx.inputs()): signingPos = inputs[i][4] - txin['signatures'][signingPos] = bh2u(signatures[i]) + Transaction.add_signature_to_txin(txin, signingPos, bh2u(signatures[i])) tx.raw = tx.serialize() @test_pin_unlocked