コード例 #1
0
def check_output_pubkey(q: Octets,
                        script: Octets,
                        control: Octets,
                        ec: Curve = secp256k1) -> bool:
    q = bytes_from_octets(q)
    script = bytes_from_octets(script)
    control = bytes_from_octets(control)
    if len(control) > 4129:  # 33 + 32 * 128
        raise BTClibValueError("Control block too long")
    m = (len(control) - 33) // 32
    if len(control) != 33 + 32 * m:
        raise BTClibValueError("Invalid control block length")
    leaf_version = control[0] & 0xFE
    preimage = leaf_version.to_bytes(1, "big") + var_bytes.serialize(script)
    k = tagged_hash(b"TapLeaf", preimage)
    for j in range(m):
        e = control[33 + 32 * j:65 + 32 * j]
        if k < e:
            k = tagged_hash(b"TapBranch", k + e)
        else:
            k = tagged_hash(b"TapBranch", e + k)
    p_bytes = control[1:33]
    t_bytes = tagged_hash(b"TapTweak", p_bytes + k)
    p = int.from_bytes(p_bytes, "big")
    t = int.from_bytes(t_bytes, "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    P = (p, secp256k1.y_even(p))
    Q = secp256k1.add(P, mult(t))
    return Q[0] == int.from_bytes(q, "big") and control[0] & 1 == Q[1] % 2
コード例 #2
0
def tree_helper(script_tree) -> Tuple[Any, bytes]:
    if len(script_tree) == 1:
        leaf_version, script = script_tree[0]
        leaf_version = leaf_version & 0xFE
        preimage = leaf_version.to_bytes(1, "big")
        preimage += var_bytes.serialize(serialize(script))
        h = tagged_hash(b"TapLeaf", preimage)
        return ([((leaf_version, script), bytes())], h)
    left, left_h = tree_helper(script_tree[0])
    right, right_h = tree_helper(script_tree[1])
    info = [(leaf, c + right_h) for leaf, c in left]
    info += [(leaf, c + left_h) for leaf, c in right]
    if right_h < left_h:
        left_h, right_h = right_h, left_h
    return (info, tagged_hash(b"TapBranch", left_h + right_h))
コード例 #3
0
def output_prvkey(
    internal_prvkey: PrvKey,
    script_tree: Optional[TaprootScriptTree] = None,
    ec: Curve = secp256k1,
) -> int:
    internal_prvkey = int_from_prv_key(internal_prvkey)
    P = mult(internal_prvkey)
    if script_tree:
        _, h = tree_helper(script_tree)
    else:
        h = tagged_hash(b"TapTweak", P[0].to_bytes(32, "big"))
    has_even_y = ec.y_even(P[0]) == P[1]
    internal_prvkey = internal_prvkey if has_even_y else ec.n - internal_prvkey
    t = int.from_bytes(tagged_hash(b"TapTweak", P[0].to_bytes(32, "big") + h),
                       "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    return (internal_prvkey + t) % ec.n
コード例 #4
0
def _det_nonce_(
    msg_hash: bytes, q: int, Q: int, aux: bytes, ec: Curve, hf: HashF
) -> int:

    # assume the random oracle model for the hash function,
    # i.e. hash values can be considered uniformly random

    # Note that in general, taking a uniformly random integer
    # modulo the curve order n would produce a biased result.
    # However, if the order n is sufficiently close to 2^hf_len,
    # then the bias is not observable:
    # e.g. for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
    #
    # the unbiased implementation is provided here,
    # which works also for very-low-cardinality test curves

    randomizer = tagged_hash("BIP0340/aux".encode(), aux, hf)
    xor = q ^ int.from_bytes(randomizer, "big", signed=False)
    max_len = max(ec.n_size, hf().digest_size)
    t = b"".join(
        [
            xor.to_bytes(max_len, byteorder="big", signed=False),
            Q.to_bytes(ec.p_size, byteorder="big", signed=False),
            msg_hash,
        ]
    )

    nonce_tag = "BIP0340/nonce".encode()
    while True:
        t = tagged_hash(nonce_tag, t, hf)
        # The following lines would introduce a bias
        # nonce = int.from_bytes(t, 'big') % ec.n
        # nonce = int_from_bits(t, ec.nlen) % ec.n
        # In general, taking a uniformly random integer (like those
        # obtained from a hash function in the random oracle model)
        # modulo the curve order n would produce a biased result.
        # However, if the order n is sufficiently close to 2^hf_len,
        # then the bias is not observable: e.g.
        # for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
        nonce = int_from_bits(t, ec.nlen)  # candidate nonce
        if 0 < nonce < ec.n:  # acceptable value for nonce
            return nonce  # successful candidate
コード例 #5
0
ファイル: sig_hash.py プロジェクト: btclib-org/btclib
def from_tx(prevouts: List[TxOut], tx: Tx, vin_i: int,
            hash_type: int) -> bytes:

    script = prevouts[vin_i].script_pub_key.script

    if is_p2tr(script):
        annex = b""
        witness = tx.vin[vin_i].script_witness
        if len(witness.stack) >= 2 and witness.stack[-1][0] == 0x50:
            annex = witness.stack[-1]
            witness.stack = witness.stack[:-1]

        if len(witness.stack) == 0:
            raise BTClibValueError("Empty stack")

        ext = b""
        if len(witness.stack) > 1:
            leaf_version = witness.stack[-1][0] & 0xFE
            preimage = leaf_version.to_bytes(1, "big")
            preimage += var_bytes.serialize(witness.stack[-2])
            tapleaf_hash = tagged_hash(b"TapLeaf", preimage)
            ext = tapleaf_hash + b"\x00\xff\xff\xff\xff"

        return taproot(
            tx,
            vin_i,
            [x.value for x in prevouts],
            [x.script_pub_key for x in prevouts],
            hash_type,
            int(bool(ext)),
            annex,
            ext,
        )

    # handle all p2sh-wrapped scripts
    if is_p2sh(script):
        script = tx.vin[vin_i].script_sig

    if is_p2wpkh(script):
        script_ = witness_v0_script(script)[0]
        return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value)

    if is_p2wsh(script):
        # the real script is contained in the witness
        script_ = witness_v0_script(tx.vin[vin_i].script_witness.stack[-1])[0]
        return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value)

    if is_p2tr(script):
        raise BTClibValueError("Taproot scripts cannot be wrapped in p2sh")

    script_ = legacy_script(script)[0]
    return legacy(script_, tx, vin_i, hash_type)
コード例 #6
0
def output_pubkey(
    internal_pubkey: Optional[Key] = None,
    script_tree: Optional[TaprootScriptTree] = None,
    ec: Curve = secp256k1,
) -> Tuple[bytes, int]:
    if not internal_pubkey and not script_tree:
        raise BTClibValueError("Missing data")
    if internal_pubkey:
        pubkey = pub_keyinfo_from_key(internal_pubkey, compressed=True)[0][1:]
    else:
        h_str = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
        pubkey = bytes.fromhex(h_str)
    if script_tree:
        _, h = tree_helper(script_tree)
    else:
        h = tagged_hash(b"TapTweak", pubkey)
    t = int.from_bytes(tagged_hash(b"TapTweak", pubkey + h), "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    x = int.from_bytes(pubkey, "big")
    Q = ec.add((x, ec.y_even(x)), mult(t))
    return Q[0].to_bytes(32, "big"), Q[1] % 2
コード例 #7
0
ファイル: ssa.py プロジェクト: giacomocaironi/btclib
def challenge_(msg_hash: Octets, x_Q: int, x_K: int, ec: Curve,
               hf: HashF) -> int:

    # the message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    t = b"".join([
        x_K.to_bytes(ec.p_size, byteorder="big", signed=False),
        x_Q.to_bytes(ec.p_size, byteorder="big", signed=False),
        msg_hash,
    ])
    t = tagged_hash("BIP0340/challenge".encode(), t, hf)

    c = int_from_bits(t, ec.nlen) % ec.n
    if c == 0:
        raise BTClibRuntimeError("invalid zero challenge")  # pragma: no cover
    return c
コード例 #8
0
def test_tagged_hash() -> None:

    s = "deadbeef"
    b = s.encode()
    assert tagged_hash("btclib", s) == tagged_hash("btclib", b)
コード例 #9
0
ファイル: sig_hash.py プロジェクト: btclib-org/btclib
def taproot(
    transaction: Tx,
    input_index: int,
    amounts: List[int],
    scriptpubkeys: List[ScriptPubKey],
    hashtype: int,
    ext_flag: int,
    annex: bytes,
    message_extension: bytes,
) -> bytes:

    if hashtype not in SIG_HASH_TYPES:
        raise BTClibValueError(f"Unknown hash type: {hashtype}")
    if hashtype & 0x03 == SINGLE and input_index >= len(transaction.vout):
        raise BTClibValueError("Sighash single wihout a corresponding output")

    preimage = b"\x00"
    preimage += hashtype.to_bytes(1, "little")
    preimage += transaction.nVersion.to_bytes(4, "little")
    preimage += transaction.nLockTime.to_bytes(4, "little")

    if hashtype & 0x80 != ANYONECANPAY:
        sha_prevouts = b""
        sha_amounts = b""
        sha_scriptpubkeys = b""
        sha_sequences = b""
        for i, vin in enumerate(transaction.vin):
            sha_prevouts += vin.prev_out.serialize()
            sha_amounts += amounts[i].to_bytes(8, "little")
            sha_scriptpubkeys += var_bytes.serialize(scriptpubkeys[i].script)
            sha_sequences += vin.nSequence.to_bytes(4, "little")
        preimage += sha256(sha_prevouts)
        preimage += sha256(sha_amounts)
        preimage += sha256(sha_scriptpubkeys)
        preimage += sha256(sha_sequences)

    if hashtype & 0x03 not in [NONE, SINGLE]:
        sha_outputs = b""
        for vout in transaction.vout:
            sha_outputs += vout.serialize()
        preimage += sha256(sha_outputs)

    annex_present = int(bool(annex))
    preimage += (2 * ext_flag + annex_present).to_bytes(1, "little")

    if hashtype & 0x80 == ANYONECANPAY:
        preimage += transaction.vin[input_index].prev_out.serialize()
        preimage += amounts[input_index].to_bytes(8, "little")
        preimage += var_bytes.serialize(scriptpubkeys[input_index].script)
        preimage += transaction.vin[input_index].nSequence.to_bytes(
            4, "little")
    else:
        preimage += input_index.to_bytes(4, "little")

    if annex_present:
        sha_annex = var_bytes.serialize(annex)
        preimage += sha256(sha_annex)

    if hashtype & 0x03 == SINGLE:
        preimage += sha256(transaction.vout[input_index].serialize())

    preimage += message_extension

    sig_hash = tagged_hash(b"TapSighash", preimage)
    return sig_hash