Finalize the script hex APIs
Recently we made an attempt at making the hex APIs for scripts easier to use, better documented, and shown via an example. After that work we decided it would be better if `LowerHex`/`UpperHex` did not have the prefix. We also wanted to further clarify the inherent function names to make the all explicit. See GitHub issue #4316 for the thread of discussion. Note that this PR does not require changes to the serde regression test which were non changed in the original work either.
This commit is contained in:
parent
41f26cf090
commit
6b90e42e78
|
@ -15,10 +15,16 @@ use bitcoin::ScriptBuf;
|
||||||
fn main() {
|
fn main() {
|
||||||
let pk = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".parse::<WPubkeyHash>().unwrap();
|
let pk = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".parse::<WPubkeyHash>().unwrap();
|
||||||
|
|
||||||
// TL;DR Use `to_hex_string` and `from_hex`.
|
// TL;DR Use `to_hex_string_prefixed` and `from_hex_prefixed`.
|
||||||
let script_code = script::p2wpkh_script_code(pk);
|
let script_code = script::p2wpkh_script_code(pk);
|
||||||
let hex = script_code.to_hex_string();
|
let hex = script_code.to_hex_string_prefixed();
|
||||||
let decoded = ScriptBuf::from_hex(&hex).unwrap();
|
let decoded = ScriptBuf::from_hex_prefixed(&hex).unwrap();
|
||||||
|
assert_eq!(decoded, script_code);
|
||||||
|
|
||||||
|
// Or if you prefer: `to_hex_string_no_length_prefix` and `from_hex_no_length_prefix`.
|
||||||
|
let script_code = script::p2wpkh_script_code(pk);
|
||||||
|
let hex = script_code.to_hex_string_no_length_prefix();
|
||||||
|
let decoded = ScriptBuf::from_hex_no_length_prefix(&hex).unwrap();
|
||||||
assert_eq!(decoded, script_code);
|
assert_eq!(decoded, script_code);
|
||||||
|
|
||||||
// Writes the script as human-readable eg, OP_DUP OP_HASH160 OP_PUSHBYTES_20 ...
|
// Writes the script as human-readable eg, OP_DUP OP_HASH160 OP_PUSHBYTES_20 ...
|
||||||
|
@ -27,28 +33,25 @@ fn main() {
|
||||||
// We do not implement parsing scripts from human-readable format.
|
// We do not implement parsing scripts from human-readable format.
|
||||||
// let decoded = s.parse::<ScriptBuf>().unwrap();
|
// let decoded = s.parse::<ScriptBuf>().unwrap();
|
||||||
|
|
||||||
// This is equivalent to consensus encoding i.e., includes the length prefix.
|
// This is not equivalent to consensus encoding i.e., does not include the length prefix.
|
||||||
let hex_lower_hex_trait = format!("{script_code:x}");
|
let hex_lower_hex_trait = format!("{script_code:x}");
|
||||||
println!("hex created using `LowerHex`: {hex_lower_hex_trait}");
|
println!("hex created using `LowerHex`: {hex_lower_hex_trait}");
|
||||||
|
|
||||||
// The `deserialize_hex` function requires the length prefix.
|
// The `deserialize_hex` function requires the length prefix.
|
||||||
assert_eq!(encode::deserialize_hex::<ScriptBuf>(&hex_lower_hex_trait).unwrap(), script_code);
|
assert!(encode::deserialize_hex::<ScriptBuf>(&hex_lower_hex_trait).is_err());
|
||||||
// And so does `from_hex`.
|
// And so does `from_hex_prefixed`.
|
||||||
assert_eq!(ScriptBuf::from_hex(&hex_lower_hex_trait).unwrap(), script_code);
|
assert!(ScriptBuf::from_hex_prefixed(&hex_lower_hex_trait).is_err());
|
||||||
|
// But we provide an explicit constructor that does not.
|
||||||
// And we also provide an explicit constructor that does not use the length prefix.
|
assert_eq!(ScriptBuf::from_hex_no_length_prefix(&hex_lower_hex_trait).unwrap(), script_code);
|
||||||
let other = ScriptBuf::from_hex_no_length_prefix(&hex_lower_hex_trait).unwrap();
|
|
||||||
// Without a prefix the script parses but its not the one we meant.
|
|
||||||
assert_ne!(other, script_code);
|
|
||||||
|
|
||||||
// This is consensus encoding i.e., includes the length prefix.
|
// This is consensus encoding i.e., includes the length prefix.
|
||||||
let hex_inherent = script_code.to_hex_string(); // Defined in `ScriptExt`.
|
let hex_inherent = script_code.to_hex_string_prefixed(); // Defined in `ScriptExt`.
|
||||||
println!("hex created using inherent `to_hex_string`: {hex_inherent}");
|
println!("hex created using inherent `to_hex_string_prefixed`: {hex_inherent}");
|
||||||
|
|
||||||
// The inverse of `to_hex_string` is `from_hex`.
|
// The inverse of `to_hex_string_prefixed` is `from_hex_string_prefixed`.
|
||||||
let decoded = ScriptBuf::from_hex(&hex_inherent).unwrap(); // Defined in `ScriptBufExt`.
|
let decoded = ScriptBuf::from_hex_prefixed(&hex_inherent).unwrap(); // Defined in `ScriptBufExt`.
|
||||||
assert_eq!(decoded, script_code);
|
assert_eq!(decoded, script_code);
|
||||||
// We can also parse the output of `to_hex_string` using `deserialize_hex`.
|
// We can also parse the output of `to_hex_string_prefixed` using `deserialize_hex`.
|
||||||
let decoded = encode::deserialize_hex::<ScriptBuf>(&hex_inherent).unwrap();
|
let decoded = encode::deserialize_hex::<ScriptBuf>(&hex_inherent).unwrap();
|
||||||
assert_eq!(decoded, script_code);
|
assert_eq!(decoded, script_code);
|
||||||
|
|
||||||
|
@ -58,8 +61,10 @@ fn main() {
|
||||||
|
|
||||||
let decoded: ScriptBuf = encode::deserialize_hex(&encoded).unwrap();
|
let decoded: ScriptBuf = encode::deserialize_hex(&encoded).unwrap();
|
||||||
assert_eq!(decoded, script_code);
|
assert_eq!(decoded, script_code);
|
||||||
|
|
||||||
// And we can mix these to calls because both include the length prefix.
|
// And we can mix these to calls because both include the length prefix.
|
||||||
let decoded = ScriptBuf::from_hex(&encoded).unwrap();
|
let encoded = encode::serialize_hex(&script_code);
|
||||||
|
let decoded = ScriptBuf::from_hex_prefixed(&encoded).unwrap();
|
||||||
assert_eq!(decoded, script_code);
|
assert_eq!(decoded, script_code);
|
||||||
|
|
||||||
// Encode/decode using a byte vector.
|
// Encode/decode using a byte vector.
|
||||||
|
|
|
@ -376,12 +376,19 @@ crate::internal_macros::define_extension_trait! {
|
||||||
fn to_asm_string(&self) -> String { self.to_string() }
|
fn to_asm_string(&self) -> String { self.to_string() }
|
||||||
|
|
||||||
/// Consensus encodes the script as lower-case hex.
|
/// Consensus encodes the script as lower-case hex.
|
||||||
fn to_hex_string(&self) -> String { consensus::encode::serialize_hex(self) }
|
#[deprecated(since = "TBD", note = "use `to_hex_string_prefixed()` instead")]
|
||||||
|
fn to_hex_string(&self) -> String { self.to_hex_string_prefixed() }
|
||||||
|
|
||||||
|
/// Consensus encodes the script as lower-case hex.
|
||||||
|
fn to_hex_string_prefixed(&self) -> String { consensus::encode::serialize_hex(self) }
|
||||||
|
|
||||||
/// Consensus encodes the script as lower-case hex.
|
/// Consensus encodes the script as lower-case hex.
|
||||||
///
|
///
|
||||||
/// This is **not** consensus encoding, you likely want to use `to_hex_string`. The returned
|
/// This is **not** consensus encoding, you likely want to use `to_hex_string_prefixed`.
|
||||||
/// hex string will not include the length prefix.
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The returned hex string will not include the length prefix.
|
||||||
fn to_hex_string_no_length_prefix(&self) -> String {
|
fn to_hex_string_no_length_prefix(&self) -> String {
|
||||||
self.as_bytes().to_lower_hex_string()
|
self.as_bytes().to_lower_hex_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,18 @@ crate::internal_macros::define_extension_trait! {
|
||||||
/// Constructs a new [`ScriptBuf`] from a hex string.
|
/// Constructs a new [`ScriptBuf`] from a hex string.
|
||||||
///
|
///
|
||||||
/// The input string is expected to be consensus encoded i.e., includes the length prefix.
|
/// The input string is expected to be consensus encoded i.e., includes the length prefix.
|
||||||
fn from_hex(s: &str) -> Result<ScriptBuf, consensus::FromHexError> {
|
fn from_hex_prefixed(s: &str) -> Result<ScriptBuf, consensus::FromHexError> {
|
||||||
consensus::encode::deserialize_hex(s)
|
consensus::encode::deserialize_hex(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a new [`ScriptBuf`] from a hex string.
|
||||||
|
///
|
||||||
|
/// The input string is expected to be consensus encoded i.e., includes the length prefix.
|
||||||
|
#[deprecated(since = "TBD", note = "use `from_hex_string_prefixed()` instead")]
|
||||||
|
fn from_hex(s: &str) -> Result<ScriptBuf, consensus::FromHexError> {
|
||||||
|
Self::from_hex_prefixed(s)
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a new [`ScriptBuf`] from a hex string.
|
/// Constructs a new [`ScriptBuf`] from a hex string.
|
||||||
///
|
///
|
||||||
/// This is **not** consensus encoding. If your hex string is a consensus encode script then
|
/// This is **not** consensus encoding. If your hex string is a consensus encode script then
|
||||||
|
|
|
@ -431,12 +431,9 @@ impl fmt::Display for ScriptBuf {
|
||||||
|
|
||||||
#[cfg(feature = "hex")]
|
#[cfg(feature = "hex")]
|
||||||
impl fmt::LowerHex for Script {
|
impl fmt::LowerHex for Script {
|
||||||
// Currently we drop all formatter options.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let compact = internals::compact_size::encode(self.as_bytes().len());
|
fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
|
||||||
write!(f, "{:x}", compact.as_slice().as_hex())?;
|
|
||||||
write!(f, "{:x}", self.as_bytes().as_hex())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
@ -454,12 +451,9 @@ internals::impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &Self| script_buf
|
||||||
|
|
||||||
#[cfg(feature = "hex")]
|
#[cfg(feature = "hex")]
|
||||||
impl fmt::UpperHex for Script {
|
impl fmt::UpperHex for Script {
|
||||||
// Currently we drop all formatter options.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let compact = internals::compact_size::encode(self.as_bytes().len());
|
fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
|
||||||
write!(f, "{:X}", compact.as_slice().as_hex())?;
|
|
||||||
write!(f, "{:X}", self.as_bytes().as_hex())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,8 +505,7 @@ impl serde::Serialize for Script {
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
if serializer.is_human_readable() {
|
if serializer.is_human_readable() {
|
||||||
// Do not call LowerHex because we don't want to add the len prefix.
|
serializer.collect_str(&format_args!("{:x}", self))
|
||||||
serializer.collect_str(&format_args!("{}", self.as_bytes().as_hex()))
|
|
||||||
} else {
|
} else {
|
||||||
serializer.serialize_bytes(self.as_bytes())
|
serializer.serialize_bytes(self.as_bytes())
|
||||||
}
|
}
|
||||||
|
@ -803,8 +796,8 @@ mod tests {
|
||||||
|
|
||||||
#[cfg(feature = "hex")]
|
#[cfg(feature = "hex")]
|
||||||
{
|
{
|
||||||
assert_eq!(format!("{:x}", script), "0300a1b2");
|
assert_eq!(format!("{:x}", script), "00a1b2");
|
||||||
assert_eq!(format!("{:X}", script), "0300A1B2");
|
assert_eq!(format!("{:X}", script), "00A1B2");
|
||||||
}
|
}
|
||||||
assert!(!format!("{:?}", script).is_empty());
|
assert!(!format!("{:?}", script).is_empty());
|
||||||
}
|
}
|
||||||
|
@ -816,8 +809,8 @@ mod tests {
|
||||||
|
|
||||||
#[cfg(feature = "hex")]
|
#[cfg(feature = "hex")]
|
||||||
{
|
{
|
||||||
assert_eq!(format!("{:x}", script_buf), "0300a1b2");
|
assert_eq!(format!("{:x}", script_buf), "00a1b2");
|
||||||
assert_eq!(format!("{:X}", script_buf), "0300A1B2");
|
assert_eq!(format!("{:X}", script_buf), "00A1B2");
|
||||||
}
|
}
|
||||||
assert!(!format!("{:?}", script_buf).is_empty());
|
assert!(!format!("{:?}", script_buf).is_empty());
|
||||||
}
|
}
|
||||||
|
@ -935,7 +928,7 @@ mod tests {
|
||||||
fn script_to_hex() {
|
fn script_to_hex() {
|
||||||
let script = Script::from_bytes(&[0xa1, 0xb2, 0xc3]);
|
let script = Script::from_bytes(&[0xa1, 0xb2, 0xc3]);
|
||||||
let hex = script.to_hex();
|
let hex = script.to_hex();
|
||||||
assert_eq!(hex, "03a1b2c3");
|
assert_eq!(hex, "a1b2c3");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -944,6 +937,6 @@ mod tests {
|
||||||
fn scriptbuf_to_hex() {
|
fn scriptbuf_to_hex() {
|
||||||
let script = ScriptBuf::from_bytes(vec![0xa1, 0xb2, 0xc3]);
|
let script = ScriptBuf::from_bytes(vec![0xa1, 0xb2, 0xc3]);
|
||||||
let hex = script.to_hex();
|
let hex = script.to_hex();
|
||||||
assert_eq!(hex, "03a1b2c3");
|
assert_eq!(hex, "a1b2c3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue