From f4bc58dc481af8198df121cdc45f2e8daafef822 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 8 Apr 2025 03:58:33 +1000 Subject: [PATCH 1/4] bitcoin: Add a script encoding example Our encoding/decoding support for scripts is currently a bit convoluted. Add an example that shows how all the APIs go together. Note this highlights a potential problem. `to_hex_string` is in `bitcoin` and therefore has access to consensus encoding but does not include the length prefix. This means that the output string from `to_hex_string` cannot be parsed with `consensus::encode::deserialize_hex` - which is surprising and has lead to difficulty by at least one user trying to debug. --- bitcoin/Cargo.toml | 4 +++ bitcoin/examples/script.rs | 61 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 bitcoin/examples/script.rs diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index 9e28cfb5c..075ddbd89 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -94,5 +94,9 @@ name = "sighash" name = "io" required-features = ["std"] +[[example]] +name = "script" +required-features = ["std"] + [lints.rust] unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(fuzzing)', 'cfg(kani)'] } diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs new file mode 100644 index 000000000..bff6e53c9 --- /dev/null +++ b/bitcoin/examples/script.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: CC0-1.0 + +use bitcoin::consensus::{encode, FromHexError}; +use bitcoin::key::WPubkeyHash; +use bitcoin::script::{self, ScriptExt, ScriptBufExt}; +use bitcoin::ScriptBuf; + +fn main() { + let pk = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".parse::().unwrap(); + + let script_code = script::p2wpkh_script_code(pk); + let hex = script_code.to_hex_string(); + let parsed = ScriptBuf::from_hex(&hex).unwrap(); + assert_eq!(parsed, script_code); + + // Writes the script as human-readable eg, OP_DUP OP_HASH160 OP_PUSHBYTES_20 ... + let _human_readable = format!("{}", script_code); + // We do not implement parsing scripts from human-readable format. + // let parsed = s.parse::().unwrap(); + + // This does not include the length prefix. + let hex_lower_hex_trait = format!("{:x}", script_code); + println!("hex created using `LowerHex`: {}", hex_lower_hex_trait); + // But `deserialize_hex` requires the length prefix. + let decoded: Result = encode::deserialize_hex(&hex_lower_hex_trait); + assert!(decoded.is_err()); + + // FIXME: The inherent functions are in `bitcoin` so have access to consensus encoding. + // This also does not include the length prefix. + let hex_inherent = script_code.to_hex_string(); + println!("hex created using inherent `to_hex_string`: {}", hex_inherent); + let parsed = ScriptBuf::from_hex(&hex_inherent).unwrap(); + assert_eq!(parsed, script_code); + + // Cannot parse the output of `to_hex_string` using `deserialize_hex` because no length prefix. + assert!(encode::deserialize_hex::(&hex_inherent).is_err()); + + // Encode/decode using `consensus::encode` functions. + // This does include the length prefix. + let encoded = encode::serialize_hex(&script_code); + println!("hex created using consensus::encode::serialize_hex: {}", encoded); + let decoded: ScriptBuf = encode::deserialize_hex(&encoded).unwrap(); + assert_eq!(decoded, script_code); + + // And we cannot mix these to calls because `serialize_hex` includes the length prefix + // but `from_hex` expects no length prefix. + let encoded = encode::serialize_hex(&script_code); + let decoded = ScriptBuf::from_hex(&encoded).unwrap(); + assert_ne!(decoded, script_code); + + // Encode/decode using a byte vector. + let encoded = encode::serialize(&script_code); + assert_eq!(&encoded[1..], script_code.as_bytes()); // Shows that prefix is the first byte. + let decoded: ScriptBuf = encode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, script_code); + + // to/from bytes excludes the prefix, these are not encoding/decoding functions so this is sane. + let bytes = script_code.to_bytes(); + let got = ScriptBuf::from_bytes(bytes); + assert_eq!(got, script_code); +} From c27b95fb0dd0e5600d1f236a141bcac452306a5c Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 14 Apr 2025 12:06:09 +1000 Subject: [PATCH 2/4] Make script to/from hex use consensus encoding I'm not sure why we do not use consensus encoding currently for encoding and decoding scripts to/from hex strings. Many tests include hard coded hex which do not include the length prefix. - Add a pair of encoding functions to encode/decode to/from hex without the length prefix. - Make `to_hex` and `from_hex` expect the length prefix i.e., use consensus encoding. This makes the API easier to use because the various encoding APIs can be use together now eg `consensus::encode_hex` and `ScriptBuf::from_hex`. --- bitcoin/examples/ecdsa-psbt.rs | 2 +- bitcoin/examples/script.rs | 52 +++++---- bitcoin/examples/taproot-psbt.rs | 6 +- bitcoin/src/address/mod.rs | 26 ++--- bitcoin/src/blockdata/script/borrowed.rs | 19 ++-- bitcoin/src/blockdata/script/owned.rs | 15 ++- bitcoin/src/blockdata/script/tests.rs | 138 +++++++++++------------ bitcoin/src/crypto/sighash.rs | 8 +- bitcoin/src/psbt/mod.rs | 48 ++++---- bitcoin/src/psbt/serialize.rs | 6 +- bitcoin/src/taproot/mod.rs | 28 ++--- bitcoin/tests/bip_174.rs | 6 +- bitcoin/tests/serde.rs | 6 +- 13 files changed, 192 insertions(+), 168 deletions(-) diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs index 5aa05ea97..85ffb878b 100644 --- a/bitcoin/examples/ecdsa-psbt.rs +++ b/bitcoin/examples/ecdsa-psbt.rs @@ -275,7 +275,7 @@ fn input_derivation_path() -> Result { } fn previous_output() -> TxOut { - let script_pubkey = ScriptBuf::from_hex(INPUT_UTXO_SCRIPT_PUBKEY) + let script_pubkey = ScriptBuf::from_hex_no_length_prefix(INPUT_UTXO_SCRIPT_PUBKEY) .expect("failed to parse input utxo scriptPubkey"); let amount = INPUT_UTXO_VALUE.parse::().expect("failed to parse input utxo value"); diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs index bff6e53c9..a7af632dc 100644 --- a/bitcoin/examples/script.rs +++ b/bitcoin/examples/script.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -use bitcoin::consensus::{encode, FromHexError}; +use bitcoin::consensus::encode; use bitcoin::key::WPubkeyHash; use bitcoin::script::{self, ScriptExt, ScriptBufExt}; use bitcoin::ScriptBuf; @@ -10,43 +10,51 @@ fn main() { let script_code = script::p2wpkh_script_code(pk); let hex = script_code.to_hex_string(); - let parsed = ScriptBuf::from_hex(&hex).unwrap(); - assert_eq!(parsed, script_code); + let decoded = ScriptBuf::from_hex(&hex).unwrap(); + assert_eq!(decoded, script_code); // Writes the script as human-readable eg, OP_DUP OP_HASH160 OP_PUSHBYTES_20 ... - let _human_readable = format!("{}", script_code); - // We do not implement parsing scripts from human-readable format. - // let parsed = s.parse::().unwrap(); + println!("human-readable script: {}", script_code); - // This does not include the length prefix. + // We do not implement parsing scripts from human-readable format. + // let decoded = s.parse::().unwrap(); + + // This is not equivalent to consensus encoding i.e., does not include the length prefix. let hex_lower_hex_trait = format!("{:x}", script_code); println!("hex created using `LowerHex`: {}", hex_lower_hex_trait); - // But `deserialize_hex` requires the length prefix. - let decoded: Result = encode::deserialize_hex(&hex_lower_hex_trait); - assert!(decoded.is_err()); - // FIXME: The inherent functions are in `bitcoin` so have access to consensus encoding. - // This also does not include the length prefix. - let hex_inherent = script_code.to_hex_string(); + // The `deserialize_hex` function requires the length prefix. + assert!(encode::deserialize_hex::(&hex_lower_hex_trait).is_err()); + // And so does `from_hex`. + assert!(ScriptBuf::from_hex(&hex_lower_hex_trait).is_err()); + // But we provide an explicit constructor that does not. + assert_eq!(ScriptBuf::from_hex_no_length_prefix(&hex_lower_hex_trait).unwrap(), script_code); + + // This is consensus encoding i.e., includes the length prefix. + let hex_inherent = script_code.to_hex_string(); // Defined in `ScriptExt`. println!("hex created using inherent `to_hex_string`: {}", hex_inherent); - let parsed = ScriptBuf::from_hex(&hex_inherent).unwrap(); - assert_eq!(parsed, script_code); - // Cannot parse the output of `to_hex_string` using `deserialize_hex` because no length prefix. - assert!(encode::deserialize_hex::(&hex_inherent).is_err()); + // The inverse of `to_hex_string` is `from_hex`. + let decoded = ScriptBuf::from_hex(&hex_inherent).unwrap(); // Defined in `ScriptBufExt`. + assert_eq!(decoded, script_code); + // We can also parse the output of `to_hex_string` using `deserialize_hex`. + let decoded = encode::deserialize_hex::(&hex_inherent).unwrap(); + assert_eq!(decoded, script_code); - // Encode/decode using `consensus::encode` functions. - // This does include the length prefix. + // We also support encode/decode using `consensus::encode` functions. let encoded = encode::serialize_hex(&script_code); println!("hex created using consensus::encode::serialize_hex: {}", encoded); + let decoded: ScriptBuf = encode::deserialize_hex(&encoded).unwrap(); assert_eq!(decoded, script_code); - // And we cannot mix these to calls because `serialize_hex` includes the length prefix - // but `from_hex` expects no length prefix. + let decoded = ScriptBuf::from_hex(&encoded).unwrap(); + assert_eq!(decoded, script_code); + + // And we can mix these to calls because both include the length prefix. let encoded = encode::serialize_hex(&script_code); let decoded = ScriptBuf::from_hex(&encoded).unwrap(); - assert_ne!(decoded, script_code); + assert_eq!(decoded, script_code); // Encode/decode using a byte vector. let encoded = encode::serialize(&script_code); diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs index 92af85156..ab8f8af4e 100644 --- a/bitcoin/examples/taproot-psbt.rs +++ b/bitcoin/examples/taproot-psbt.rs @@ -257,7 +257,7 @@ fn generate_bip86_key_spend_tx( let mut input = Input { witness_utxo: { - let script_pubkey = ScriptBuf::from_hex(input_utxo.script_pubkey) + let script_pubkey = ScriptBuf::from_hex_no_length_prefix(input_utxo.script_pubkey) .expect("failed to parse input utxo scriptPubkey"); Some(TxOut { value: from_amount, script_pubkey }) }, @@ -275,7 +275,7 @@ fn generate_bip86_key_spend_tx( for input in [&input_utxo].iter() { input_txouts.push(TxOut { value: input.amount, - script_pubkey: ScriptBuf::from_hex(input.script_pubkey)?, + script_pubkey: ScriptBuf::from_hex_no_length_prefix(input.script_pubkey)?, }); } @@ -333,7 +333,7 @@ fn generate_bip86_key_spend_tx( tx.verify(|_| { Some(TxOut { value: from_amount, - script_pubkey: ScriptBuf::from_hex(input_utxo.script_pubkey).unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix(input_utxo.script_pubkey).unwrap(), }) }) .expect("failed to verify transaction"); diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 167de2292..b49213a23 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -1053,7 +1053,7 @@ mod tests { assert_eq!( addr.script_pubkey(), - ScriptBuf::from_hex("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() + ScriptBuf::from_hex_no_length_prefix("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() ); assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); assert_eq!(addr.address_type(), Some(AddressType::P2pkh)); @@ -1082,7 +1082,7 @@ mod tests { assert_eq!( addr.script_pubkey(), - ScriptBuf::from_hex("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap(), + ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap(), ); assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); assert_eq!(addr.address_type(), Some(AddressType::P2sh)); @@ -1091,7 +1091,7 @@ mod tests { #[test] fn p2sh_parse() { - let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae").unwrap(); let addr = Address::p2sh(&script, NetworkKind::Test).unwrap(); assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"); assert_eq!(addr.address_type(), Some(AddressType::P2sh)); @@ -1100,7 +1100,7 @@ mod tests { #[test] fn p2sh_parse_for_large_script() { - let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap(); let res = Address::p2sh(&script, NetworkKind::Test); assert_eq!(res.unwrap_err().invalid_size(), script.len()) } @@ -1120,7 +1120,7 @@ mod tests { #[test] fn p2wsh() { // stolen from Bitcoin transaction 5df912fda4becb1c29e928bec8d64d93e9ba8efa9b5b405bd683c86fd2c65667 - let script = ScriptBuf::from_hex("52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae").unwrap(); let addr = Address::p2wsh(&script, KnownHrp::Mainnet).expect("script is valid"); assert_eq!( &addr.to_string(), @@ -1145,7 +1145,7 @@ mod tests { #[test] fn p2shwsh() { // stolen from Bitcoin transaction f9ee2be4df05041d0e0a35d7caa3157495ca4f93b233234c9967b6901dacf7a9 - let script = ScriptBuf::from_hex("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap(); let addr = Address::p2shwsh(&script, NetworkKind::Main).expect("script is valid"); assert_eq!(&addr.to_string(), "36EqgNnsWW94SreZgBWc1ANC6wpFZwirHr"); assert_eq!(addr.address_type(), Some(AddressType::P2sh)); @@ -1234,7 +1234,7 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() + ScriptBuf::from_hex_no_length_prefix("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() ); let addr = @@ -1248,7 +1248,7 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap() + ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap() ); let addr: Address = @@ -1278,7 +1278,7 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex( + ScriptBuf::from_hex_no_length_prefix( "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262" ) .unwrap() @@ -1297,7 +1297,7 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex("001454d26dddb59c7073c6a197946ea1841951fa7a74").unwrap() + ScriptBuf::from_hex_no_length_prefix("001454d26dddb59c7073c6a197946ea1841951fa7a74").unwrap() ); } @@ -1476,13 +1476,13 @@ mod tests { fn fail_address_from_script() { use crate::witness_program; - let bad_p2wpkh = ScriptBuf::from_hex("0014dbc5b0a8f9d4353b4b54c3db48846bb15abfec").unwrap(); - let bad_p2wsh = ScriptBuf::from_hex( + let bad_p2wpkh = ScriptBuf::from_hex_no_length_prefix("15000014dbc5b0a8f9d4353b4b54c3db48846bb15abfec").unwrap(); + let bad_p2wsh = ScriptBuf::from_hex_no_length_prefix( "00202d4fa2eb233d008cc83206fa2f4f2e60199000f5b857a835e3172323385623", ) .unwrap(); let invalid_segwitv0_script = - ScriptBuf::from_hex("001161458e330389cd0437ee9fe3641d70cc18").unwrap(); + ScriptBuf::from_hex_no_length_prefix("001161458e330389cd0437ee9fe3641d70cc18").unwrap(); let expected = Err(FromScriptError::UnrecognizedScript); assert_eq!(Address::from_script(&bad_p2wpkh, Network::Bitcoin), expected); diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs index 481145e8d..cb8ded5af 100644 --- a/bitcoin/src/blockdata/script/borrowed.rs +++ b/bitcoin/src/blockdata/script/borrowed.rs @@ -2,6 +2,7 @@ use core::fmt; +use hex::DisplayHex as _; use internals::ToU64 as _; use super::witness_version::WitnessVersion; @@ -9,11 +10,11 @@ use super::{ Builder, Instruction, InstructionIndices, Instructions, PushBytes, RedeemScriptSizeError, ScriptHash, WScriptHash, WitnessScriptSizeError, }; -use crate::consensus::Encodable; +use crate::consensus::{self, Encodable}; use crate::opcodes::all::*; use crate::opcodes::{self, Opcode}; use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY}; -use crate::prelude::{sink, DisplayHex, String, ToString}; +use crate::prelude::{sink, String, ToString}; use crate::taproot::{LeafVersion, TapLeafHash}; use crate::{Amount, FeeRate}; @@ -374,12 +375,16 @@ crate::internal_macros::define_extension_trait! { #[deprecated(since = "TBD", note = "use `to_string()` instead")] fn to_asm_string(&self) -> String { self.to_string() } - /// Formats 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) } + + /// Consensus encodes the script as lower-case hex. /// - /// This is a more convenient and performant way to write `format!("{:x}", script)`. - /// For better performance you should generally prefer displaying the script but if `String` is - /// required (this is common in tests) this method can be used. - fn to_hex_string(&self) -> String { self.as_bytes().to_lower_hex_string() } + /// This is **not** consensus encoding, you likely want to use `to_hex_string`. The returned + /// hex string will not include the length prefix. + fn to_hex_string_no_length_prefix(&self) -> String { + self.as_bytes().to_lower_hex_string() + } /// Returns the first opcode of the script (if there is any). fn first_opcode(&self) -> Option { diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index 7d4c9b978..9a466276f 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -3,13 +3,14 @@ #[cfg(doc)] use core::ops::Deref; -use hex::FromHex; +use hex::FromHex as _; use internals::ToU64 as _; use super::{opcode_to_verify, Builder, Instruction, PushBytes, ScriptExtPriv as _}; use crate::opcodes::all::*; use crate::opcodes::{self, Opcode}; use crate::prelude::Vec; +use crate::consensus; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] @@ -27,7 +28,17 @@ crate::internal_macros::define_extension_trait! { } /// Constructs a new [`ScriptBuf`] from a hex string. - fn from_hex(s: &str) -> Result { + /// + /// The input string is expected to be consensus encoded i.e., includes the length prefix. + fn from_hex(s: &str) -> Result { + consensus::encode::deserialize_hex(s) + } + + /// Constructs a new [`ScriptBuf`] from a hex string. + /// + /// This is **not** consensus encoding. If your hex string is a consensus encode script then + /// use `ScriptBuf::from_hex`. + fn from_hex_no_length_prefix(s: &str) -> Result { let v = Vec::from_hex(s)?; Ok(ScriptBuf::from_bytes(v)) } diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index fe2c75f84..1b29c77d2 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -203,7 +203,7 @@ fn script_builder() { .push_opcode(OP_EQUALVERIFY) .push_opcode(OP_CHECKSIG) .into_script(); - assert_eq!(script.to_hex_string(), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac"); + assert_eq!(script.to_hex_string_no_length_prefix(), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac"); } #[test] @@ -236,7 +236,7 @@ fn script_generators() { let op_return = ScriptBuf::new_op_return(data); assert!(op_return.is_op_return()); assert_eq!( - op_return.to_hex_string(), + op_return.to_hex_string_no_length_prefix(), "6a24aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a" ); } @@ -244,42 +244,42 @@ fn script_generators() { #[test] fn script_builder_verify() { let simple = Builder::new().push_verify().into_script(); - assert_eq!(simple.to_hex_string(), "69"); + assert_eq!(simple.to_hex_string_no_length_prefix(), "69"); let simple2 = Builder::from(vec![]).push_verify().into_script(); - assert_eq!(simple2.to_hex_string(), "69"); + assert_eq!(simple2.to_hex_string_no_length_prefix(), "69"); let nonverify = Builder::new().push_verify().push_verify().into_script(); - assert_eq!(nonverify.to_hex_string(), "6969"); + assert_eq!(nonverify.to_hex_string_no_length_prefix(), "6969"); let nonverify2 = Builder::from(vec![0x69]).push_verify().into_script(); - assert_eq!(nonverify2.to_hex_string(), "6969"); + assert_eq!(nonverify2.to_hex_string_no_length_prefix(), "6969"); let equal = Builder::new().push_opcode(OP_EQUAL).push_verify().into_script(); - assert_eq!(equal.to_hex_string(), "88"); + assert_eq!(equal.to_hex_string_no_length_prefix(), "88"); let equal2 = Builder::from(vec![0x87]).push_verify().into_script(); - assert_eq!(equal2.to_hex_string(), "88"); + assert_eq!(equal2.to_hex_string_no_length_prefix(), "88"); let numequal = Builder::new().push_opcode(OP_NUMEQUAL).push_verify().into_script(); - assert_eq!(numequal.to_hex_string(), "9d"); + assert_eq!(numequal.to_hex_string_no_length_prefix(), "9d"); let numequal2 = Builder::from(vec![0x9c]).push_verify().into_script(); - assert_eq!(numequal2.to_hex_string(), "9d"); + assert_eq!(numequal2.to_hex_string_no_length_prefix(), "9d"); let checksig = Builder::new().push_opcode(OP_CHECKSIG).push_verify().into_script(); - assert_eq!(checksig.to_hex_string(), "ad"); + assert_eq!(checksig.to_hex_string_no_length_prefix(), "ad"); let checksig2 = Builder::from(vec![0xac]).push_verify().into_script(); - assert_eq!(checksig2.to_hex_string(), "ad"); + assert_eq!(checksig2.to_hex_string_no_length_prefix(), "ad"); let checkmultisig = Builder::new().push_opcode(OP_CHECKMULTISIG).push_verify().into_script(); - assert_eq!(checkmultisig.to_hex_string(), "af"); + assert_eq!(checkmultisig.to_hex_string_no_length_prefix(), "af"); let checkmultisig2 = Builder::from(vec![0xae]).push_verify().into_script(); - assert_eq!(checkmultisig2.to_hex_string(), "af"); + assert_eq!(checkmultisig2.to_hex_string_no_length_prefix(), "af"); let trick_slice = Builder::new() .push_slice([0xae]) // OP_CHECKMULTISIG .push_verify() .into_script(); - assert_eq!(trick_slice.to_hex_string(), "01ae69"); + assert_eq!(trick_slice.to_hex_string_no_length_prefix(), "01ae69"); let trick_slice2 = Builder::from(vec![0x01, 0xae]).push_verify().into_script(); - assert_eq!(trick_slice2.to_hex_string(), "01ae69"); + assert_eq!(trick_slice2.to_hex_string_no_length_prefix(), "01ae69"); } #[test] @@ -373,7 +373,7 @@ fn non_minimal_scriptints() { #[test] fn script_hashes() { - let script = ScriptBuf::from_hex("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac").unwrap(); assert_eq!( script.script_hash().unwrap().to_string(), "8292bcfbef1884f73c813dfe9c82fd7e814291ea" @@ -383,7 +383,7 @@ fn script_hashes() { "3e1525eb183ad4f9b3c5fa3175bdca2a52e947b135bbb90383bf9f6408e2c324" ); assert_eq!( - ScriptBuf::from_hex("20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac") + ScriptBuf::from_hex_no_length_prefix("20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac") .unwrap() .tapscript_leaf_hash() .to_string(), @@ -394,40 +394,40 @@ fn script_hashes() { #[test] fn provably_unspendable() { // p2pk - assert!(!ScriptBuf::from_hex("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac").unwrap().is_op_return()); - assert!(!ScriptBuf::from_hex("4104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac").unwrap().is_op_return()); + assert!(!ScriptBuf::from_hex_no_length_prefix("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac").unwrap().is_op_return()); + assert!(!ScriptBuf::from_hex_no_length_prefix("4104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac").unwrap().is_op_return()); // p2pkhash - assert!(!ScriptBuf::from_hex("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") + assert!(!ScriptBuf::from_hex_no_length_prefix("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") .unwrap() .is_op_return()); - assert!(ScriptBuf::from_hex("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") + assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") .unwrap() .is_op_return()); } #[test] fn op_return() { - assert!(ScriptBuf::from_hex("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") + assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") .unwrap() .is_op_return()); - assert!(!ScriptBuf::from_hex("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") + assert!(!ScriptBuf::from_hex_no_length_prefix("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") .unwrap() .is_op_return()); - assert!(!ScriptBuf::from_hex("").unwrap().is_op_return()); + assert!(!ScriptBuf::from_hex_no_length_prefix("").unwrap().is_op_return()); } #[test] fn standard_op_return() { - assert!(ScriptBuf::from_hex("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") + assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") .unwrap() .is_standard_op_return()); - assert!(ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e") + assert!(ScriptBuf::from_hex_no_length_prefix("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e") .unwrap() .is_standard_op_return()); - assert!(ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21") + assert!(ScriptBuf::from_hex_no_length_prefix("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21") .unwrap() .is_standard_op_return()); - assert!(!ScriptBuf::from_hex("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21524f42") + assert!(!ScriptBuf::from_hex_no_length_prefix("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e21524f42") .unwrap() .is_standard_op_return()); } @@ -437,44 +437,44 @@ fn multisig() { // First multisig? 1-of-2 // In block 164467, txid 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1 assert!( - ScriptBuf::from_hex("514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae") + ScriptBuf::from_hex_no_length_prefix("514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae") .unwrap() .is_multisig() ); // 2-of-2 assert!( - ScriptBuf::from_hex("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae") + ScriptBuf::from_hex_no_length_prefix("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae") .unwrap() .is_multisig() ); // Extra opcode after OP_CHECKMULTISIG assert!( - !ScriptBuf::from_hex("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae52") + !ScriptBuf::from_hex_no_length_prefix("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae52") .unwrap() .is_multisig() ); // Required sigs > num pubkeys assert!( - !ScriptBuf::from_hex("5321021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae") + !ScriptBuf::from_hex_no_length_prefix("5321021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f452ae") .unwrap() .is_multisig() ); // Num pubkeys != pushnum assert!( - !ScriptBuf::from_hex("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f453ae") + !ScriptBuf::from_hex_no_length_prefix("5221021c4ac2ecebc398e390e07f045aac5cc421f82f0739c1ce724d3d53964dc6537d21023a2e9155e0b62f76737605504819a2b4e5ce20653f6c397d7a178ae42ba702f453ae") .unwrap() .is_multisig() ); // Taproot hash from another test - assert!(!ScriptBuf::from_hex( + assert!(!ScriptBuf::from_hex_no_length_prefix( "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac" ) .unwrap() .is_multisig()); // OP_RETURN from another test - assert!(!ScriptBuf::from_hex("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") + assert!(!ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") .unwrap() .is_multisig()); } @@ -484,7 +484,7 @@ fn multisig() { fn script_json_serialize() { use serde_json; - let original = ScriptBuf::from_hex("827651a0698faaa9a8a7a687").unwrap(); + let original = ScriptBuf::from_hex_no_length_prefix("827651a0698faaa9a8a7a687").unwrap(); let json = serde_json::to_value(&original).unwrap(); assert_eq!(json, serde_json::Value::String("827651a0698faaa9a8a7a687".to_owned())); let des = serde_json::from_value::(json).unwrap(); @@ -494,27 +494,27 @@ fn script_json_serialize() { #[test] fn script_asm() { assert_eq!( - ScriptBuf::from_hex("6363636363686868686800").unwrap().to_string(), + ScriptBuf::from_hex_no_length_prefix("6363636363686868686800").unwrap().to_string(), "OP_IF OP_IF OP_IF OP_IF OP_IF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_0" ); - assert_eq!(ScriptBuf::from_hex("2102715e91d37d239dea832f1460e91e368115d8ca6cc23a7da966795abad9e3b699ac").unwrap().to_string(), + assert_eq!(ScriptBuf::from_hex_no_length_prefix("2102715e91d37d239dea832f1460e91e368115d8ca6cc23a7da966795abad9e3b699ac").unwrap().to_string(), "OP_PUSHBYTES_33 02715e91d37d239dea832f1460e91e368115d8ca6cc23a7da966795abad9e3b699 OP_CHECKSIG"); // Elements Alpha peg-out transaction with some signatures removed for brevity. Mainly to test PUSHDATA1 - assert_eq!(ScriptBuf::from_hex("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap().to_string(), + assert_eq!(ScriptBuf::from_hex_no_length_prefix("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap().to_string(), "OP_0 OP_PUSHBYTES_71 304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401 OP_0 OP_PUSHDATA1 552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae"); // Various weird scripts found in transaction 6d7ed9914625c73c0288694a6819196a27ef6c08f98e1270d975a8e65a3dc09a // which triggered overflow bugs on 32-bit machines in script formatting in the past. - assert_eq!(ScriptBuf::from_hex("01").unwrap().to_string(), "OP_PUSHBYTES_1 "); - assert_eq!(ScriptBuf::from_hex("0201").unwrap().to_string(), "OP_PUSHBYTES_2 "); - assert_eq!(ScriptBuf::from_hex("4c").unwrap().to_string(), ""); - assert_eq!(ScriptBuf::from_hex("4c0201").unwrap().to_string(), "OP_PUSHDATA1 "); - assert_eq!(ScriptBuf::from_hex("4d").unwrap().to_string(), ""); + assert_eq!(ScriptBuf::from_hex_no_length_prefix("01").unwrap().to_string(), "OP_PUSHBYTES_1 "); + assert_eq!(ScriptBuf::from_hex_no_length_prefix("0201").unwrap().to_string(), "OP_PUSHBYTES_2 "); + assert_eq!(ScriptBuf::from_hex_no_length_prefix("4c").unwrap().to_string(), ""); + assert_eq!(ScriptBuf::from_hex_no_length_prefix("4c0201").unwrap().to_string(), "OP_PUSHDATA1 "); + assert_eq!(ScriptBuf::from_hex_no_length_prefix("4d").unwrap().to_string(), ""); assert_eq!( - ScriptBuf::from_hex("4dffff01").unwrap().to_string(), + ScriptBuf::from_hex_no_length_prefix("4dffff01").unwrap().to_string(), "OP_PUSHDATA2 " ); assert_eq!( - ScriptBuf::from_hex("4effffffff01").unwrap().to_string(), + ScriptBuf::from_hex_no_length_prefix("4effffffff01").unwrap().to_string(), "OP_PUSHDATA4 " ); } @@ -522,69 +522,69 @@ fn script_asm() { #[test] fn script_buf_collect() { assert_eq!(&core::iter::empty::>().collect::(), Script::new()); - let script = ScriptBuf::from_hex("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap(); assert_eq!(script.instructions().collect::>().unwrap(), script); } #[test] fn script_p2sh_p2p2k_template() { // random outputs I picked out of the mempool - assert!(ScriptBuf::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") + assert!(ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") .unwrap() .is_p2pkh()); - assert!(!ScriptBuf::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") + assert!(!ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") .unwrap() .is_p2sh()); - assert!(!ScriptBuf::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ad") + assert!(!ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ad") .unwrap() .is_p2pkh()); - assert!(!ScriptBuf::from_hex("").unwrap().is_p2pkh()); - assert!(ScriptBuf::from_hex("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") + assert!(!ScriptBuf::from_hex_no_length_prefix("").unwrap().is_p2pkh()); + assert!(ScriptBuf::from_hex_no_length_prefix("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") .unwrap() .is_p2sh()); - assert!(!ScriptBuf::from_hex("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") + assert!(!ScriptBuf::from_hex_no_length_prefix("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") .unwrap() .is_p2pkh()); - assert!(!ScriptBuf::from_hex("a314acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") + assert!(!ScriptBuf::from_hex_no_length_prefix("a314acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") .unwrap() .is_p2sh()); } #[test] fn script_p2pk() { - assert!(ScriptBuf::from_hex( + assert!(ScriptBuf::from_hex_no_length_prefix( "21021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac" ) .unwrap() .is_p2pk()); - assert!(ScriptBuf::from_hex("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac").unwrap().is_p2pk()); + assert!(ScriptBuf::from_hex_no_length_prefix("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac").unwrap().is_p2pk()); } #[test] fn p2sh_p2wsh_conversion() { // Test vectors taken from Core tests/data/script_tests.json // bare p2wsh - let witness_script = ScriptBuf::from_hex("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); + let witness_script = ScriptBuf::from_hex_no_length_prefix("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); let expected_without = - ScriptBuf::from_hex("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") + ScriptBuf::from_hex_no_length_prefix("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") .unwrap(); assert!(witness_script.to_p2wsh().unwrap().is_p2wsh()); assert_eq!(witness_script.to_p2wsh().unwrap(), expected_without); // p2sh - let redeem_script = ScriptBuf::from_hex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8").unwrap(); + let redeem_script = ScriptBuf::from_hex_no_length_prefix("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8").unwrap(); let expected_p2shout = - ScriptBuf::from_hex("a91491b24bf9f5288532960ac687abb035127b1d28a587").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a91491b24bf9f5288532960ac687abb035127b1d28a587").unwrap(); assert!(redeem_script.to_p2sh().unwrap().is_p2sh()); assert_eq!(redeem_script.to_p2sh().unwrap(), expected_p2shout); // p2sh-p2wsh - let witness_script = ScriptBuf::from_hex("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); + let witness_script = ScriptBuf::from_hex_no_length_prefix("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); let expected_without = - ScriptBuf::from_hex("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") + ScriptBuf::from_hex_no_length_prefix("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") .unwrap(); let expected_out = - ScriptBuf::from_hex("a914f386c2ba255cc56d20cfa6ea8b062f8b5994551887").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a914f386c2ba255cc56d20cfa6ea8b062f8b5994551887").unwrap(); assert!(witness_script.to_p2sh().unwrap().is_p2sh()); assert_eq!(witness_script.to_p2wsh().unwrap(), expected_without); assert_eq!(witness_script.to_p2wsh().unwrap().to_p2sh().unwrap(), expected_out); @@ -600,12 +600,12 @@ macro_rules! unwrap_all { #[test] fn iterator() { - let zero = ScriptBuf::from_hex("00").unwrap(); - let zeropush = ScriptBuf::from_hex("0100").unwrap(); + let zero = ScriptBuf::from_hex_no_length_prefix("00").unwrap(); + let zeropush = ScriptBuf::from_hex_no_length_prefix("0100").unwrap(); - let nonminimal = ScriptBuf::from_hex("4c0169b2").unwrap(); // PUSHDATA1 for no reason - let minimal = ScriptBuf::from_hex("0169b2").unwrap(); // minimal - let nonminimal_alt = ScriptBuf::from_hex("026900b2").unwrap(); // non-minimal number but minimal push (should be OK) + let nonminimal = ScriptBuf::from_hex_no_length_prefix("4c0169b2").unwrap(); // PUSHDATA1 for no reason + let minimal = ScriptBuf::from_hex_no_length_prefix("0169b2").unwrap(); // minimal + let nonminimal_alt = ScriptBuf::from_hex_no_length_prefix("026900b2").unwrap(); // non-minimal number but minimal push (should be OK) let v_zero: Result, Error> = zero.instruction_indices_minimal().collect(); let v_zeropush: Result, Error> = zeropush.instruction_indices_minimal().collect(); diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index b91816768..6584d66bf 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -1832,7 +1832,7 @@ mod tests { let leaf_hash = match (script_hex, script_leaf_hash) { (Some(script_hex), _) => { - let script_inner = ScriptBuf::from_hex(script_hex).unwrap(); + let script_inner = ScriptBuf::from_hex_no_length_prefix(script_hex).unwrap(); Some(ScriptPath::with_defaults(&script_inner).leaf_hash()) } (_, Some(script_leaf_hash)) => Some(script_leaf_hash.parse::().unwrap()), @@ -2089,7 +2089,7 @@ mod tests { ), ).unwrap(); - let spk = ScriptBuf::from_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1").unwrap(); + let spk = ScriptBuf::from_hex_no_length_prefix("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1").unwrap(); let value = Amount::from_sat_u32(600_000_000); let mut cache = SighashCache::new(&tx); @@ -2130,7 +2130,7 @@ mod tests { ).unwrap(); let redeem_script = - ScriptBuf::from_hex("001479091972186c449eb1ded22b78e40d009bdf0089").unwrap(); + ScriptBuf::from_hex_no_length_prefix("001479091972186c449eb1ded22b78e40d009bdf0089").unwrap(); let value = Amount::from_sat_u32(1_000_000_000); let mut cache = SighashCache::new(&tx); @@ -2171,7 +2171,7 @@ mod tests { )) .unwrap(); - let witness_script = ScriptBuf::from_hex( + let witness_script = ScriptBuf::from_hex_no_length_prefix( "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28\ bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b\ 9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58\ diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 9b3dfad59..33cfd42c2 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -1336,7 +1336,7 @@ mod tests { }], output: vec![TxOut { value: Amount::from_sat(output).unwrap(), - script_pubkey: ScriptBuf::from_hex( + script_pubkey: ScriptBuf::from_hex_no_length_prefix( "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787", ) .unwrap(), @@ -1350,7 +1350,7 @@ mod tests { inputs: vec![Input { witness_utxo: Some(TxOut { value: Amount::from_sat(input).unwrap(), - script_pubkey: ScriptBuf::from_hex( + script_pubkey: ScriptBuf::from_hex_no_length_prefix( "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587", ) .unwrap(), @@ -1463,10 +1463,10 @@ mod tests { let expected: Output = Output { redeem_script: Some( - ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), + ScriptBuf::from_hex_no_length_prefix("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), ), witness_script: Some( - ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), + ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), ), bip32_derivation: hd_keypaths, ..Default::default() @@ -1497,14 +1497,14 @@ mod tests { output: vec![ TxOut { value: Amount::from_sat_u32(99_999_699), - script_pubkey: ScriptBuf::from_hex( + script_pubkey: ScriptBuf::from_hex_no_length_prefix( "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac", ) .unwrap(), }, TxOut { value: Amount::from_sat_u32(100_000_000), - script_pubkey: ScriptBuf::from_hex( + script_pubkey: ScriptBuf::from_hex_no_length_prefix( "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787", ) .unwrap(), @@ -1561,7 +1561,7 @@ mod tests { .unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985") + script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985") .unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[hex!( @@ -1570,7 +1570,7 @@ mod tests { }], output: vec![TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex( + script_pubkey: ScriptBuf::from_hex_no_length_prefix( "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587", ) .unwrap(), @@ -1621,7 +1621,7 @@ mod tests { non_witness_utxo: Some(tx), witness_utxo: Some(TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), }), sighash_type: Some("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse::().unwrap()), redeem_script: Some(vec![0x51].into()), @@ -1746,11 +1746,11 @@ mod tests { output: vec![ TxOut { value: Amount::from_sat_u32(99_999_699), - script_pubkey: ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), }, TxOut { value: Amount::from_sat_u32(100_000_000), - script_pubkey: ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), }, ], }, @@ -1770,7 +1770,7 @@ mod tests { txid: "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389".parse().unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985").unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985").unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[ hex!("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").as_slice(), @@ -1782,7 +1782,7 @@ mod tests { txid: "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886".parse().unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014fe3e9ef1a745e974d902c4355943abcb34bd5353").unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix("160014fe3e9ef1a745e974d902c4355943abcb34bd5353").unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[ hex!("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").as_slice(), @@ -1793,11 +1793,11 @@ mod tests { output: vec![ TxOut { value: Amount::from_sat_u32(200_000_000), - script_pubkey: ScriptBuf::from_hex("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac").unwrap(), }, TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), }, ], }), @@ -1839,7 +1839,7 @@ mod tests { let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap(); let expected_out = - ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); assert!(redeem_script.is_p2wpkh()); assert_eq!( @@ -1885,7 +1885,7 @@ mod tests { let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap(); let expected_out = - ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); assert!(redeem_script.is_p2wpkh()); assert_eq!( @@ -1910,7 +1910,7 @@ mod tests { let redeem_script = psbt.inputs[0].redeem_script.as_ref().unwrap(); let expected_out = - ScriptBuf::from_hex("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87").unwrap(); assert!(redeem_script.is_p2wsh()); assert_eq!( @@ -2079,11 +2079,11 @@ mod tests { output: vec![ TxOut { value: Amount::from_sat_u32(99_999_699), - script_pubkey: ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), }, TxOut { value: Amount::from_sat_u32(100_000_000), - script_pubkey: ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), }, ], }, @@ -2103,7 +2103,7 @@ mod tests { txid: "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389".parse().unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985").unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985").unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[ hex!("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").as_slice(), @@ -2115,7 +2115,7 @@ mod tests { txid: "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886".parse().unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014fe3e9ef1a745e974d902c4355943abcb34bd5353").unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix("160014fe3e9ef1a745e974d902c4355943abcb34bd5353").unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[ hex!("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").as_slice(), @@ -2126,11 +2126,11 @@ mod tests { output: vec![ TxOut { value: Amount::from_sat_u32(200_000_000), - script_pubkey: ScriptBuf::from_hex("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac").unwrap(), }, TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), }, ], }), diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs index 25eef99c6..bc582a147 100644 --- a/bitcoin/src/psbt/serialize.rs +++ b/bitcoin/src/psbt/serialize.rs @@ -414,7 +414,7 @@ mod tests { let mut val = opcode; let mut builder = TaprootBuilder::new(); for depth in depth_map { - let script = ScriptBuf::from_hex(&format!("{:02x}", val)).unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix(&format!("{:02x}", val)).unwrap(); builder = builder.add_leaf(*depth, script).unwrap(); let (new_val, _) = val.overflowing_add(1); val = new_val; @@ -429,7 +429,7 @@ mod tests { builder = builder .add_leaf_with_ver( 3, - ScriptBuf::from_hex("b9").unwrap(), + ScriptBuf::from_hex_no_length_prefix("b9").unwrap(), LeafVersion::from_consensus(0xC2).unwrap(), ) .unwrap(); @@ -443,7 +443,7 @@ mod tests { builder = builder .add_leaf_with_ver( 3, - ScriptBuf::from_hex("b9").unwrap(), + ScriptBuf::from_hex_no_length_prefix("b9").unwrap(), LeafVersion::from_consensus(0xC2).unwrap(), ) .unwrap(); diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index e4a60a35b..1323985e2 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1781,7 +1781,7 @@ mod test { ) { let out_pk = out_spk_hex[4..].parse::().unwrap(); let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk); - let script = ScriptBuf::from_hex(script_hex).unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix(script_hex).unwrap(); let control_block = ControlBlock::from_hex(control_block_hex).unwrap(); assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string()); assert!(control_block.verify_taproot_commitment(secp, out_pk.to_inner(), &script)); @@ -1847,11 +1847,11 @@ mod test { .unwrap(); let script_weights = [ - (10, ScriptBuf::from_hex("51").unwrap()), // semantics of script don't matter for this test - (20, ScriptBuf::from_hex("52").unwrap()), - (20, ScriptBuf::from_hex("53").unwrap()), - (30, ScriptBuf::from_hex("54").unwrap()), - (19, ScriptBuf::from_hex("55").unwrap()), + (10, ScriptBuf::from_hex_no_length_prefix("51").unwrap()), // semantics of script don't matter for this test + (20, ScriptBuf::from_hex_no_length_prefix("52").unwrap()), + (20, ScriptBuf::from_hex_no_length_prefix("53").unwrap()), + (30, ScriptBuf::from_hex_no_length_prefix("54").unwrap()), + (19, ScriptBuf::from_hex_no_length_prefix("55").unwrap()), ]; let tree_info = TaprootSpendInfo::with_huffman_tree(&secp, internal_key, script_weights.clone()) @@ -1872,7 +1872,7 @@ mod test { *length, tree_info .script_map - .get(&(ScriptBuf::from_hex(script).unwrap(), LeafVersion::TapScript)) + .get(&(ScriptBuf::from_hex_no_length_prefix(script).unwrap(), LeafVersion::TapScript)) .expect("Present Key") .iter() .next() @@ -1913,11 +1913,11 @@ mod test { // / \ / \ // A B C / \ // D E - let a = ScriptBuf::from_hex("51").unwrap(); - let b = ScriptBuf::from_hex("52").unwrap(); - let c = ScriptBuf::from_hex("53").unwrap(); - let d = ScriptBuf::from_hex("54").unwrap(); - let e = ScriptBuf::from_hex("55").unwrap(); + let a = ScriptBuf::from_hex_no_length_prefix("51").unwrap(); + let b = ScriptBuf::from_hex_no_length_prefix("52").unwrap(); + let c = ScriptBuf::from_hex_no_length_prefix("53").unwrap(); + let d = ScriptBuf::from_hex_no_length_prefix("54").unwrap(); + let e = ScriptBuf::from_hex_no_length_prefix("55").unwrap(); let builder = builder.add_leaf(2, a.clone()).unwrap(); let builder = builder.add_leaf(2, b.clone()).unwrap(); let builder = builder.add_leaf(2, c.clone()).unwrap(); @@ -2025,7 +2025,7 @@ mod test { builder = process_script_trees(leaf, builder, leaves, depth + 1); } } else { - let script = ScriptBuf::from_hex(v["script"].as_str().unwrap()).unwrap(); + let script = ScriptBuf::from_hex_no_length_prefix(v["script"].as_str().unwrap()).unwrap(); let ver = LeafVersion::from_consensus(v["leafVersion"].as_u64().unwrap() as u8).unwrap(); leaves.push((script.clone(), ver)); @@ -2080,7 +2080,7 @@ mod test { let expected_tweak = arr["intermediary"]["tweak"].as_str().unwrap().parse::().unwrap(); let expected_spk = - ScriptBuf::from_hex(arr["expected"]["scriptPubKey"].as_str().unwrap()).unwrap(); + ScriptBuf::from_hex_no_length_prefix(arr["expected"]["scriptPubKey"].as_str().unwrap()).unwrap(); let expected_addr = arr["expected"]["bip350Address"] .as_str() .unwrap() diff --git a/bitcoin/tests/bip_174.rs b/bitcoin/tests/bip_174.rs index 495658658..cda6bf895 100644 --- a/bitcoin/tests/bip_174.rs +++ b/bitcoin/tests/bip_174.rs @@ -22,7 +22,7 @@ fn hex_psbt(s: &str) -> Psbt { } #[track_caller] -fn hex_script(s: &str) -> ScriptBuf { ScriptBuf::from_hex(s).expect("valid hex digits") } +fn hex_script(s: &str) -> ScriptBuf { ScriptBuf::from_hex_no_length_prefix(s).expect("valid hex digits") } #[test] fn bip174_psbt_workflow() { @@ -181,13 +181,13 @@ fn create_transaction() -> Transaction { TxOut { value: Amount::from_str_in(output_0.amount, Denomination::Bitcoin) .expect("failed to parse amount"), - script_pubkey: ScriptBuf::from_hex(output_0.script_pubkey) + script_pubkey: ScriptBuf::from_hex_no_length_prefix(output_0.script_pubkey) .expect("failed to parse script"), }, TxOut { value: Amount::from_str_in(output_1.amount, Denomination::Bitcoin) .expect("failed to parse amount"), - script_pubkey: ScriptBuf::from_hex(output_1.script_pubkey) + script_pubkey: ScriptBuf::from_hex_no_length_prefix(output_1.script_pubkey) .expect("failed to parse script"), }, ], diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs index 25c89306c..fbcce5030 100644 --- a/bitcoin/tests/serde.rs +++ b/bitcoin/tests/serde.rs @@ -226,7 +226,7 @@ fn serde_regression_psbt() { .unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985") + script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985") .unwrap(), sequence: Sequence::from_consensus(4294967295), witness: Witness::from_slice(&[Vec::from_hex( @@ -236,7 +236,7 @@ fn serde_regression_psbt() { }], output: vec![TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587") + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587") .unwrap(), }], }; @@ -283,7 +283,7 @@ fn serde_regression_psbt() { non_witness_utxo: Some(tx), witness_utxo: Some(TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(), }), sighash_type: Some(PsbtSighashType::from("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse::().unwrap())), redeem_script: Some(vec![0x51].into()), From 2b72f1f30bdafa234cfc6525d6e7724598d40507 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 16 Apr 2025 18:10:15 +1000 Subject: [PATCH 3/4] Make Lower/Upper hex print scripts with len prefix Add the length prefix when formatting hex strings by way of `LowerHex` and `UpperHex`. This looses formatting options because I can't remember right now how not to - again. --- bitcoin/examples/script.rs | 26 ++++++++++++++++---------- primitives/src/script/mod.rs | 25 ++++++++++++++++--------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs index a7af632dc..e7195ecae 100644 --- a/bitcoin/examples/script.rs +++ b/bitcoin/examples/script.rs @@ -1,5 +1,12 @@ // SPDX-License-Identifier: CC0-1.0 +//! Demonstrates the API for parsing and formatting Bitcoin scripts. +//! +//! Bitcoin script is conceptually a vector of bytes. As such it is consensus encoded with a compact +//! size encoded length prefix. See [CompactSize]. +//! +//! [`CompactSize`]: + use bitcoin::consensus::encode; use bitcoin::key::WPubkeyHash; use bitcoin::script::{self, ScriptExt, ScriptBufExt}; @@ -8,6 +15,7 @@ use bitcoin::ScriptBuf; fn main() { let pk = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".parse::().unwrap(); + // TL;DR Use `to_hex_string` and `from_hex`. let script_code = script::p2wpkh_script_code(pk); let hex = script_code.to_hex_string(); let decoded = ScriptBuf::from_hex(&hex).unwrap(); @@ -19,16 +27,19 @@ fn main() { // We do not implement parsing scripts from human-readable format. // let decoded = s.parse::().unwrap(); - // This is not equivalent to consensus encoding i.e., does not include the length prefix. + // This is equivalent to consensus encoding i.e., includes the length prefix. let hex_lower_hex_trait = format!("{:x}", script_code); println!("hex created using `LowerHex`: {}", hex_lower_hex_trait); // The `deserialize_hex` function requires the length prefix. - assert!(encode::deserialize_hex::(&hex_lower_hex_trait).is_err()); + assert_eq!(encode::deserialize_hex::(&hex_lower_hex_trait).unwrap(), script_code); // And so does `from_hex`. - assert!(ScriptBuf::from_hex(&hex_lower_hex_trait).is_err()); - // But we provide an explicit constructor that does not. - assert_eq!(ScriptBuf::from_hex_no_length_prefix(&hex_lower_hex_trait).unwrap(), script_code); + assert_eq!(ScriptBuf::from_hex(&hex_lower_hex_trait).unwrap(), script_code); + + // And we also provide an explicit constructor that does not use the length prefix. + 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. let hex_inherent = script_code.to_hex_string(); // Defined in `ScriptExt`. @@ -47,12 +58,7 @@ fn main() { let decoded: ScriptBuf = encode::deserialize_hex(&encoded).unwrap(); assert_eq!(decoded, script_code); - - let decoded = ScriptBuf::from_hex(&encoded).unwrap(); - assert_eq!(decoded, script_code); - // And we can mix these to calls because both include the length prefix. - let encoded = encode::serialize_hex(&script_code); let decoded = ScriptBuf::from_hex(&encoded).unwrap(); assert_eq!(decoded, script_code); diff --git a/primitives/src/script/mod.rs b/primitives/src/script/mod.rs index 012b4f444..473b85cb1 100644 --- a/primitives/src/script/mod.rs +++ b/primitives/src/script/mod.rs @@ -431,9 +431,12 @@ impl fmt::Display for ScriptBuf { #[cfg(feature = "hex")] impl fmt::LowerHex for Script { + // Currently we drop all formatter options. #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f) + let compact = internals::compact_size::encode(self.as_bytes().len()); + write!(f, "{:x}", compact.as_slice().as_hex())?; + write!(f, "{:x}", self.as_bytes().as_hex()) } } #[cfg(feature = "alloc")] @@ -451,9 +454,12 @@ internals::impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &Self| script_buf #[cfg(feature = "hex")] impl fmt::UpperHex for Script { + // Currently we drop all formatter options. #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f) + let compact = internals::compact_size::encode(self.as_bytes().len()); + write!(f, "{:X}", compact.as_slice().as_hex())?; + write!(f, "{:X}", self.as_bytes().as_hex()) } } @@ -505,7 +511,8 @@ impl serde::Serialize for Script { S: serde::Serializer, { if serializer.is_human_readable() { - serializer.collect_str(&format_args!("{:x}", self)) + // Do not call LowerHex because we don't want to add the len prefix. + serializer.collect_str(&format_args!("{}", self.as_bytes().as_hex())) } else { serializer.serialize_bytes(self.as_bytes()) } @@ -796,8 +803,8 @@ mod tests { #[cfg(feature = "hex")] { - assert_eq!(format!("{:x}", script), "00a1b2"); - assert_eq!(format!("{:X}", script), "00A1B2"); + assert_eq!(format!("{:x}", script), "0300a1b2"); + assert_eq!(format!("{:X}", script), "0300A1B2"); } assert!(!format!("{:?}", script).is_empty()); } @@ -809,8 +816,8 @@ mod tests { #[cfg(feature = "hex")] { - assert_eq!(format!("{:x}", script_buf), "00a1b2"); - assert_eq!(format!("{:X}", script_buf), "00A1B2"); + assert_eq!(format!("{:x}", script_buf), "0300a1b2"); + assert_eq!(format!("{:X}", script_buf), "0300A1B2"); } assert!(!format!("{:?}", script_buf).is_empty()); } @@ -928,7 +935,7 @@ mod tests { fn script_to_hex() { let script = Script::from_bytes(&[0xa1, 0xb2, 0xc3]); let hex = script.to_hex(); - assert_eq!(hex, "a1b2c3"); + assert_eq!(hex, "03a1b2c3"); } #[test] @@ -937,6 +944,6 @@ mod tests { fn scriptbuf_to_hex() { let script = ScriptBuf::from_bytes(vec![0xa1, 0xb2, 0xc3]); let hex = script.to_hex(); - assert_eq!(hex, "a1b2c3"); + assert_eq!(hex, "03a1b2c3"); } } From 5d5a19793aae73ff09d7064455a1a995eb796c28 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 28 Apr 2025 13:44:24 +1000 Subject: [PATCH 4/4] Run the formatter --- bitcoin/examples/script.rs | 2 +- bitcoin/src/address/mod.rs | 23 +++-- bitcoin/src/blockdata/script/owned.rs | 2 +- bitcoin/src/blockdata/script/tests.rs | 138 +++++++++++++++++--------- bitcoin/src/crypto/sighash.rs | 7 +- bitcoin/src/psbt/mod.rs | 34 +++++-- bitcoin/src/taproot/mod.rs | 14 ++- bitcoin/tests/bip_174.rs | 4 +- bitcoin/tests/serde.rs | 12 ++- 9 files changed, 158 insertions(+), 78 deletions(-) diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs index e7195ecae..de72033d4 100644 --- a/bitcoin/examples/script.rs +++ b/bitcoin/examples/script.rs @@ -9,7 +9,7 @@ use bitcoin::consensus::encode; use bitcoin::key::WPubkeyHash; -use bitcoin::script::{self, ScriptExt, ScriptBufExt}; +use bitcoin::script::{self, ScriptBufExt, ScriptExt}; use bitcoin::ScriptBuf; fn main() { diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index b49213a23..b178612d2 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -1053,7 +1053,10 @@ mod tests { assert_eq!( addr.script_pubkey(), - ScriptBuf::from_hex_no_length_prefix("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() + ScriptBuf::from_hex_no_length_prefix( + "76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac" + ) + .unwrap() ); assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); assert_eq!(addr.address_type(), Some(AddressType::P2pkh)); @@ -1082,7 +1085,8 @@ mod tests { assert_eq!( addr.script_pubkey(), - ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap(), + ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087") + .unwrap(), ); assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); assert_eq!(addr.address_type(), Some(AddressType::P2sh)); @@ -1234,7 +1238,10 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex_no_length_prefix("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac").unwrap() + ScriptBuf::from_hex_no_length_prefix( + "76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac" + ) + .unwrap() ); let addr = @@ -1248,7 +1255,8 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087").unwrap() + ScriptBuf::from_hex_no_length_prefix("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087") + .unwrap() ); let addr: Address = @@ -1297,7 +1305,8 @@ mod tests { assert_eq!(addr.to_string(), into.to_string()); assert_eq!( into.script_pubkey(), - ScriptBuf::from_hex_no_length_prefix("001454d26dddb59c7073c6a197946ea1841951fa7a74").unwrap() + ScriptBuf::from_hex_no_length_prefix("001454d26dddb59c7073c6a197946ea1841951fa7a74") + .unwrap() ); } @@ -1476,7 +1485,9 @@ mod tests { fn fail_address_from_script() { use crate::witness_program; - let bad_p2wpkh = ScriptBuf::from_hex_no_length_prefix("15000014dbc5b0a8f9d4353b4b54c3db48846bb15abfec").unwrap(); + let bad_p2wpkh = + ScriptBuf::from_hex_no_length_prefix("15000014dbc5b0a8f9d4353b4b54c3db48846bb15abfec") + .unwrap(); let bad_p2wsh = ScriptBuf::from_hex_no_length_prefix( "00202d4fa2eb233d008cc83206fa2f4f2e60199000f5b857a835e3172323385623", ) diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index 9a466276f..49357d32e 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -7,10 +7,10 @@ use hex::FromHex as _; use internals::ToU64 as _; use super::{opcode_to_verify, Builder, Instruction, PushBytes, ScriptExtPriv as _}; +use crate::consensus; use crate::opcodes::all::*; use crate::opcodes::{self, Opcode}; use crate::prelude::Vec; -use crate::consensus; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index 1b29c77d2..51d24b25b 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -203,7 +203,10 @@ fn script_builder() { .push_opcode(OP_EQUALVERIFY) .push_opcode(OP_CHECKSIG) .into_script(); - assert_eq!(script.to_hex_string_no_length_prefix(), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac"); + assert_eq!( + script.to_hex_string_no_length_prefix(), + "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac" + ); } #[test] @@ -383,10 +386,12 @@ fn script_hashes() { "3e1525eb183ad4f9b3c5fa3175bdca2a52e947b135bbb90383bf9f6408e2c324" ); assert_eq!( - ScriptBuf::from_hex_no_length_prefix("20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac") - .unwrap() - .tapscript_leaf_hash() - .to_string(), + ScriptBuf::from_hex_no_length_prefix( + "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac" + ) + .unwrap() + .tapscript_leaf_hash() + .to_string(), "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21" ); } @@ -397,30 +402,40 @@ fn provably_unspendable() { assert!(!ScriptBuf::from_hex_no_length_prefix("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac").unwrap().is_op_return()); assert!(!ScriptBuf::from_hex_no_length_prefix("4104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac").unwrap().is_op_return()); // p2pkhash - assert!(!ScriptBuf::from_hex_no_length_prefix("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") - .unwrap() - .is_op_return()); - assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") - .unwrap() - .is_op_return()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac" + ) + .unwrap() + .is_op_return()); + assert!(ScriptBuf::from_hex_no_length_prefix( + "6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87" + ) + .unwrap() + .is_op_return()); } #[test] fn op_return() { - assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") - .unwrap() - .is_op_return()); - assert!(!ScriptBuf::from_hex_no_length_prefix("76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac") - .unwrap() - .is_op_return()); + assert!(ScriptBuf::from_hex_no_length_prefix( + "6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87" + ) + .unwrap() + .is_op_return()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "76a914ee61d57ab51b9d212335b1dba62794ac20d2bcf988ac" + ) + .unwrap() + .is_op_return()); assert!(!ScriptBuf::from_hex_no_length_prefix("").unwrap().is_op_return()); } #[test] fn standard_op_return() { - assert!(ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") - .unwrap() - .is_standard_op_return()); + assert!(ScriptBuf::from_hex_no_length_prefix( + "6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87" + ) + .unwrap() + .is_standard_op_return()); assert!(ScriptBuf::from_hex_no_length_prefix("6a48656c6c6f2c2074686973206973206d7920666972737420636f6e747269627574696f6e20746f207275737420626974636f696e2e20506c6561736520617070726f7665206d79205052206672656e") .unwrap() .is_standard_op_return()); @@ -474,9 +489,11 @@ fn multisig() { .unwrap() .is_multisig()); // OP_RETURN from another test - assert!(!ScriptBuf::from_hex_no_length_prefix("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87") - .unwrap() - .is_multisig()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87" + ) + .unwrap() + .is_multisig()); } #[test] @@ -504,10 +521,19 @@ fn script_asm() { "OP_0 OP_PUSHBYTES_71 304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401 OP_0 OP_PUSHDATA1 552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae"); // Various weird scripts found in transaction 6d7ed9914625c73c0288694a6819196a27ef6c08f98e1270d975a8e65a3dc09a // which triggered overflow bugs on 32-bit machines in script formatting in the past. - assert_eq!(ScriptBuf::from_hex_no_length_prefix("01").unwrap().to_string(), "OP_PUSHBYTES_1 "); - assert_eq!(ScriptBuf::from_hex_no_length_prefix("0201").unwrap().to_string(), "OP_PUSHBYTES_2 "); + assert_eq!( + ScriptBuf::from_hex_no_length_prefix("01").unwrap().to_string(), + "OP_PUSHBYTES_1 " + ); + assert_eq!( + ScriptBuf::from_hex_no_length_prefix("0201").unwrap().to_string(), + "OP_PUSHBYTES_2 " + ); assert_eq!(ScriptBuf::from_hex_no_length_prefix("4c").unwrap().to_string(), ""); - assert_eq!(ScriptBuf::from_hex_no_length_prefix("4c0201").unwrap().to_string(), "OP_PUSHDATA1 "); + assert_eq!( + ScriptBuf::from_hex_no_length_prefix("4c0201").unwrap().to_string(), + "OP_PUSHDATA1 " + ); assert_eq!(ScriptBuf::from_hex_no_length_prefix("4d").unwrap().to_string(), ""); assert_eq!( ScriptBuf::from_hex_no_length_prefix("4dffff01").unwrap().to_string(), @@ -529,25 +555,35 @@ fn script_buf_collect() { #[test] fn script_p2sh_p2p2k_template() { // random outputs I picked out of the mempool - assert!(ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") - .unwrap() - .is_p2pkh()); - assert!(!ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac") - .unwrap() - .is_p2sh()); - assert!(!ScriptBuf::from_hex_no_length_prefix("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ad") - .unwrap() - .is_p2pkh()); + assert!(ScriptBuf::from_hex_no_length_prefix( + "76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac" + ) + .unwrap() + .is_p2pkh()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac" + ) + .unwrap() + .is_p2sh()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ad" + ) + .unwrap() + .is_p2pkh()); assert!(!ScriptBuf::from_hex_no_length_prefix("").unwrap().is_p2pkh()); assert!(ScriptBuf::from_hex_no_length_prefix("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") .unwrap() .is_p2sh()); - assert!(!ScriptBuf::from_hex_no_length_prefix("a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") - .unwrap() - .is_p2pkh()); - assert!(!ScriptBuf::from_hex_no_length_prefix("a314acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87") - .unwrap() - .is_p2sh()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "a914acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87" + ) + .unwrap() + .is_p2pkh()); + assert!(!ScriptBuf::from_hex_no_length_prefix( + "a314acc91e6fef5c7f24e5c8b3f11a664aa8f1352ffd87" + ) + .unwrap() + .is_p2sh()); } #[test] @@ -565,26 +601,30 @@ fn p2sh_p2wsh_conversion() { // Test vectors taken from Core tests/data/script_tests.json // bare p2wsh let witness_script = ScriptBuf::from_hex_no_length_prefix("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); - let expected_without = - ScriptBuf::from_hex_no_length_prefix("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") - .unwrap(); + let expected_without = ScriptBuf::from_hex_no_length_prefix( + "0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + ) + .unwrap(); assert!(witness_script.to_p2wsh().unwrap().is_p2wsh()); assert_eq!(witness_script.to_p2wsh().unwrap(), expected_without); // p2sh let redeem_script = ScriptBuf::from_hex_no_length_prefix("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8").unwrap(); let expected_p2shout = - ScriptBuf::from_hex_no_length_prefix("a91491b24bf9f5288532960ac687abb035127b1d28a587").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a91491b24bf9f5288532960ac687abb035127b1d28a587") + .unwrap(); assert!(redeem_script.to_p2sh().unwrap().is_p2sh()); assert_eq!(redeem_script.to_p2sh().unwrap(), expected_p2shout); // p2sh-p2wsh let witness_script = ScriptBuf::from_hex_no_length_prefix("410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac").unwrap(); - let expected_without = - ScriptBuf::from_hex_no_length_prefix("0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64") - .unwrap(); + let expected_without = ScriptBuf::from_hex_no_length_prefix( + "0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + ) + .unwrap(); let expected_out = - ScriptBuf::from_hex_no_length_prefix("a914f386c2ba255cc56d20cfa6ea8b062f8b5994551887").unwrap(); + ScriptBuf::from_hex_no_length_prefix("a914f386c2ba255cc56d20cfa6ea8b062f8b5994551887") + .unwrap(); assert!(witness_script.to_p2sh().unwrap().is_p2sh()); assert_eq!(witness_script.to_p2wsh().unwrap(), expected_without); assert_eq!(witness_script.to_p2wsh().unwrap().to_p2sh().unwrap(), expected_out); diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 6584d66bf..e2ea1a0cd 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -2089,7 +2089,9 @@ mod tests { ), ).unwrap(); - let spk = ScriptBuf::from_hex_no_length_prefix("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1").unwrap(); + let spk = + ScriptBuf::from_hex_no_length_prefix("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1") + .unwrap(); let value = Amount::from_sat_u32(600_000_000); let mut cache = SighashCache::new(&tx); @@ -2130,7 +2132,8 @@ mod tests { ).unwrap(); let redeem_script = - ScriptBuf::from_hex_no_length_prefix("001479091972186c449eb1ded22b78e40d009bdf0089").unwrap(); + ScriptBuf::from_hex_no_length_prefix("001479091972186c449eb1ded22b78e40d009bdf0089") + .unwrap(); let value = Amount::from_sat_u32(1_000_000_000); let mut cache = SighashCache::new(&tx); diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 33cfd42c2..2fef0a8b1 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -1463,10 +1463,16 @@ mod tests { let expected: Output = Output { redeem_script: Some( - ScriptBuf::from_hex_no_length_prefix("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(), + ScriptBuf::from_hex_no_length_prefix( + "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac", + ) + .unwrap(), ), witness_script: Some( - ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(), + ScriptBuf::from_hex_no_length_prefix( + "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787", + ) + .unwrap(), ), bip32_derivation: hd_keypaths, ..Default::default() @@ -1561,8 +1567,10 @@ mod tests { .unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985") - .unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix( + "160014be18d152a9b012039daf3da7de4f53349eecb985", + ) + .unwrap(), sequence: Sequence::MAX, witness: Witness::from_slice(&[hex!( "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105" @@ -1838,8 +1846,10 @@ mod tests { assert!(&psbt.inputs[0].final_script_sig.is_some()); let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap(); - let expected_out = - ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); + let expected_out = ScriptBuf::from_hex_no_length_prefix( + "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787", + ) + .unwrap(); assert!(redeem_script.is_p2wpkh()); assert_eq!( @@ -1884,8 +1894,10 @@ mod tests { assert!(&psbt.inputs[1].final_script_sig.is_none()); let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap(); - let expected_out = - ScriptBuf::from_hex_no_length_prefix("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(); + let expected_out = ScriptBuf::from_hex_no_length_prefix( + "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787", + ) + .unwrap(); assert!(redeem_script.is_p2wpkh()); assert_eq!( @@ -1909,8 +1921,10 @@ mod tests { assert!(&psbt.inputs[0].final_script_sig.is_none()); let redeem_script = psbt.inputs[0].redeem_script.as_ref().unwrap(); - let expected_out = - ScriptBuf::from_hex_no_length_prefix("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87").unwrap(); + let expected_out = ScriptBuf::from_hex_no_length_prefix( + "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87", + ) + .unwrap(); assert!(redeem_script.is_p2wsh()); assert_eq!( diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index 1323985e2..d1f40a264 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1872,7 +1872,10 @@ mod test { *length, tree_info .script_map - .get(&(ScriptBuf::from_hex_no_length_prefix(script).unwrap(), LeafVersion::TapScript)) + .get(&( + ScriptBuf::from_hex_no_length_prefix(script).unwrap(), + LeafVersion::TapScript + )) .expect("Present Key") .iter() .next() @@ -2025,7 +2028,8 @@ mod test { builder = process_script_trees(leaf, builder, leaves, depth + 1); } } else { - let script = ScriptBuf::from_hex_no_length_prefix(v["script"].as_str().unwrap()).unwrap(); + let script = + ScriptBuf::from_hex_no_length_prefix(v["script"].as_str().unwrap()).unwrap(); let ver = LeafVersion::from_consensus(v["leafVersion"].as_u64().unwrap() as u8).unwrap(); leaves.push((script.clone(), ver)); @@ -2079,8 +2083,10 @@ mod test { .unwrap(); let expected_tweak = arr["intermediary"]["tweak"].as_str().unwrap().parse::().unwrap(); - let expected_spk = - ScriptBuf::from_hex_no_length_prefix(arr["expected"]["scriptPubKey"].as_str().unwrap()).unwrap(); + let expected_spk = ScriptBuf::from_hex_no_length_prefix( + arr["expected"]["scriptPubKey"].as_str().unwrap(), + ) + .unwrap(); let expected_addr = arr["expected"]["bip350Address"] .as_str() .unwrap() diff --git a/bitcoin/tests/bip_174.rs b/bitcoin/tests/bip_174.rs index cda6bf895..ee7a84703 100644 --- a/bitcoin/tests/bip_174.rs +++ b/bitcoin/tests/bip_174.rs @@ -22,7 +22,9 @@ fn hex_psbt(s: &str) -> Psbt { } #[track_caller] -fn hex_script(s: &str) -> ScriptBuf { ScriptBuf::from_hex_no_length_prefix(s).expect("valid hex digits") } +fn hex_script(s: &str) -> ScriptBuf { + ScriptBuf::from_hex_no_length_prefix(s).expect("valid hex digits") +} #[test] fn bip174_psbt_workflow() { diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs index fbcce5030..f9e7e4a6a 100644 --- a/bitcoin/tests/serde.rs +++ b/bitcoin/tests/serde.rs @@ -226,8 +226,10 @@ fn serde_regression_psbt() { .unwrap(), vout: 1, }, - script_sig: ScriptBuf::from_hex_no_length_prefix("160014be18d152a9b012039daf3da7de4f53349eecb985") - .unwrap(), + script_sig: ScriptBuf::from_hex_no_length_prefix( + "160014be18d152a9b012039daf3da7de4f53349eecb985", + ) + .unwrap(), sequence: Sequence::from_consensus(4294967295), witness: Witness::from_slice(&[Vec::from_hex( "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105", @@ -236,8 +238,10 @@ fn serde_regression_psbt() { }], output: vec![TxOut { value: Amount::from_sat(190_303_501_938).unwrap(), - script_pubkey: ScriptBuf::from_hex_no_length_prefix("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587") - .unwrap(), + script_pubkey: ScriptBuf::from_hex_no_length_prefix( + "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587", + ) + .unwrap(), }], }; let unknown: BTreeMap> =