Esempio n. 1
0
def serialize_dict_bytes_bytes(type_: bytes,
                               dictionary: Mapping[bytes, bytes]) -> bytes:
    "Return the binary representation of the dataclass element."

    return b"".join([
        var_bytes.serialize(type_ + k) + var_bytes.serialize(v)
        for k, v in sorted(dictionary.items())
    ])
Esempio n. 2
0
def serialize_hd_key_paths(
        type_: bytes, hd_key_paths: Mapping[bytes, BIP32KeyOrigin]) -> bytes:
    "Return the binary representation of the dataclass element."

    if len(type_) != 1:
        err_msg = f"invalid type marker lenght: {len(type_)}, instead of 1"
        raise BTClibValueError(err_msg)

    return b"".join([
        var_bytes.serialize(type_ + k) + var_bytes.serialize(v.serialize())
        for k, v in sorted(hd_key_paths.items())
    ])
Esempio n. 3
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
Esempio n. 4
0
    def serialize(self, check_validity: bool = True) -> bytes:

        if check_validity:
            self.assert_valid()

        out = self.value.to_bytes(8, byteorder="little", signed=False)
        out += var_bytes.serialize(self.script_pub_key.script)
        return out
Esempio n. 5
0
    def serialize(self, check_validity: bool = True) -> bytes:
        "Return the serialization of the Witness."

        if check_validity:
            self.assert_valid()

        out = var_int.serialize(len(self.stack))
        return out + b"".join([var_bytes.serialize(w) for w in self.stack])
Esempio n. 6
0
def test_nulldata() -> None:

    OP_RETURN = b"\x6a"  # pylint: disable=invalid-name

    # self-consistency
    string = "time-stamped data"
    payload = string.encode()
    script_pub_key = serialize(["OP_RETURN", payload])
    assert script_pub_key == ScriptPubKey.nulldata(string).script

    # back from the script_pub_key to the payload
    assert ("nulldata", payload) == type_and_payload(script_pub_key)

    # data -> payload in this case is invertible (no hash functions)
    assert payload.decode("ascii") == string

    assert address(script_pub_key) == ""

    # documented test cases: https://learnmeabitcoin.com/guide/nulldata
    string = "hello world"
    payload = string.encode()
    assert payload.hex() == "68656c6c6f20776f726c64"  # pylint: disable=no-member
    script_pub_key = OP_RETURN + var_bytes.serialize(payload)
    assert script_pub_key == ScriptPubKey.nulldata(string).script
    assert ("nulldata", payload) == type_and_payload(script_pub_key)

    # documented test cases: https://learnmeabitcoin.com/guide/nulldata
    string = "charley loves heidi"
    payload = string.encode()
    assert (payload.hex()  # pylint: disable=no-member
            == "636861726c6579206c6f766573206865696469")
    script_pub_key = OP_RETURN + var_bytes.serialize(payload)
    assert script_pub_key == ScriptPubKey.nulldata(string).script
    assert ("nulldata", payload) == type_and_payload(script_pub_key)

    # documented test cases: https://learnmeabitcoin.com/guide/nulldata
    string = "家族も友達もみんなが笑顔の毎日がほしい"
    payload = string.encode()
    assert (
        payload.hex()  # pylint: disable=no-member
        ==
        "e5aeb6e6978fe38282e58f8be98194e38282e381bfe38293e381aae3818ce7ac91e9a194e381aee6af8ee697a5e3818ce381bbe38197e38184"
    )
    script_pub_key = OP_RETURN + var_bytes.serialize(payload)
    assert script_pub_key == ScriptPubKey.nulldata(string).script
    assert ("nulldata", payload) == type_and_payload(script_pub_key)
Esempio n. 7
0
    def serialize(self, check_validity: bool = True) -> bytes:

        if check_validity:
            self.assert_valid()

        out = self.prev_out.serialize()
        out += var_bytes.serialize(self.script_sig)
        out += self.sequence.to_bytes(4, byteorder="little", signed=False)
        return out
Esempio n. 8
0
    def serialize(self, check_validity: bool = True) -> bytes:
        "Serialize an ECDSA signature to strict ASN.1 DER representation"

        if check_validity:
            self.assert_valid()

        out = _serialize_scalar(self.r)
        out += _serialize_scalar(self.s)
        return _DER_SIG_MARKER + var_bytes.serialize(out)
Esempio n. 9
0
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)
Esempio n. 10
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))
Esempio n. 11
0
def segwit_v0(script_: Octets, tx: Tx, vin_i: int, hash_type: int,
              amount: int) -> bytes:
    script_ = bytes_from_octets(script_)

    hash_prev_outs = b"\x00" * 32
    if not hash_type & ANYONECANPAY:
        hash_prev_outs = b"".join([vin.prev_out.serialize() for vin in tx.vin])
        hash_prev_outs = hash256(hash_prev_outs)

    hash_seqs = b"\x00" * 32
    if (not (hash_type & ANYONECANPAY) and (hash_type & 0x1F) != SINGLE
            and (hash_type & 0x1F) != NONE):
        hash_seqs = b"".join([
            vin.sequence.to_bytes(4, byteorder="little", signed=False)
            for vin in tx.vin
        ])
        hash_seqs = hash256(hash_seqs)

    hash_outputs = b"\x00" * 32
    if hash_type & 0x1F not in (SINGLE, NONE):
        hash_outputs = b"".join([vout.serialize() for vout in tx.vout])
        hash_outputs = hash256(hash_outputs)
    elif (hash_type & 0x1F) == SINGLE and vin_i < len(tx.vout):
        hash_outputs = hash256(tx.vout[vin_i].serialize())

    preimage = b"".join([
        tx.version.to_bytes(4, byteorder="little", signed=False),
        hash_prev_outs,
        hash_seqs,
        tx.vin[vin_i].prev_out.serialize(),
        var_bytes.serialize(script_),
        amount.to_bytes(8, byteorder="little", signed=False),  # value
        tx.vin[vin_i].sequence.to_bytes(4, byteorder="little", signed=False),
        hash_outputs,
        tx.lock_time.to_bytes(4, byteorder="little", signed=False),
        hash_type.to_bytes(4, byteorder="little", signed=False),
    ])
    return hash256(preimage)
Esempio n. 12
0
def _serialize_scalar(scalar: int) -> bytes:
    # 'highest bit set' padding included here
    scalar_size = scalar.bit_length() // 8 + 1
    scalar_bytes = scalar.to_bytes(scalar_size, byteorder="big", signed=False)
    return _DER_SCALAR_MARKER + var_bytes.serialize(scalar_bytes)
Esempio n. 13
0
def serialize_bytes(type_: bytes, value: bytes) -> bytes:
    "Return the binary representation of the dataclass element."
    return var_bytes.serialize(type_) + var_bytes.serialize(value)
Esempio n. 14
0
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