Introduce `hex_lit` crate

So far we deserialized hex into `Vec<u8>` 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.
This commit is contained in:
Martin Habovstiak 2023-02-18 11:21:13 +01:00
parent 794ddde657
commit bb2c7ec790
4 changed files with 21 additions and 26 deletions

View File

@ -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 } core2 = { version = "0.3.0", default-features = false, features = ["alloc"], optional = true }
# Do NOT use this as a feature! Use the `serde` feature instead. # 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 } actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
hex_lit = "0.1.1"
[dev-dependencies] [dev-dependencies]
serde_json = "1.0.0" serde_json = "1.0.0"

View File

@ -1139,7 +1139,7 @@ mod tests {
use super::*; use super::*;
use crate::crypto::key::PublicKey; use crate::crypto::key::PublicKey;
use crate::internal_macros::hex; use hex_lit::hex;
use crate::network::constants::Network::{Bitcoin, Testnet}; use crate::network::constants::Network::{Bitcoin, Testnet};
fn roundtrips(addr: &Address) { fn roundtrips(addr: &Address) {
@ -1272,10 +1272,8 @@ mod tests {
#[test] #[test]
fn test_non_existent_segwit_version() { fn test_non_existent_segwit_version() {
// 40-byte program // 40-byte program
let program = hex!( let program = hex!("654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4");
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4" let witness_prog = WitnessProgram::new(WitnessVersion::V13, program.to_vec()).unwrap();
);
let witness_prog = WitnessProgram::new(WitnessVersion::V13, program).unwrap();
let addr = Address::new(Bitcoin, Payload::WitnessProgram(witness_prog)); let addr = Address::new(Bitcoin, Payload::WitnessProgram(witness_prog));
roundtrips(&addr); roundtrips(&addr);
} }

View File

@ -8,13 +8,11 @@
//! single transaction. //! single transaction.
//! //!
use crate::prelude::*;
use core::default::Default; use core::default::Default;
use bitcoin_internals::impl_array_newtype; use bitcoin_internals::impl_array_newtype;
use hex_lit::hex;
use crate::hashes::hex::{self, HexIterator};
use crate::hashes::{Hash, sha256d}; use crate::hashes::{Hash, sha256d};
use crate::blockdata::script; use crate::blockdata::script;
use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::all::*;
@ -85,11 +83,9 @@ fn bitcoin_genesis_tx() -> Transaction {
}); });
// Outputs // Outputs
let script_bytes: Result<Vec<u8>, hex::Error> = let script_bytes = hex!("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
HexIterator::new("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap()
.collect();
let out_script = script::Builder::new() let out_script = script::Builder::new()
.push_slice(script_bytes.unwrap().as_slice()) .push_slice(&script_bytes)
.push_opcode(OP_CHECKSIG) .push_opcode(OP_CHECKSIG)
.into_script(); .into_script();
ret.output.push(TxOut { ret.output.push(TxOut {

View File

@ -3,13 +3,12 @@ use core::str::FromStr;
use super::*; use super::*;
use crate::hashes::Hash; use crate::hashes::Hash;
use crate::hashes::hex::FromHex;
use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash}; use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};
use crate::blockdata::opcodes; use crate::blockdata::opcodes;
use crate::crypto::key::{PublicKey, XOnlyPublicKey}; use crate::crypto::key::{PublicKey, XOnlyPublicKey};
use crate::psbt::serialize::Serialize; use crate::psbt::serialize::Serialize;
use crate::internal_macros::hex; use hex_lit::hex;
#[test] #[test]
fn script() { 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[..]); script = script.push_int(-10000000); comp.extend([4u8, 128, 150, 152, 128].iter().cloned()); assert_eq!(script.as_bytes(), &comp[..]);
// data // 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 // keys
const KEYSTR1: &str = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"; const KEYSTR1: &str = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af";
@ -88,9 +87,9 @@ fn p2pk_pubkey_bytes_different_op_code_returns_none() {
#[test] #[test]
fn p2pk_pubkey_bytes_incorrect_key_size_returns_none() { fn p2pk_pubkey_bytes_incorrect_key_size_returns_none() {
// 63 byte key // 63 byte key
let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1";
let invalid_p2pk_script = Script::builder() let invalid_p2pk_script = Script::builder()
.push_slice(malformed_key.as_bytes()) .push_slice(malformed_key)
.push_opcode(OP_CHECKSIG) .push_opcode(OP_CHECKSIG)
.into_script(); .into_script();
assert!(invalid_p2pk_script.p2pk_pubkey_bytes().is_none()); assert!(invalid_p2pk_script.p2pk_pubkey_bytes().is_none());
@ -154,9 +153,9 @@ fn p2pk_public_key_different_op_code_returns_none() {
#[test] #[test]
fn p2pk_public_key_incorrect_size_returns_none() { fn p2pk_public_key_incorrect_size_returns_none() {
let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1"; let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1";
let malformed_key_script = Script::builder() let malformed_key_script = Script::builder()
.push_slice(malformed_key.as_bytes()) .push_slice(malformed_key)
.push_opcode(OP_CHECKSIG) .push_opcode(OP_CHECKSIG)
.into_script(); .into_script();
assert!(malformed_key_script.p2pk_public_key().is_none()); assert!(malformed_key_script.p2pk_public_key().is_none());
@ -165,9 +164,9 @@ fn p2pk_public_key_incorrect_size_returns_none() {
#[test] #[test]
fn p2pk_public_key_invalid_key_returns_none() { fn p2pk_public_key_invalid_key_returns_none() {
let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux"; let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux";
let invalid_key_script = Script::builder() let invalid_key_script = Script::builder()
.push_slice(malformed_key.as_bytes()) .push_slice(malformed_key)
.push_opcode(OP_CHECKSIG) .push_opcode(OP_CHECKSIG)
.into_script(); .into_script();
assert!(invalid_key_script.p2pk_public_key().is_none()); assert!(invalid_key_script.p2pk_public_key().is_none());
@ -190,7 +189,7 @@ fn script_x_only_key() {
const KEYSTR: &str = "209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be"; const KEYSTR: &str = "209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be";
let x_only_key = XOnlyPublicKey::from_str(&KEYSTR[2..]).unwrap(); let x_only_key = XOnlyPublicKey::from_str(&KEYSTR[2..]).unwrap();
let script = Builder::new().push_x_only_key(&x_only_key); 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] #[test]
@ -231,7 +230,7 @@ fn script_generators() {
// Test data are taken from the second output of // Test data are taken from the second output of
// 2ccb3a1f745eb4eefcf29391460250adda5fab78aaddb902d25d3cd97d9d8e61 transaction // 2ccb3a1f745eb4eefcf29391460250adda5fab78aaddb902d25d3cd97d9d8e61 transaction
let data = Vec::<u8>::from_hex("aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a").unwrap(); let data = hex!("aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a");
let op_return = ScriptBuf::new_op_return(&data); let op_return = ScriptBuf::new_op_return(&data);
assert!(op_return.is_op_return()); assert!(op_return.is_op_return());
assert_eq!(op_return.to_hex_string(), "6a24aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a"); assert_eq!(op_return.to_hex_string(), "6a24aa21a9ed20280f53f2d21663cac89e6bd2ad19edbabb048cda08e73ed19e9268d0afea2a");
@ -314,7 +313,7 @@ fn script_serialize() {
let hex_script = hex!("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52"); let hex_script = hex!("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52");
let script: Result<ScriptBuf, _> = deserialize(&hex_script); let script: Result<ScriptBuf, _> = deserialize(&hex_script);
assert!(script.is_ok()); assert!(script.is_ok());
assert_eq!(serialize(&script.unwrap()), hex_script); assert_eq!(serialize(&script.unwrap()), &hex_script as &[u8]);
} }
#[test] #[test]
@ -547,9 +546,10 @@ fn script_ord() {
#[cfg(feature = "bitcoinconsensus")] #[cfg(feature = "bitcoinconsensus")]
fn test_bitcoinconsensus () { fn test_bitcoinconsensus () {
// a random segwit transaction from the blockchain using native segwit // 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"); 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] #[test]