From 8e428562cbff89aa3f9d111792eb6105cfd8cf03 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sat, 30 Jul 2022 14:22:18 +0200 Subject: [PATCH] Implemented unsized `Script` This renames `Script` to `ScriptBuf` and adds unsized `Script` modeled after `PathBuf`/`Path`. The change cleans up the API a bit, especially all functions that previously accepted `&Script` now accept truly borrowed version. Some functions that perviously accepted `&[u8]` can now accept `&Script` because constructing it is no loger costly. --- bitcoin/Cargo.toml | 1 + bitcoin/examples/ecdsa-psbt.rs | 8 +- bitcoin/examples/taproot-psbt.rs | 20 +- .../fuzz/fuzz_targets/deserialize_script.rs | 2 +- .../fuzz_targets/script_bytes_to_asm_fmt.rs | 2 +- bitcoin/src/address.rs | 16 +- bitcoin/src/bip152.rs | 6 +- bitcoin/src/bip158.rs | 19 +- bitcoin/src/blockdata/block.rs | 2 +- bitcoin/src/blockdata/script.rs | 1928 ++++++++++++----- bitcoin/src/blockdata/transaction.rs | 22 +- bitcoin/src/internal_macros.rs | 2 +- bitcoin/src/lib.rs | 3 +- bitcoin/src/network/message.rs | 4 +- bitcoin/src/psbt/map/input.rs | 18 +- bitcoin/src/psbt/map/output.rs | 14 +- bitcoin/src/psbt/mod.rs | 24 +- bitcoin/src/psbt/serialize.rs | 22 +- bitcoin/src/sighash.rs | 24 +- bitcoin/src/taproot.rs | 56 +- bitcoin/tests/psbt.rs | 12 +- bitcoin/tests/serde.rs | 28 +- 22 files changed, 1525 insertions(+), 708 deletions(-) diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index 294e3da6..c903f384 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -20,6 +20,7 @@ serde = ["actual-serde", "bitcoin_hashes/serde", "secp256k1/serde"] secp-lowmemory = ["secp256k1/lowmemory"] secp-recovery = ["secp256k1/recovery"] bitcoinconsensus-std = ["bitcoinconsensus/std", "std"] +rust_v_1_53 = [] # At least one of std, no-std must be enabled. # diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs index 77494fbd..b6405d0b 100644 --- a/bitcoin/examples/ecdsa-psbt.rs +++ b/bitcoin/examples/ecdsa-psbt.rs @@ -42,7 +42,7 @@ use bitcoin::locktime::absolute; use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType}; use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::{ - Address, Amount, Network, OutPoint, PublicKey, Script, Sequence, Transaction, TxIn, TxOut, + Address, Amount, Network, OutPoint, PublicKey, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness, }; @@ -191,7 +191,7 @@ impl WatchOnly { txid: Txid::from_hex(INPUT_UTXO_TXID)?, vout: INPUT_UTXO_VOUT, }, - script_sig: Script::new(), + script_sig: ScriptBuf::new(), sequence: Sequence::MAX, // Disable LockTime and RBF. witness: Witness::default(), }], @@ -216,7 +216,7 @@ impl WatchOnly { let pk = self.input_xpub.to_pub(); let wpkh = pk.wpubkey_hash().expect("a compressed pubkey"); - let redeem_script = Script::new_v0_p2wpkh(&wpkh); + let redeem_script = ScriptBuf::new_v0_p2wpkh(&wpkh); input.redeem_script = Some(redeem_script); let fingerprint = self.master_fingerprint; @@ -284,7 +284,7 @@ fn input_derivation_path() -> Result { } fn previous_output() -> TxOut { - let script_pubkey = Script::from_hex(INPUT_UTXO_SCRIPT_PUBKEY) + let script_pubkey = ScriptBuf::from_hex(INPUT_UTXO_SCRIPT_PUBKEY) .expect("failed to parse input utxo scriptPubkey"); let amount = Amount::from_str(INPUT_UTXO_VALUE).expect("failed to parse input utxo value"); diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs index e4149521..6a3477aa 100644 --- a/bitcoin/examples/taproot-psbt.rs +++ b/bitcoin/examples/taproot-psbt.rs @@ -94,7 +94,7 @@ use bitcoin::taproot::{ LeafVersion, TapLeafHash, TapSighashHash, TaprootBuilder, TaprootSpendInfo, }; use bitcoin::{ - absolute, script, Address, Amount, OutPoint, Script, Transaction, TxIn, TxOut, Txid, Witness, + absolute, script, Address, Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid, Witness, }; fn main() -> Result<(), Box> { @@ -241,7 +241,7 @@ fn generate_bip86_key_spend_tx( txid: Txid::from_hex(input_utxo.txid)?, vout: input_utxo.vout, }, - script_sig: Script::new(), + script_sig: ScriptBuf::new(), sequence: bitcoin::Sequence(0xFFFFFFFF), // Ignore nSequence. witness: Witness::default(), }], @@ -263,7 +263,7 @@ fn generate_bip86_key_spend_tx( let mut input = Input { witness_utxo: { - let script_pubkey = Script::from_hex(input_utxo.script_pubkey) + let script_pubkey = ScriptBuf::from_hex(input_utxo.script_pubkey) .expect("failed to parse input utxo scriptPubkey"); let amount = Amount::from_sat(from_amount); @@ -289,7 +289,7 @@ fn generate_bip86_key_spend_tx( vout, &sighash::Prevouts::All(&[TxOut { value: from_amount, - script_pubkey: Script::from_str(input_utxo.script_pubkey)?, + script_pubkey: ScriptBuf::from_str(input_utxo.script_pubkey)?, }]), hash_ty, )?; @@ -333,7 +333,7 @@ fn generate_bip86_key_spend_tx( tx.verify(|_| { Some(TxOut { value: from_amount, - script_pubkey: Script::from_hex(input_utxo.script_pubkey).unwrap(), + script_pubkey: ScriptBuf::from_hex(input_utxo.script_pubkey).unwrap(), }) }) .expect("failed to verify transaction"); @@ -367,7 +367,7 @@ impl BenefactorWallet { }) } - fn time_lock_script(locktime: absolute::LockTime, beneficiary_key: XOnlyPublicKey) -> Script { + fn time_lock_script(locktime: absolute::LockTime, beneficiary_key: XOnlyPublicKey) -> ScriptBuf { script::Builder::new() .push_int(locktime.to_consensus_u32() as i64) .push_opcode(OP_CLTV) @@ -404,7 +404,7 @@ impl BenefactorWallet { .finalize(&self.secp, internal_keypair.x_only_public_key().0) .expect("Should be finalizable"); self.current_spend_info = Some(taproot_spend_info.clone()); - let script_pubkey = Script::new_v1_p2tr( + let script_pubkey = ScriptBuf::new_v1_p2tr( &self.secp, taproot_spend_info.internal_key(), taproot_spend_info.merkle_root(), @@ -425,7 +425,7 @@ impl BenefactorWallet { lock_time, input: vec![TxIn { previous_output: OutPoint { txid: tx.txid(), vout: 0 }, - script_sig: Script::new(), + script_sig: ScriptBuf::new(), sequence: bitcoin::Sequence(0xFFFFFFFD), // enable locktime and opt-in RBF witness: Witness::default(), }], @@ -505,7 +505,7 @@ impl BenefactorWallet { .expect("Should be finalizable"); self.current_spend_info = Some(taproot_spend_info.clone()); let prevout_script_pubkey = input.witness_utxo.as_ref().unwrap().script_pubkey.clone(); - let output_script_pubkey = Script::new_v1_p2tr( + let output_script_pubkey = ScriptBuf::new_v1_p2tr( &self.secp, taproot_spend_info.internal_key(), taproot_spend_info.merkle_root(), @@ -573,7 +573,7 @@ impl BenefactorWallet { lock_time, input: vec![TxIn { previous_output: OutPoint { txid: tx.txid(), vout: 0 }, - script_sig: Script::new(), + script_sig: ScriptBuf::new(), sequence: bitcoin::Sequence(0xFFFFFFFD), // enable locktime and opt-in RBF witness: Witness::default(), }], diff --git a/bitcoin/fuzz/fuzz_targets/deserialize_script.rs b/bitcoin/fuzz/fuzz_targets/deserialize_script.rs index 8cd25ae3..94875076 100644 --- a/bitcoin/fuzz/fuzz_targets/deserialize_script.rs +++ b/bitcoin/fuzz/fuzz_targets/deserialize_script.rs @@ -6,7 +6,7 @@ use bitcoin::blockdata::script; use bitcoin::consensus::encode; fn do_test(data: &[u8]) { - let s: Result = encode::deserialize(data); + let s: Result = encode::deserialize(data); if let Ok(script) = s { let _: Result, script::Error> = script.instructions().collect(); diff --git a/bitcoin/fuzz/fuzz_targets/script_bytes_to_asm_fmt.rs b/bitcoin/fuzz/fuzz_targets/script_bytes_to_asm_fmt.rs index 534f6f9f..e8736575 100644 --- a/bitcoin/fuzz/fuzz_targets/script_bytes_to_asm_fmt.rs +++ b/bitcoin/fuzz/fuzz_targets/script_bytes_to_asm_fmt.rs @@ -17,7 +17,7 @@ impl fmt::Write for NullWriter { fn do_test(data: &[u8]) { let mut writer = NullWriter; - bitcoin::Script::bytes_to_asm_fmt(data, &mut writer); + bitcoin::Script::from_bytes(data).fmt_asm(&mut writer); } #[cfg(feature = "afl")] diff --git a/bitcoin/src/address.rs b/bitcoin/src/address.rs index 7772702a..a0da8586 100644 --- a/bitcoin/src/address.rs +++ b/bitcoin/src/address.rs @@ -409,9 +409,11 @@ impl Payload { return Err(Error::InvalidSegwitV0ProgramLength(script.len() - 2)); } + let opcode = script.first_opcode().expect("witness_version guarantees len() > 4"); + Payload::WitnessProgram { - version: WitnessVersion::try_from(opcodes::All::from(script[0]))?, - program: script[2..].to_vec(), + version: WitnessVersion::try_from(opcode)?, + program: script.as_bytes()[2..].to_vec(), } } else { return Err(Error::UnrecognizedScript); @@ -419,12 +421,12 @@ impl Payload { } /// Generates a script pubkey spending to this [Payload]. - pub fn script_pubkey(&self) -> script::Script { + pub fn script_pubkey(&self) -> script::ScriptBuf { match *self { - Payload::PubkeyHash(ref hash) => script::Script::new_p2pkh(hash), - Payload::ScriptHash(ref hash) => script::Script::new_p2sh(hash), + Payload::PubkeyHash(ref hash) => script::ScriptBuf::new_p2pkh(hash), + Payload::ScriptHash(ref hash) => script::ScriptBuf::new_p2sh(hash), Payload::WitnessProgram { version, program: ref prog } => - script::Script::new_witness_program(version, prog), + script::ScriptBuf::new_witness_program(version, prog) } } @@ -674,7 +676,7 @@ impl Address { } /// Generates a script pubkey spending to this address. - pub fn script_pubkey(&self) -> script::Script { self.payload.script_pubkey() } + pub fn script_pubkey(&self) -> script::ScriptBuf { self.payload.script_pubkey() } /// Creates a URI string *bitcoin:address* optimized to be encoded in QR codes. /// diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs index c3e6d899..a8eacbb5 100644 --- a/bitcoin/src/bip152.rs +++ b/bitcoin/src/bip152.rs @@ -377,7 +377,7 @@ mod test { use crate::consensus::encode::{deserialize, serialize}; use crate::hashes::hex::FromHex; use crate::{ - CompactTarget, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode, TxOut, Txid, + CompactTarget, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxMerkleNode, TxOut, Txid, Witness, }; @@ -387,11 +387,11 @@ mod test { lock_time: absolute::LockTime::from_consensus(2), input: vec![TxIn { previous_output: OutPoint::new(Txid::hash(nonce), 0), - script_sig: Script::new(), + script_sig: ScriptBuf::new(), sequence: Sequence(1), witness: Witness::new(), }], - output: vec![TxOut { value: 1, script_pubkey: Script::new() }], + output: vec![TxOut { value: 1, script_pubkey: ScriptBuf::new() }], } } diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs index 8493d4e7..d487f7bb 100644 --- a/bitcoin/src/bip158.rs +++ b/bitcoin/src/bip158.rs @@ -20,7 +20,7 @@ //! # Examples //! //! ```ignore -//! fn get_script_for_coin(coin: &OutPoint) -> Result { +//! fn get_script_for_coin(coin: &OutPoint) -> Result { //! // get utxo ... //! } //! @@ -32,7 +32,7 @@ //! //! // read and evaluate a filter //! -//! let query: Iterator = // .. some scripts you care about +//! let query: Iterator = // .. some scripts you care about //! if filter.match_any(&block_hash, &mut query.map(|s| s.as_bytes())) { //! // get this block //! } @@ -117,9 +117,10 @@ impl BlockFilter { pub fn new(content: &[u8]) -> BlockFilter { BlockFilter { content: content.to_vec() } } /// Computes a SCRIPT_FILTER that contains spent and output scripts. - pub fn new_script_filter(block: &Block, script_for_coin: M) -> Result + pub fn new_script_filter(block: &Block, script_for_coin: M) -> Result where - M: Fn(&OutPoint) -> Result, + M: Fn(&OutPoint) -> Result, + S: Borrow