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)
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)
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)
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)
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)
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, )
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]