Ejemplo n.º 1
0
    def from_unconfidential(
        cls: Type[T_CCoinConfidentialAddress],
        unconfidential_adr: CCoinAddress, blinding_pubkey: Union[CPubKey,
                                                                 bytes,
                                                                 bytearray]
    ) -> T_CCoinConfidentialAddress:
        """Convert unconfidential address to confidential

        Raises CConfidentialAddressError if blinding_pubkey is invalid
        (CConfidentialAddressError is a subclass of CCoinAddressError)

        unconfidential_adr can be string or CBase58CoinAddress
        instance. blinding_pubkey must be a bytes instance
        """
        ensure_isinstance(blinding_pubkey, (CPubKey, bytes, bytearray),
                          'blinding_pubkey')
        if not isinstance(blinding_pubkey, CPubKey):
            blinding_pubkey = CPubKey(blinding_pubkey)
        if not blinding_pubkey.is_fullyvalid():
            raise ValueError('invalid blinding pubkey')

        # without #noqa linter gives warning that we should use isinstance.
        # but here we want exact match, isinstance is not applicable
        if type(cls) is not type(unconfidential_adr.__class__):  #noqa
            raise TypeError(
                'cannot create {} from {}: this address class might belong '
                'to different chain'.format(
                    cls.__name__, unconfidential_adr.__class__.__name__))

        clsmap = {
            P2PKHCoinAddress: P2PKHCoinConfidentialAddress,
            P2WPKHCoinAddress: P2WPKHCoinConfidentialAddress,
            P2SHCoinAddress: P2SHCoinConfidentialAddress,
            P2WSHCoinAddress: P2WSHCoinConfidentialAddress,
        }
        for unconf_cls, conf_cls in clsmap.items():
            mapped_cls_list = dispatcher_mapped_list(conf_cls)
            if mapped_cls_list:
                if len(mapped_cls_list) != 1:
                    raise TypeError(
                        f"{conf_cls.__name__} must be final dispatch class")
                chain_specific_conf_cls = mapped_cls_list[0]
            else:
                chain_specific_conf_cls = conf_cls
            if isinstance(unconfidential_adr, unconf_cls) and\
                    (issubclass(cls, (conf_cls, chain_specific_conf_cls))
                     or issubclass(chain_specific_conf_cls, cls)):
                conf_adr = conf_cls.from_bytes(blinding_pubkey +
                                               unconfidential_adr)
                return cast(T_CCoinConfidentialAddress, conf_adr)
            if issubclass(cls, (conf_cls, chain_specific_conf_cls)):
                raise TypeError(
                    'cannot create {} from {}: only subclasses of {} are accepted'
                    .format(cls.__name__,
                            unconfidential_adr.__class__.__name__,
                            unconf_cls.__name__))

        raise CConfidentialAddressError(
            'cannot create {} from {}: no matching confidential address class'.
            format(cls.__name__, unconfidential_adr.__class__.__name__))
Ejemplo n.º 2
0
    def __new__(cls: Type[T_CCoinAddress], s: str) -> T_CCoinAddress:
        ensure_isinstance(s, str, 'address string')
        recognized_encoding = []
        target_cls_set = dispatcher_mapped_list(cls)
        for target_cls in target_cls_set:
            try:
                return target_cls(s)
            except CCoinAddressError:
                recognized_encoding.append(target_cls.__name__)
            except bitcointx.core.AddressDataEncodingError:
                pass

        if recognized_encoding:
            raise CCoinAddressError(
                'Correct encoding for any of {}, but not correct format'
                .format(recognized_encoding))

        raise CCoinAddressError(
            'Unrecognized encoding for any of {}'
            .format([tcls.__name__ for tcls in target_cls_set]))
Ejemplo n.º 3
0
    def from_pubkey(cls: Type[T_P2WPKHCoinAddress],
                    pubkey: Union[CPubKey, bytes, bytearray],
                    accept_invalid: bool = False) -> T_P2WPKHCoinAddress:
        """Create a P2WPKH address from a pubkey

        Raises CCoinAddressError if pubkey is invalid, unless accept_invalid
        is True.

        The pubkey must be a bytes instance;
        """
        ensure_isinstance(pubkey, (CPubKey, bytes, bytearray), 'pubkey')

        if not accept_invalid:
            if not isinstance(pubkey, CPubKey):
                pubkey = CPubKey(pubkey)
            if not pubkey.is_fullyvalid():
                raise P2PKHCoinAddressError('invalid pubkey')

        pubkey_hash = bitcointx.core.Hash160(pubkey)
        return cls.from_bytes(pubkey_hash)
Ejemplo n.º 4
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')
Ejemplo n.º 5
0
def VerifyScript(scriptSig: CScript, scriptPubKey: CScript,
                 txTo: 'bitcointx.core.CTransaction', inIdx: int,
                 flags: Optional[Union[Tuple[ScriptVerifyFlag_Type, ...],
                                       Set[ScriptVerifyFlag_Type]]] = None,
                 amount: int = 0, witness: Optional[CScriptWitness] = None
                 ) -> None:
    """Verify a scriptSig satisfies a scriptPubKey

    scriptSig    - Signature

    scriptPubKey - PubKey

    txTo         - Spending transaction

    inIdx        - Index of the transaction input containing scriptSig

    Raises a ValidationError subclass if the validation fails.
    """

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

    script_class = scriptSig.__class__

    if flags is None:
        flags = STANDARD_SCRIPT_VERIFY_FLAGS - UNHANDLED_SCRIPT_VERIFY_FLAGS
    else:
        flags = set(flags)  # might be passed as tuple

    if flags & UNHANDLED_SCRIPT_VERIFY_FLAGS:
        raise VerifyScriptError(
            "some of the flags cannot be handled by current code: {}"
            .format(script_verify_flags_to_string(flags & UNHANDLED_SCRIPT_VERIFY_FLAGS)))

    stack: List[bytes] = []
    EvalScript(stack, scriptSig, txTo, inIdx, flags=flags)
    if SCRIPT_VERIFY_P2SH in flags:
        stackCopy = list(stack)
    EvalScript(stack, scriptPubKey, txTo, inIdx, flags=flags)
    if len(stack) == 0:
        raise VerifyScriptError("scriptPubKey left an empty stack")
    if not _CastToBool(stack[-1]):
        raise VerifyScriptError("scriptPubKey returned false")

    hadWitness = False
    if witness is None:
        witness = CScriptWitness([])

    if SCRIPT_VERIFY_WITNESS in flags and scriptPubKey.is_witness_scriptpubkey():
        hadWitness = True

        if scriptSig:
            raise VerifyScriptError("scriptSig is not empty")

        VerifyWitnessProgram(witness,
                             scriptPubKey.witness_version(),
                             scriptPubKey.witness_program(),
                             txTo, inIdx, flags=flags, amount=amount,
                             script_class=script_class)

        # Bypass the cleanstack check at the end. The actual stack is obviously not clean
        # for witness programs.
        stack = stack[:1]

    # Additional validation for spend-to-script-hash transactions
    if SCRIPT_VERIFY_P2SH in flags and scriptPubKey.is_p2sh():
        if not scriptSig.is_push_only():
            raise VerifyScriptError("P2SH scriptSig not is_push_only()")

        # restore stack
        stack = stackCopy

        # stack cannot be empty here, because if it was the
        # P2SH  HASH <> EQUAL  scriptPubKey would be evaluated with
        # an empty stack and the EvalScript above would return false.
        assert len(stack)

        pubKey2 = script_class(stack.pop())

        EvalScript(stack, pubKey2, txTo, inIdx, flags=flags)

        if not len(stack):
            raise VerifyScriptError("P2SH inner scriptPubKey left an empty stack")

        if not _CastToBool(stack[-1]):
            raise VerifyScriptError("P2SH inner scriptPubKey returned false")

        # P2SH witness program
        if SCRIPT_VERIFY_WITNESS in flags and pubKey2.is_witness_scriptpubkey():
            hadWitness = True

            if scriptSig != script_class([pubKey2]):
                raise VerifyScriptError("scriptSig is not exactly a single push of the redeemScript")

            VerifyWitnessProgram(witness,
                                 pubKey2.witness_version(),
                                 pubKey2.witness_program(),
                                 txTo, inIdx, flags=flags, amount=amount,
                                 script_class=script_class)

            # Bypass the cleanstack check at the end. The actual stack is obviously not clean
            # for witness programs.
            stack = stack[:1]

    if SCRIPT_VERIFY_CLEANSTACK in flags:
        if SCRIPT_VERIFY_P2SH not in flags:
            raise ValueError(
                'SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_P2SH')

        if len(stack) == 0:
            raise VerifyScriptError("scriptPubKey left an empty stack")
        elif len(stack) != 1:
            raise VerifyScriptError("scriptPubKey left extra items on stack")

    if SCRIPT_VERIFY_WITNESS in flags:
        # We can't check for correct unexpected witness data if P2SH was off, so require
        # that WITNESS implies P2SH. Otherwise, going from WITNESS->P2SH+WITNESS would be
        # possible, which is not a softfork.
        if SCRIPT_VERIFY_P2SH not in flags:
            raise ValueError(
                "SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH")

        if not hadWitness and witness:
            raise VerifyScriptError("Unexpected witness")
Ejemplo n.º 6
0
def RawElementsSignatureHash(
    script: CScript,
    txTo: 'elementstx.core.CElementsTransaction',
    inIdx: int,
    hashtype: SIGHASH_Type,
    amount: Optional['elementstx.core.CConfidentialValue'] = None,
    sigversion: SIGVERSION_Type = SIGVERSION_BASE
) -> Tuple[bytes, Optional[str]]:
    """Consensus-correct SignatureHash

    Returns (hash, err) to precisely match the consensus-critical behavior of
    the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)

    If you're just writing wallet software you probably want SignatureHash()
    instead.
    """
    if sigversion not in (SIGVERSION_BASE, SIGVERSION_WITNESS_V0):
        raise ValueError('unexpected sigversion')

    if sigversion == SIGVERSION_BASE:
        # revert to standard bitcoin signature hash
        # amount is not used in SIGVERSION_BASE sighash,
        # so we specify invalid value.
        return RawBitcoinSignatureHash(script,
                                       txTo,
                                       inIdx,
                                       hashtype,
                                       amount=-1,
                                       sigversion=sigversion)

    ensure_isinstance(amount, elementstx.core.CConfidentialValue, 'amount')
    assert isinstance(amount, elementstx.core.CConfidentialValue)

    hashPrevouts = b'\x00' * 32
    hashSequence = b'\x00' * 32
    hashIssuance = b'\x00' * 32
    hashOutputs = b'\x00' * 32  # noqa

    if not (hashtype & SIGHASH_ANYONECANPAY):
        serialize_prevouts = bytes()
        serialize_issuance = bytes()
        for vin in txTo.vin:
            serialize_prevouts += vin.prevout.serialize()
            if vin.assetIssuance.is_null():
                serialize_issuance += b'\x00'
            else:
                serialize_issuance += vin.assetIssuance.serialize()
        hashPrevouts = Hash(serialize_prevouts)
        hashIssuance = Hash(serialize_issuance)

    if (not (hashtype & SIGHASH_ANYONECANPAY)
            and (hashtype & 0x1f) != SIGHASH_SINGLE
            and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_sequence = bytes()
        for i in txTo.vin:
            serialize_sequence += struct.pack("<I", i.nSequence)
        hashSequence = Hash(serialize_sequence)

    if ((hashtype & 0x1f) != SIGHASH_SINGLE
            and (hashtype & 0x1f) != SIGHASH_NONE):
        serialize_outputs = bytes()
        for o in txTo.vout:
            serialize_outputs += o.serialize()
        hashOutputs = Hash(serialize_outputs)
    elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
        serialize_outputs = txTo.vout[inIdx].serialize()
        hashOutputs = Hash(serialize_outputs)

    f = BytesIO()
    f.write(struct.pack("<i", txTo.nVersion))
    f.write(hashPrevouts)
    f.write(hashSequence)
    f.write(hashIssuance)
    txTo.vin[inIdx].prevout.stream_serialize(f)
    BytesSerializer.stream_serialize(script, f)
    f.write(amount.commitment)
    f.write(struct.pack("<I", txTo.vin[inIdx].nSequence))
    if not txTo.vin[inIdx].assetIssuance.is_null():
        txTo.vin[inIdx].assetIssuance.stream_serialize(f)
    f.write(hashOutputs)
    f.write(struct.pack("<i", txTo.nLockTime))
    f.write(struct.pack("<i", hashtype))

    hash = Hash(f.getvalue())

    return (hash, None)