示例#1
0
 def sign_tx(self, tx):
     """Sign tx that has been created by MiniWallet in P2PK mode"""
     assert self._priv_key is not None
     (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx,
                                          0, SIGHASH_ALL)
     assert err is None
     tx.vin[0].scriptSig = CScript([
         self._priv_key.sign_ecdsa(sighash) +
         bytes(bytearray([SIGHASH_ALL]))
     ])
示例#2
0
 def sign_tx(self, tx, fixed_length=True):
     """Sign tx that has been created by MiniWallet in P2PK mode"""
     assert self._priv_key is not None
     (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
     assert err is None
     # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
     # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes)
     # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes
     der_sig = b''
     while not len(der_sig) == 71:
         der_sig = self._priv_key.sign_ecdsa(sighash)
         if not fixed_length:
             break
     tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
示例#3
0
    def prepare_tx_signed_with_sighash(self, address_type,
                                       sighash_rangeproof_aware,
                                       attach_issuance):
        # Create a tx that is signed with a specific version of the sighash
        # method.
        # If `sighash_rangeproof_aware` is
        # true, the sighash will contain the rangeproofs if SIGHASH_RANGEPROOF is set
        # false, the sighash will NOT contain the rangeproofs if SIGHASH_RANGEPROOF is set

        addr = self.nodes[1].getnewaddress("", address_type)
        assert len(self.nodes[1].getaddressinfo(addr)["confidential_key"]) > 0
        self.nodes[0].sendtoaddress(addr, 1.0)
        self.nodes[0].generate(1)
        self.sync_all()
        utxo = self.nodes[1].listunspent(1, 1, [addr])[0]
        utxo_tx = FromHex(CTransaction(),
                          self.nodes[1].getrawtransaction(utxo["txid"]))
        utxo_spk = CScript(hex_str_to_bytes(utxo["scriptPubKey"]))
        utxo_value = utxo_tx.vout[utxo["vout"]].nValue

        assert len(utxo["amountblinder"]) > 0
        sink_addr = self.nodes[2].getnewaddress()
        unsigned_hex = self.nodes[1].createrawtransaction([{
            "txid": utxo["txid"],
            "vout": utxo["vout"]
        }], [{
            sink_addr: 0.9
        }, {
            "fee": 0.1
        }])
        if attach_issuance:
            # Attach a blinded issuance
            unsigned_hex = self.nodes[1].rawissueasset(
                unsigned_hex,
                [{
                    "asset_amount": 100,
                    "asset_address": self.nodes[1].getnewaddress(),
                    "token_amount": 100,
                    "token_address": self.nodes[1].getnewaddress(),
                    "blind":
                    True,  # FIXME: if blind=False, `blindrawtranaction` fails. Should fix this in a future PR
                }])[0]["hex"]
        blinded_hex = self.nodes[1].blindrawtransaction(unsigned_hex)
        blinded_tx = FromHex(CTransaction(), blinded_hex)
        signed_hex = self.nodes[1].signrawtransactionwithwallet(
            blinded_hex)["hex"]
        signed_tx = FromHex(CTransaction(), signed_hex)

        # Make sure that the tx the node produced is always valid.
        test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
        assert test_accept["allowed"], "not accepted: {}".format(
            test_accept["reject-reason"])

        # Prepare the keypair we need to re-sign the tx.
        wif = self.nodes[1].dumpprivkey(addr)
        (b, v) = base58_to_byte(wif)
        privkey = ECKey()
        privkey.set(b[0:32], len(b) == 33)
        pubkey = privkey.get_pubkey()

        # Now we need to replace the signature with an equivalent one with the new sighash set,
        # which we do using the Python logic to detect any forking changes in the sighash format.
        hashtype = SIGHASH_ALL | SIGHASH_RANGEPROOF
        if address_type == "legacy":
            if sighash_rangeproof_aware:
                (sighash, _) = LegacySignatureHash(utxo_spk, blinded_tx, 0,
                                                   hashtype)
            else:
                (sighash,
                 _) = LegacySignatureHash(utxo_spk,
                                          blinded_tx,
                                          0,
                                          hashtype,
                                          enable_sighash_rangeproof=False)
            signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode(
                'latin-1')
            assert len(signature) <= 0xfc
            assert len(pubkey.get_bytes()) <= 0xfc
            signed_tx.vin[0].scriptSig = CScript(
                struct.pack("<B", len(signature)) + signature +
                struct.pack("<B", len(pubkey.get_bytes())) +
                pubkey.get_bytes())
        elif address_type == "bech32" or address_type == "p2sh-segwit":
            assert signed_tx.wit.vtxinwit[0].scriptWitness.stack[
                1] == pubkey.get_bytes()
            pubkeyhash = hash160(pubkey.get_bytes())
            script = get_p2pkh_script(pubkeyhash)
            if sighash_rangeproof_aware:
                sighash = SegwitV0SignatureHash(script, blinded_tx, 0,
                                                hashtype, utxo_value)
            else:
                sighash = SegwitV0SignatureHash(
                    script,
                    blinded_tx,
                    0,
                    hashtype,
                    utxo_value,
                    enable_sighash_rangeproof=False)
            signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode(
                'latin-1')
            signed_tx.wit.vtxinwit[0].scriptWitness.stack[0] = signature
        else:
            assert False

        # Make sure that the tx we manually signed is valid
        signed_hex = signed_tx.serialize_with_witness().hex()
        test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
        if sighash_rangeproof_aware:
            assert test_accept["allowed"], "not accepted: {}".format(
                test_accept["reject-reason"])
        else:
            assert not test_accept["allowed"], "tx was accepted"

        if sighash_rangeproof_aware:
            signed_hex = self.nodes[1].signrawtransactionwithwallet(
                blinded_hex, [], "ALL|RANGEPROOF")["hex"]
            signed_tx = FromHex(CTransaction(), signed_hex)

            # Make sure that the tx that the node signed is valid
            test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
            assert test_accept["allowed"], "not accepted: {}".format(
                test_accept["reject-reason"])

            # Try re-signing with node 0, which should have no effect since the transaction was already complete
            signed_hex = self.nodes[0].signrawtransactionwithwallet(
                signed_hex)["hex"]
            test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
            assert test_accept["allowed"], "not accepted: {}".format(
                test_accept["reject-reason"])

            # Try signing using the PSBT interface
            psbt_hex = self.nodes[0].converttopsbt(unsigned_hex)
            signed_psbt = self.nodes[1].walletprocesspsbt(
                psbt_hex, True, "ALL|RANGEPROOF")
            extracted_tx = self.nodes[0].finalizepsbt(signed_psbt["psbt"])
            assert extracted_tx["complete"]
            test_accept = self.nodes[0].testmempoolaccept(
                [extracted_tx["hex"]])[0]
            assert test_accept["allowed"], "not accepted: {}".format(
                test_accept["reject-reason"])
        else:
            signed_tx.rehash()

        return signed_tx
示例#4
0
    def prepare_tx_signed_with_sighash(self, address_type,
                                       sighash_rangeproof_aware):
        # Create a tx that is signed with a specific version of the sighash
        # method.
        # If `sighash_rangeproof_aware` is
        # true, the sighash will contain the rangeproofs if SIGHASH_RANGEPROOF is set
        # false, the sighash will NOT contain the rangeproofs if SIGHASH_RANGEPROOF is set

        addr = self.nodes[1].getnewaddress("", address_type)
        assert len(self.nodes[1].getaddressinfo(addr)["confidential_key"]) > 0
        self.nodes[0].sendtoaddress(addr, 1.0)
        self.nodes[0].generate(1)
        self.sync_all()
        utxo = self.nodes[1].listunspent(1, 1, [addr])[0]
        utxo_tx = FromHex(CTransaction(),
                          self.nodes[1].getrawtransaction(utxo["txid"]))
        utxo_spk = CScript(hex_str_to_bytes(utxo["scriptPubKey"]))
        utxo_value = utxo_tx.vout[utxo["vout"]].nValue

        assert len(utxo["amountblinder"]) > 0
        sink_addr = self.nodes[2].getnewaddress()
        unsigned_hex = self.nodes[1].createrawtransaction([{
            "txid": utxo["txid"],
            "vout": utxo["vout"]
        }], {
            sink_addr: 0.9,
            "fee": 0.1
        })
        blinded_hex = self.nodes[1].blindrawtransaction(unsigned_hex)
        blinded_tx = FromHex(CTransaction(), blinded_hex)
        signed_hex = self.nodes[1].signrawtransactionwithwallet(
            blinded_hex)["hex"]
        signed_tx = FromHex(CTransaction(), signed_hex)

        # Make sure that the tx the node produced is always valid.
        test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0]
        assert test_accept["allowed"], "not accepted: {}".format(
            test_accept["reject-reason"])

        # Prepare the keypair we need to re-sign the tx.
        wif = self.nodes[1].dumpprivkey(addr)
        (b, v) = base58_to_byte(wif)
        privkey = ECKey()
        privkey.set(b[0:32], len(b) == 33)
        pubkey = privkey.get_pubkey()

        # Now we need to replace the signature with an equivalent one with the new sighash set.
        hashtype = SIGHASH_ALL | SIGHASH_RANGEPROOF
        if address_type == "legacy":
            if sighash_rangeproof_aware:
                (sighash, _) = LegacySignatureHash(utxo_spk, blinded_tx, 0,
                                                   hashtype)
            else:
                (sighash,
                 _) = LegacySignatureHash(utxo_spk,
                                          blinded_tx,
                                          0,
                                          hashtype,
                                          enable_sighash_rangeproof=False)
            signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode(
                'latin-1')
            assert len(signature) <= 0xfc
            assert len(pubkey.get_bytes()) <= 0xfc
            signed_tx.vin[0].scriptSig = CScript(
                struct.pack("<B", len(signature)) + signature +
                struct.pack("<B", len(pubkey.get_bytes())) +
                pubkey.get_bytes())
        elif address_type == "bech32" or address_type == "p2sh-segwit":
            assert signed_tx.wit.vtxinwit[0].scriptWitness.stack[
                1] == pubkey.get_bytes()
            pubkeyhash = hash160(pubkey.get_bytes())
            script = get_p2pkh_script(pubkeyhash)
            if sighash_rangeproof_aware:
                sighash = SegwitV0SignatureHash(script, blinded_tx, 0,
                                                hashtype, utxo_value)
            else:
                sighash = SegwitV0SignatureHash(
                    script,
                    blinded_tx,
                    0,
                    hashtype,
                    utxo_value,
                    enable_sighash_rangeproof=False)
            signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode(
                'latin-1')
            signed_tx.wit.vtxinwit[0].scriptWitness.stack[0] = signature
        else:
            assert False

        signed_tx.rehash()
        return signed_tx