예제 #1
0
    def test_immutable_tx_creation_with_mutable_parts_specified(self):
        tx = CTransaction(
            vin=[CMutableTxIn(prevout=COutPoint(hash=b'a' * 32, n=0))],
            vout=[CMutableTxOut(nValue=1)],
            witness=CMutableTxWitness(
                [CMutableTxInWitness(CScriptWitness([CScript([0])]))]))

        def check_immutable_parts(tx):
            self.assertTrue(tx.vin[0].is_immutable())
            self.assertTrue(tx.vin[0].is_immutable())
            self.assertTrue(tx.vout[0].is_immutable())
            self.assertTrue(tx.wit.is_immutable())
            self.assertTrue(tx.wit.vtxinwit[0].is_immutable())

        check_immutable_parts(tx)

        # Test that if we deserialize with CTransaction,
        # all the parts are immutable
        tx = CTransaction.deserialize(tx.serialize())
        check_immutable_parts(tx)

        # Test some parts separately, because when created via
        # CMutableTransaction instantiation, they are created with from_*
        # methods, and not directly

        txin = CTxIn(prevout=CMutableOutPoint(hash=b'a' * 32, n=0))
        self.assertTrue(txin.prevout.is_immutable())

        wit = CTxWitness((CMutableTxInWitness(), ))
        self.assertTrue(wit.vtxinwit[0].is_immutable())
    def check_sign(self, blinded_tx: CTransaction, signed_tx: CTransaction,
                   bundle: Dict[str, Any]) -> None:
        tx_to_sign = blinded_tx.to_mutable()
        for n, vin in enumerate(tx_to_sign.vin):
            utxo = bundle['vin_utxo'][n]
            amount = -1 if utxo['amount'] == -1 else coins_to_satoshi(
                utxo['amount'])

            scriptPubKey = CScript(x(utxo['scriptPubKey']))
            a = CCoinAddress(utxo['address'])
            if 'privkey' in utxo:
                privkey = CCoinKey(utxo['privkey'])
                assert isinstance(a, P2PKHCoinAddress),\
                    "only P2PKH is supported for single-sig"
                assert a == P2PKHElementsAddress.from_pubkey(privkey.pub)
                assert scriptPubKey == a.to_scriptPubKey()
                sighash = SignatureHash(scriptPubKey,
                                        tx_to_sign,
                                        n,
                                        SIGHASH_ALL,
                                        amount=amount,
                                        sigversion=SIGVERSION_BASE)
                sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
                tx_to_sign.vin[n].scriptSig = CScript(
                    [CScript(sig), CScript(privkey.pub)])
            else:
                pk_list = [CCoinKey(pk) for pk in utxo['privkey_list']]
                redeem_script_data = [utxo['num_p2sh_participants']]
                redeem_script_data.extend([pk.pub for pk in pk_list])
                redeem_script_data.extend([len(pk_list), OP_CHECKMULTISIG])
                redeem_script = CScript(redeem_script_data)
                assert isinstance(a, P2SHCoinAddress),\
                    "only P2SH is supported for multi-sig."
                assert scriptPubKey == redeem_script.to_p2sh_scriptPubKey()
                assert a == P2SHElementsAddress.from_scriptPubKey(
                    redeem_script.to_p2sh_scriptPubKey())
                sighash = SignatureHash(redeem_script,
                                        tx_to_sign,
                                        n,
                                        SIGHASH_ALL,
                                        amount=amount,
                                        sigversion=SIGVERSION_BASE)
                sigs = [
                    pk.sign(sighash) + bytes([SIGHASH_ALL]) for pk in pk_list
                ]
                tx_to_sign.vin[n].scriptSig = CScript([b''] + sigs +
                                                      [redeem_script])

            VerifyScript(tx_to_sign.vin[n].scriptSig,
                         scriptPubKey,
                         tx_to_sign,
                         n,
                         amount=amount)

        self.assertEqual(tx_to_sign.serialize(), signed_tx.serialize())
예제 #3
0
def ConsensusVerifyScript(
        scriptSig: CScript,
        scriptPubKey: CScript,
        txTo: CTransaction,
        inIdx: int,
        flags: Union[Tuple[ScriptVerifyFlag_Type, ...],
                     Set[ScriptVerifyFlag_Type]] = (),
        amount: int = 0,
        witness: Optional[CScriptWitness] = None,
        consensus_library_hanlde: Optional[ctypes.CDLL] = None) -> None:
    """Verify a scriptSig satisfies a scriptPubKey, via libbitcoinconsensus
    `bitcoinconsensus_verify_script_with_amount()` function.

    The arguments are compatible with `VerifyScript()` from
    `bitcointx.core.scripteval`

    scriptSig    - Signature. Must be present in the transaction input at
                   inIdx. Redundant, but is there for compatibility of
                   arguments with VerifyScript() that allow to supply
                   different scriptSig than the one in the input
    scriptPubKey - PubKey
    txTo         - Spending transaction
    inIdx        - Index of the transaction input containing scriptSig
    flags        - Script execution flags (flags defined in
                   `bitcointx.core.scripteval`). Only a subset of flags
                   are allowed (see BITCOINCONSENSUS_ACCEPTED_FLAGS in
                   `bitcointx.core.bitcoinconsensus`)
    amount       - amount of the input
    witness      - CScriptWitness() for the corresponding input.
                   If None, the witness will be taken from the transaction.
                   If not None, the witness in the transaction must be empty,
                   or the same as supplied value.
    consensus_library_hanlde - if supplied, the function
                   `bitcoinconsensus_verify_script_with_amount()` will be
                   called via this handle. If not, default libbitcoinconsensus
                   handle will be used, and the attempt to load the library
                   will be performed on first use.

    Raises a ValidationError subclass if the validation fails.
    May rise ValueError or TypeError if supplied arguments are incorrect.
    May rise RuntimeError if there's some problems with interfaceing with
    the library
    """
    global _libbitcoin_consensus

    if not MoneyRange(amount):
        raise ValueError('amount out of MoneyRange')

    ensure_isinstance(scriptSig, CScript, 'scriptSig')
    if not type(scriptSig) == type(scriptPubKey):
        raise TypeError(
            "scriptSig and scriptPubKey must be of the same script class")

    if txTo.vin[inIdx].scriptSig != scriptSig:
        raise ValueError(
            f'supplied scriptSig is not present in input {inIdx} of '
            f'the supplied transaction')

    if witness is not None:
        ensure_isinstance(witness, CScriptWitness, 'witness')
        if not txTo.wit.vtxinwit[inIdx].scriptWitness.is_null() \
                and txTo.wit.vtxinwit[inIdx].scriptWitness != witness:
            raise ValueError(
                'transaction has witness for input {}, '
                'but it is different from what is supplied as witness kwarg'.
                format(inIdx))
        txTo = txTo.to_mutable()
        txTo.wit.vtxinwit[inIdx].scriptWitness = witness

    handle = consensus_library_hanlde

    if handle is None:
        if _libbitcoin_consensus is None:
            _libbitcoin_consensus = load_bitcoinconsensus_library()
        handle = _libbitcoin_consensus

    tx_data = txTo.serialize()

    libconsensus_flags = _flags_to_libconsensus(flags)

    # bitcoinconsensus_error type is enum, as declared in the C header.
    # enum can be of any size, as chosen by the compiler.
    # most likely it will be of the size of u_int, but there's no guarantee.
    # While teoretically possible, enum is very unlikely to be larger than
    # size of u_int (you do not need billions of enum values, and u_int
    # is just convenient, being the size of the machine word).
    # It conceivable that it may be smaller in size than u_int, though.
    # At least on little-endian architectures, this is not a problem.
    # On big-endian, if the compiler choses u_int8_t for enum, the error
    # that we read afterwards may be wrong. In this case, we will raise
    # RuntimeError.
    error_code = ctypes.c_uint()
    error_code.value = 0

    result = handle.bitcoinconsensus_verify_script_with_amount(
        scriptPubKey, len(scriptPubKey), amount, tx_data, len(tx_data), inIdx,
        libconsensus_flags, ctypes.byref(error_code))

    if result == 1:
        # script was verified successfully - just return, no exception raised.
        return

    assert result == 0

    err = error_code.value

    if err > BITCOINCONENSUS_LAST_ERROR_VALUE:
        raise RuntimeError(
            'bitcoinconsensus_verify_script_with_amount failed with '
            'unknown error code {}'.format(err))

    if err != bitcoinconsensus_ERR_OK:
        # The errors returned are all about the input values.
        # Therefore it seems appropriate to raise ValueError here
        raise ValueError(BITCOINCONSENSUS_ERROR_NAMES[err])

    raise VerifyScriptError('script verification failed')
예제 #4
0
        change_addr = rpc.getnewaddress()
        change_pubkey_hex = rpc.getaddressinfo(change_addr)['pubkey']
        change_out = CMutableTxOut(
            CoreCoinParams.MAX_MONEY,
            CScript([x(change_pubkey_hex), OP_CHECKSIG]))

        digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))]

        txouts = [change_out] + digest_outs

        tx = CTransaction(txins, txouts).to_mutable()

        FEE_PER_VBYTE = 0.00025 * CoreCoinParams.COIN / 1000
        while True:
            required_fee = tx.get_virtual_size() * FEE_PER_VBYTE
            tx.vout[0].nValue = int(value_in -
                                    max(required_fee, 0.00011 *
                                        CoreCoinParams.COIN))

            r = rpc.signrawtransactionwithwallet(b2x(tx.serialize()))
            assert r['complete']
            tx = CTransaction.deserialize(x(r['hex']))

            if value_in - tx.vout[0].nValue >= required_fee:
                tx_bytes = tx.serialize()
                tx_hex = b2x(tx_bytes)
                print(tx_hex)
                print(len(tx_bytes), 'bytes', file=sys.stderr)
                print(rpc.sendrawtransaction(tx_hex))
                break
예제 #5
0
 def broadcast_transaction(self, tx: Transaction):
     url = self.URL.format(network=self.network)
     response = requests.post(url, json.dumps({"tx": tx.serialize().hex()}))
     if response.status_code != 201:
         raise FeeEstimationAPIError(response.status_code)
     return True
예제 #6
0
unsigned_tx = CTransaction(unsigned_vin, vout)

# Sign!
signed_vin = []
for i, (scriptSig, redeemScript) in enumerate(scripts):
    sighash = redeemScript.sighash(unsigned_tx, i, SIGHASH_NONE)
    sig = args.privkey.sign(sighash) + bytes([SIGHASH_NONE])

    signed_scriptSig = CScript([sig] + list(scriptSig))

    txin = CTxIn(unsigned_vin[i].prevout, signed_scriptSig)

    signed_vin.append(txin)

signed_tx = CTransaction(signed_vin, vout)

if args.dryrun:
    serialized_tx = signed_tx.serialize()
    logging.info('tx size: %d bytes' % len(serialized_tx))
    logging.debug('hex: %s' % b2x(serialized_tx))

else:
    # FIXME: the tx could be too long here, but there's no way to get sendmany
    # to *not* broadcast the transaction first. This is a proof-of-concept, so
    # punting.

    tx_hex = b2x(signed_tx.serialize())
    logging.debug('Sending publish tx, hex: %s' % tx_hex)
    txid = rpc.sendrawtransaction(tx_hex)
    logging.info('Sent publish tx: %s' % txid)
예제 #7
0
        value_in = coins_to_satoshi(unspent[-1]['amount'])

        change_addr = rpc.getnewaddress()
        change_pubkey_hex = rpc.getaddressinfo(change_addr)['pubkey']
        txouts = [
            CTxOut(CoreCoinParams.MAX_MONEY,
                   CScript([x(change_pubkey_hex), OP_CHECKSIG])),
            CTxOut(0, CScript([OP_RETURN, digest]))
        ]

        tx_unsigned = CTransaction(txins, txouts).to_mutable()

        FEE_PER_VBYTE = 0.00025 * CoreCoinParams.COIN / 1000
        while True:
            required_fee = tx_unsigned.get_virtual_size() * FEE_PER_VBYTE
            tx_unsigned.vout[0].nValue = int(value_in -
                                             max(required_fee, 0.00011 *
                                                 CoreCoinParams.COIN))

            r = rpc.signrawtransactionwithwallet(b2x(tx_unsigned.serialize()))
            assert r['complete']
            tx_signed = CTransaction.deserialize(x(r['hex']))

            if value_in - tx_signed.vout[0].nValue >= required_fee:
                tx_bytes = tx_signed.serialize()
                tx_hex = b2x(tx_bytes)
                print(tx_hex)
                print(len(tx_bytes), 'bytes', file=sys.stderr)
                print(rpc.sendrawtransaction(tx_hex))
                break