예제 #1
0
    def parse(cls: Type["Sig"],
              data: BinaryData,
              check_validity: bool = True) -> "Sig":
        """Return a Sig by parsing binary data.

        Deserialize a strict ASN.1 DER representation of an ECDSA signature.
        """

        stream = bytesio_from_binarydata(data)
        ec = secp256k1

        # [0x30] [data-size][0x02][r-size][r][0x02][s-size][s]
        marker = stream.read(1)
        if marker != _DER_SIG_MARKER:
            err_msg = f"invalid compound header: {marker.hex()}"
            err_msg += f", instead of DER sequence tag {_DER_SIG_MARKER.hex()}"
            raise BTClibValueError(err_msg)

        # [data-size][0x02][r-size][r][0x02][s-size][s]
        sig_data = var_bytes.parse(stream, forbid_zero_size=True)

        # [0x02][r-size][r][0x02][s-size][s]
        sig_data_substream = bytesio_from_binarydata(sig_data)
        r = _deserialize_scalar(sig_data_substream)
        s = _deserialize_scalar(sig_data_substream)

        # to prevent malleability
        # the sig_data_substream must have been consumed entirely
        if sig_data_substream.read(1) != b"":
            err_msg = "invalid DER sequence length"
            raise BTClibValueError(err_msg)

        return cls(r, s, ec, check_validity)
예제 #2
0
    def parse(cls: Type["Witness"],
              data: BinaryData,
              check_validity: bool = True) -> "Witness":
        "Return a Witness by parsing binary data."

        data = bytesio_from_binarydata(data)
        n = var_int.parse(data)
        stack = [var_bytes.parse(data) for _ in range(n)]
        return cls(stack, check_validity)
예제 #3
0
 def parse(cls: Type["TxOut"],
           data: BinaryData,
           check_validity: bool = True) -> "TxOut":
     stream = bytesio_from_binarydata(data)
     value = int.from_bytes(stream.read(8),
                            byteorder="little",
                            signed=False)
     script = var_bytes.parse(stream)
     return cls(value, ScriptPubKey(script, "mainnet"), check_validity)
예제 #4
0
    def parse(cls: Type["TxIn"],
              data: BinaryData,
              check_validity: bool = True) -> "TxIn":

        stream = bytesio_from_binarydata(data)
        prev_out = OutPoint.parse(stream)
        script_sig = var_bytes.parse(stream)
        sequence = int.from_bytes(stream.read(4),
                                  byteorder="little",
                                  signed=False)

        return cls(prev_out, script_sig, sequence, Witness(), check_validity)
예제 #5
0
def _deserialize_scalar(sig_data_stream: BytesIO) -> int:

    marker = sig_data_stream.read(1)
    if marker != _DER_SCALAR_MARKER:
        err_msg = f"invalid value header: {marker.hex()}"
        err_msg += f", instead of integer element {_DER_SCALAR_MARKER.hex()}"
        raise BTClibValueError(err_msg)

    r_bytes = var_bytes.parse(sig_data_stream, forbid_zero_size=True)
    if r_bytes[0] == 0 and r_bytes[1] < 0x80:
        raise BTClibValueError("invalid 'highest bit set' padding")
    if r_bytes[0] >= 0x80:
        raise BTClibValueError("invalid negative scalar")

    return int.from_bytes(r_bytes, byteorder="big", signed=False)
예제 #6
0
 def parse(
     cls: Type["TxOut"],
     data: BinaryData,
     check_validity: bool = True,
 ) -> "TxOut":
     stream = bytesio_from_binarydata(data)
     value = int.from_bytes(stream.read(8),
                            byteorder="little",
                            signed=False)
     script = var_bytes.parse(stream)
     return cls(
         value,
         ScriptPubKey(script, "mainnet", check_validity=False
                      ),  # https://github.com/bitcoin/bitcoin/issues/320
         check_validity,
     )
예제 #7
0
    def height(self) -> Optional[int]:
        """Return the height committed into a BIP34 coinbase script_sig.

        Version 2 blocks commit block height into the coinbase script_sig.

        https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki

        Block 227,835 (2013-03-24 15:49:13 GMT) was the last version 1 block.
        """
        if not self.transactions[0].is_coinbase():
            raise BTClibValueError("first transaction is not a coinbase")

        if self.header.version == 1:
            return None

        # Height is "serialized CScript": first byte is number of bytes,
        # followed by the _signed_ little-endian representation of the height
        # (genesis block is height zero).
        coinbase_script = self.transactions[0].vin[0].script_sig
        height_ = var_bytes.parse(coinbase_script)
        return decode_num(height_)
def addresses(script_pub_key: Octets, network: str = "mainnet") -> List[str]:
    "Return the p2pkh addresses of the pub_keys used in a p2ms script_pub_key."

    script_pub_key = bytes_from_octets(script_pub_key)
    # p2ms [m, pub_keys, n, OP_CHECKMULTISIG]
    length = len(script_pub_key)
    if length < 37:
        raise BTClibValueError(f"invalid p2ms length {length}")
    if script_pub_key[-1] != 0xAE:
        raise BTClibValueError("missing final OP_CHECKMULTISIG")
    m = script_pub_key[0] - 80
    if not 0 < m < 17:
        raise BTClibValueError(f"invalid m in m-of-n: {m}")
    n = script_pub_key[-2] - 80
    if not m <= n < 17:
        raise BTClibValueError(f"invalid m-of-n: {m}-of-{n}")

    stream = bytesio_from_binarydata(script_pub_key[1:-2])
    pub_keys = [var_bytes.parse(stream) for _ in range(n)]

    if stream.read(1):
        raise BTClibValueError("invalid p2ms script_pub_key size")

    return [b58.p2pkh(pub_key, network) for pub_key in pub_keys]