URI: 
       ttransaction.py: shortcut witness/scriptSig serialisation - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 1d6f000868caa02607129a99cad7bed8ec3f28df
   DIR parent b3110b3b468e90cd6bcc3dfc5e83d7c991dc23fa
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Wed, 18 Apr 2018 19:00:14 +0200
       
       ttransaction.py: shortcut witness/scriptSig serialisation
       
       Diffstat:
         M lib/bitcoin.py                      |       7 +++++++
         M lib/transaction.py                  |      67 +++++++++++++++++++------------
         M plugins/digitalbitbox/digitalbitbo… |       3 ++-
         M plugins/ledger/ledger.py            |       2 +-
       
       4 files changed, 51 insertions(+), 28 deletions(-)
       ---
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -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)
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -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))
       t@@ -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
       t@@ -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):
       t@@ -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
       t@@ -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]]
       t@@ -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
       t@@ -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 = ''
       t@@ -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
       t@@ -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':
       t@@ -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
   DIR diff --git a/plugins/digitalbitbox/digitalbitbox.py b/plugins/digitalbitbox/digitalbitbox.py
       t@@ -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
   DIR diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py
       t@@ -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