From bb2c7ec790a89046fee0689ccc6a3e0f0c00da9b Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sat, 18 Feb 2023 11:21:13 +0100 Subject: [PATCH] Introduce `hex_lit` crate So far we deserialized hex into `Vec` at run time. This was mainly in tests where it had negligible performance cost. However moving the computation to compile time has a few benefits: it allows proving the length of the decoded bytes and identifies potential typos before the code goes through LLVM and other compilation machinery which makes feedback faster. This change uses the `hex_lit` crate to move computation to compile time. It is implemented as `const` declarative macro which doesn't blow up compilation time. --- bitcoin/Cargo.toml | 1 + bitcoin/src/address.rs | 8 +++----- bitcoin/src/blockdata/constants.rs | 10 +++------- bitcoin/src/blockdata/script/tests.rs | 28 +++++++++++++-------------- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index f31d7906..2c4cbba2 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -44,6 +44,7 @@ bitcoinconsensus = { version = "0.20.2-0.5.0", optional = true, default-features core2 = { version = "0.3.0", default-features = false, features = ["alloc"], optional = true } # Do NOT use this as a feature! Use the `serde` feature instead. actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true } +hex_lit = "0.1.1" [dev-dependencies] serde_json = "1.0.0" diff --git a/bitcoin/src/address.rs b/bitcoin/src/address.rs index aa27076e..dc0c0114 100644 --- a/bitcoin/src/address.rs +++ b/bitcoin/src/address.rs @@ -1139,7 +1139,7 @@ mod tests { use super::*; use crate::crypto::key::PublicKey; - use crate::internal_macros::hex; + use hex_lit::hex; use crate::network::constants::Network::{Bitcoin, Testnet}; fn roundtrips(addr: &Address) { @@ -1272,10 +1272,8 @@ mod tests { #[test] fn test_non_existent_segwit_version() { // 40-byte program - let program = hex!( - "654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4" - ); - let witness_prog = WitnessProgram::new(WitnessVersion::V13, program).unwrap(); + let program = hex!("654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"); + let witness_prog = WitnessProgram::new(WitnessVersion::V13, program.to_vec()).unwrap(); let addr = Address::new(Bitcoin, Payload::WitnessProgram(witness_prog)); roundtrips(&addr); } diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs index 4e7b8e39..7d18776e 100644 --- a/bitcoin/src/blockdata/constants.rs +++ b/bitcoin/src/blockdata/constants.rs @@ -8,13 +8,11 @@ //! single transaction. //! -use crate::prelude::*; - use core::default::Default; use bitcoin_internals::impl_array_newtype; +use hex_lit::hex; -use crate::hashes::hex::{self, HexIterator}; use crate::hashes::{Hash, sha256d}; use crate::blockdata::script; use crate::blockdata::opcodes::all::*; @@ -85,11 +83,9 @@ fn bitcoin_genesis_tx() -> Transaction { }); // Outputs - let script_bytes: Result, hex::Error> = - HexIterator::new("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap() - .collect(); + let script_bytes = hex!("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); let out_script = script::Builder::new() - .push_slice(script_bytes.unwrap().as_slice()) + .push_slice(&script_bytes) .push_opcode(OP_CHECKSIG) .into_script(); ret.output.push(TxOut { diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs index 708e6373..2fcb7275 100644 --- a/bitcoin/src/blockdata/script/tests.rs +++ b/bitcoin/src/blockdata/script/tests.rs @@ -3,13 +3,12 @@ use core::str::FromStr; use super::*; use crate::hashes::Hash; -use crate::hashes::hex::FromHex; use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash}; use crate::consensus::encode::{deserialize, serialize}; use crate::blockdata::opcodes; use crate::crypto::key::{PublicKey, XOnlyPublicKey}; use crate::psbt::serialize::Serialize; -use crate::internal_macros::hex; +use hex_lit::hex; #[test] fn script() { @@ -32,7 +31,7 @@ fn script() { script = script.push_int(-10000000); comp.extend([4u8, 128, 150, 152, 128].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); // data - script = script.push_slice("NRA4VR".as_bytes()); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); + script = script.push_slice(b"NRA4VR"); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]); // keys const KEYSTR1: &str = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"; @@ -88,9 +87,9 @@ fn p2pk_pubkey_bytes_different_op_code_returns_none() { #[test] fn p2pk_pubkey_bytes_incorrect_key_size_returns_none() { // 63 byte key - let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; + let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; let invalid_p2pk_script = Script::builder() - .push_slice(malformed_key.as_bytes()) + .push_slice(malformed_key) .push_opcode(OP_CHECKSIG) .into_script(); assert!(invalid_p2pk_script.p2pk_pubkey_bytes().is_none()); @@ -154,9 +153,9 @@ fn p2pk_public_key_different_op_code_returns_none() { #[test] fn p2pk_public_key_incorrect_size_returns_none() { - let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; + let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; let malformed_key_script = Script::builder() - .push_slice(malformed_key.as_bytes()) + .push_slice(malformed_key) .push_opcode(OP_CHECKSIG) .into_script(); assert!(malformed_key_script.p2pk_public_key().is_none()); @@ -165,9 +164,9 @@ fn p2pk_public_key_incorrect_size_returns_none() { #[test] fn p2pk_public_key_invalid_key_returns_none() { - let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux"; + let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux"; let invalid_key_script = Script::builder() - .push_slice(malformed_key.as_bytes()) + .push_slice(malformed_key) .push_opcode(OP_CHECKSIG) .into_script(); assert!(invalid_key_script.p2pk_public_key().is_none()); @@ -190,7 +189,7 @@ fn script_x_only_key() { const KEYSTR: &str = "209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be"; let x_only_key = XOnlyPublicKey::from_str(&KEYSTR[2..]).unwrap(); let script = Builder::new().push_x_only_key(&x_only_key); - assert_eq!(script.into_bytes(), hex!(KEYSTR)); + assert_eq!(script.into_bytes(), &hex!(KEYSTR) as &[u8]); } #[test] @@ -231,7 +230,7 @@ fn script_generators() { // Test data are taken from the second output of // 2ccb3a1f745eb4eefcf29391460250adda5fab78aaddb902d25d3cd97d9d8e61 transaction - let data = Vec::::from_hex("aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a").unwrap(); + let data = hex!("aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a"); let op_return = ScriptBuf::new_op_return(&data); assert!(op_return.is_op_return()); assert_eq!(op_return.to_hex_string(), "6a24aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a"); @@ -314,7 +313,7 @@ fn script_serialize() { let hex_script = hex!("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52"); let script: Result = deserialize(&hex_script); assert!(script.is_ok()); - assert_eq!(serialize(&script.unwrap()), hex_script); + assert_eq!(serialize(&script.unwrap()), &hex_script as &[u8]); } #[test] @@ -547,9 +546,10 @@ fn script_ord() { #[cfg(feature = "bitcoinconsensus")] fn test_bitcoinconsensus () { // a random segwit transaction from the blockchain using native segwit - let spent = Builder::from(hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d")).into_script(); + let spent_bytes = hex!("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d"); + let spent = Script::from_bytes(&spent_bytes); let spending = hex!("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000"); - spent.verify(0, crate::Amount::from_sat(18393430), spending.as_slice()).unwrap(); + spent.verify(0, crate::Amount::from_sat(18393430), &spending).unwrap(); } #[test]