Merge rust-bitcoin/rust-bitcoin#1190: Added a newtype for script-pushable slice
bcd95fa036
Add a newtype for script-pushable slice (Martin Habovstiak)8fedbcbf13
Add `ecdsa::SerializedSignature` (Martin Habovstiak)26fc4152ec
Use `PushDataLenLen` to improve confidence (Martin Habovstiak)bb2c7ec790
Introduce `hex_lit` crate (Martin Habovstiak) Pull request description: The code previously contained undocumented panic when trying to push slice. This is now solved by making a newtype that guarantees limited length. Closes #1186 Closes #1189 This is done on top of unsized script change. I only request light review of the design for now. I'd like to improve it to use a macro for all unsized types. ACKs for top commit: tcharding: ACKbcd95fa036
apoelstra: ACKbcd95fa036
Tree-SHA512: 193bee9b72e850a1a827124e6774111dc9bbc41a3999ae777acbf201a2ca062cad96debb27b1fd3374c89bacb95aab22638e967b7a11b3cbe955f2d5521f64d9
This commit is contained in:
commit
ad50fd728f
|
@ -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"
|
||||
|
|
|
@ -22,7 +22,7 @@ fn do_test(data: &[u8]) {
|
|||
// reserialized as numbers. (For -1 through 16, this will use special ops; for
|
||||
// others it'll just reserialize them as pushes.)
|
||||
if bytes.len() == 1 && bytes[0] != 0x80 && bytes[0] != 0x00 {
|
||||
if let Ok(num) = script::read_scriptint(bytes) {
|
||||
if let Ok(num) = script::read_scriptint(bytes.as_bytes()) {
|
||||
b = b.push_int(num);
|
||||
} else {
|
||||
b = b.push_slice(bytes);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
//! bitcoin = { version = "...", features = ["rand-std"] }
|
||||
//! ```
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::str::FromStr;
|
||||
|
@ -43,7 +43,7 @@ use crate::blockdata::constants::{
|
|||
};
|
||||
use crate::blockdata::opcodes;
|
||||
use crate::blockdata::opcodes::all::*;
|
||||
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf};
|
||||
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf, PushBytes, PushBytesBuf, PushBytesErrorReport};
|
||||
use crate::crypto::key::PublicKey;
|
||||
use crate::crypto::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
||||
use crate::error::ParseIntError;
|
||||
|
@ -402,12 +402,14 @@ pub struct WitnessProgram {
|
|||
/// The witness program version.
|
||||
version: WitnessVersion,
|
||||
/// The witness program. (Between 2 and 40 bytes)
|
||||
program: Vec<u8>,
|
||||
program: PushBytesBuf,
|
||||
}
|
||||
|
||||
impl WitnessProgram {
|
||||
/// Creates a new witness program.
|
||||
pub fn new(version: WitnessVersion, program: Vec<u8>) -> Result<Self, Error> {
|
||||
pub fn new<P>(version: WitnessVersion, program: P) -> Result<Self, Error> where P: TryInto<PushBytesBuf>, <P as TryInto<PushBytesBuf>>::Error: PushBytesErrorReport {
|
||||
let program = program.try_into()
|
||||
.map_err(|error| Error::InvalidWitnessProgramLength(error.input_len()))?;
|
||||
if program.len() < 2 || program.len() > 40 {
|
||||
return Err(Error::InvalidWitnessProgramLength(program.len()));
|
||||
}
|
||||
|
@ -425,7 +427,7 @@ impl WitnessProgram {
|
|||
}
|
||||
|
||||
/// Returns the witness program.
|
||||
pub fn program(&self) -> &[u8] {
|
||||
pub fn program(&self) -> &PushBytes {
|
||||
&self.program
|
||||
}
|
||||
}
|
||||
|
@ -444,9 +446,13 @@ impl Payload {
|
|||
} else if script.is_witness_program() {
|
||||
let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
|
||||
|
||||
let witness_program = script
|
||||
.as_bytes()[2..]
|
||||
.to_vec();
|
||||
|
||||
let witness_program = WitnessProgram::new(
|
||||
WitnessVersion::try_from(opcode)?,
|
||||
script.as_bytes()[2..].to_vec(),
|
||||
witness_program,
|
||||
)?;
|
||||
Payload::WitnessProgram(witness_program)
|
||||
} else {
|
||||
|
@ -481,7 +487,7 @@ impl Payload {
|
|||
pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
|
||||
let prog = WitnessProgram::new(
|
||||
WitnessVersion::V0,
|
||||
pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.into_inner().to_vec()
|
||||
pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?
|
||||
)?;
|
||||
Ok(Payload::WitnessProgram(prog))
|
||||
}
|
||||
|
@ -490,7 +496,7 @@ impl Payload {
|
|||
pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
|
||||
let builder = script::Builder::new()
|
||||
.push_int(0)
|
||||
.push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.as_ref());
|
||||
.push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
|
||||
|
||||
Ok(Payload::ScriptHash(builder.into_script().script_hash()))
|
||||
}
|
||||
|
@ -499,7 +505,7 @@ impl Payload {
|
|||
pub fn p2wsh(script: &Script) -> Payload {
|
||||
let prog = WitnessProgram::new(
|
||||
WitnessVersion::V0,
|
||||
script.wscript_hash().as_inner().to_vec()
|
||||
script.wscript_hash()
|
||||
).expect("wscript_hash has len 32 compatible with segwitv0");
|
||||
Payload::WitnessProgram(prog)
|
||||
}
|
||||
|
@ -507,7 +513,7 @@ impl Payload {
|
|||
/// Create a pay to script payload that embeds a witness pay to script hash address
|
||||
pub fn p2shwsh(script: &Script) -> Payload {
|
||||
let ws =
|
||||
script::Builder::new().push_int(0).push_slice(script.wscript_hash().as_ref()).into_script();
|
||||
script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script();
|
||||
|
||||
Payload::ScriptHash(ws.script_hash())
|
||||
}
|
||||
|
@ -521,7 +527,7 @@ impl Payload {
|
|||
let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
|
||||
let prog = WitnessProgram::new(
|
||||
WitnessVersion::V1,
|
||||
output_key.to_inner().serialize().to_vec()
|
||||
output_key.to_inner().serialize()
|
||||
).expect("taproot output key has len 32 <= 40");
|
||||
Payload::WitnessProgram(prog)
|
||||
}
|
||||
|
@ -532,7 +538,7 @@ impl Payload {
|
|||
pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload {
|
||||
let prog = WitnessProgram::new(
|
||||
WitnessVersion::V1,
|
||||
output_key.to_inner().serialize().to_vec()
|
||||
output_key.to_inner().serialize()
|
||||
).expect("taproot output key has len 32 <= 40");
|
||||
Payload::WitnessProgram(prog)
|
||||
}
|
||||
|
@ -543,7 +549,7 @@ impl Payload {
|
|||
match self {
|
||||
Payload::ScriptHash(hash) => hash.as_ref(),
|
||||
Payload::PubkeyHash(hash) => hash.as_ref(),
|
||||
Payload::WitnessProgram(prog) => prog.program(),
|
||||
Payload::WitnessProgram(prog) => prog.program().as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -588,7 +594,7 @@ impl<'a> fmt::Display for AddressEncoding<'a> {
|
|||
let mut bech32_writer =
|
||||
bech32::Bech32Writer::new(self.bech32_hrp, version.bech32_variant(), writer)?;
|
||||
bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?;
|
||||
bech32::ToBase32::write_base32(&prog, &mut bech32_writer)
|
||||
bech32::ToBase32::write_base32(&prog.as_bytes(), &mut bech32_writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1139,7 +1145,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 +1278,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);
|
||||
}
|
||||
|
|
|
@ -334,7 +334,7 @@ impl Block {
|
|||
match push.map_err(|_| Bip34Error::NotPresent)? {
|
||||
script::Instruction::PushBytes(b) => {
|
||||
// Check that the number is encoded in the minimal way.
|
||||
let h = script::read_scriptint(b).map_err(|_e| Bip34Error::UnexpectedPush(b.to_vec()))?;
|
||||
let h = script::read_scriptint(b.as_bytes()).map_err(|_e| Bip34Error::UnexpectedPush(b.as_bytes().to_vec()))?;
|
||||
if h < 0 {
|
||||
Err(Bip34Error::NegativeHeight)
|
||||
} else {
|
||||
|
|
|
@ -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<Vec<u8>, 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 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::fmt;
|
||||
#[cfg(rust_v_1_53)]
|
||||
use core::ops::Bound;
|
||||
|
@ -277,6 +277,14 @@ impl Script {
|
|||
&& self.0[1] == OP_PUSHBYTES_20.to_u8()
|
||||
}
|
||||
|
||||
pub(crate) fn v0_p2wpkh(&self) -> Option<&[u8; 20]> {
|
||||
if self.is_v0_p2wpkh() {
|
||||
Some(self.0[2..].try_into().expect("is_v0_p2wpkh checks the length"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a script pubkey is a P2TR output.
|
||||
#[inline]
|
||||
pub fn is_v1_p2tr(&self) -> bool {
|
||||
|
|
|
@ -9,7 +9,7 @@ use secp256k1::XOnlyPublicKey;
|
|||
|
||||
use crate::blockdata::locktime::absolute;
|
||||
use crate::blockdata::opcodes::{self, all::*};
|
||||
use crate::blockdata::script::{write_scriptint, opcode_to_verify, Script, ScriptBuf};
|
||||
use crate::blockdata::script::{write_scriptint, opcode_to_verify, Script, ScriptBuf, PushBytes};
|
||||
use crate::blockdata::transaction::Sequence;
|
||||
use crate::key::PublicKey;
|
||||
use crate::prelude::*;
|
||||
|
@ -56,11 +56,11 @@ impl Builder {
|
|||
pub(in crate::blockdata) fn push_int_non_minimal(self, data: i64) -> Builder {
|
||||
let mut buf = [0u8; 8];
|
||||
let len = write_scriptint(&mut buf, data);
|
||||
self.push_slice(&buf[..len])
|
||||
self.push_slice(&<&PushBytes>::from(&buf)[..len])
|
||||
}
|
||||
|
||||
/// Adds instructions to push some arbitrary data onto the stack.
|
||||
pub fn push_slice(mut self, data: &[u8]) -> Builder {
|
||||
pub fn push_slice<T: AsRef<PushBytes>>(mut self, data: T) -> Builder {
|
||||
self.0.push_slice(data);
|
||||
self.1 = None;
|
||||
self
|
||||
|
@ -69,15 +69,15 @@ impl Builder {
|
|||
/// Adds instructions to push a public key onto the stack.
|
||||
pub fn push_key(self, key: &PublicKey) -> Builder {
|
||||
if key.compressed {
|
||||
self.push_slice(&key.inner.serialize()[..])
|
||||
self.push_slice(key.inner.serialize())
|
||||
} else {
|
||||
self.push_slice(&key.inner.serialize_uncompressed()[..])
|
||||
self.push_slice(key.inner.serialize_uncompressed())
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds instructions to push an XOnly public key onto the stack.
|
||||
pub fn push_x_only_key(self, x_only_key: &XOnlyPublicKey) -> Builder {
|
||||
self.push_slice(&x_only_key.serialize())
|
||||
self.push_slice(x_only_key.serialize())
|
||||
}
|
||||
|
||||
/// Adds a single opcode to the script.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use core::convert::TryInto;
|
||||
use crate::blockdata::opcodes;
|
||||
use crate::blockdata::script::{read_uint_iter, Error, Script, ScriptBuf, UintError};
|
||||
use crate::blockdata::script::{read_uint_iter, Error, Script, ScriptBuf, UintError, PushBytes};
|
||||
|
||||
/// A "parsed opcode" which allows iterating over a [`Script`] in a more sensible way.
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum Instruction<'a> {
|
||||
/// Push a bunch of data.
|
||||
PushBytes(&'a [u8]),
|
||||
PushBytes(&'a PushBytes),
|
||||
/// Some non-push opcode.
|
||||
Op(opcodes::All),
|
||||
}
|
||||
|
@ -22,8 +23,8 @@ impl<'a> Instruction<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the opcode if the instruction is not a data push.
|
||||
pub fn push_bytes(&self) -> Option<&[u8]> {
|
||||
/// Returns the pushed bytes if the instruction is a data push.
|
||||
pub fn push_bytes(&self) -> Option<&PushBytes> {
|
||||
match self {
|
||||
Instruction::Op(_) => None,
|
||||
Instruction::PushBytes(bytes) => Some(bytes),
|
||||
|
@ -64,22 +65,23 @@ impl<'a> Instructions<'a> {
|
|||
///
|
||||
/// If the iterator is not long enough [`Error::EarlyEndOfScript`] is returned and the iterator
|
||||
/// is killed to avoid returning an infinite stream of errors.
|
||||
pub(super) fn take_slice_or_kill(&mut self, len: usize) -> Result<&'a [u8], Error> {
|
||||
pub(super) fn take_slice_or_kill(&mut self, len: u32) -> Result<&'a PushBytes, Error> {
|
||||
let len = len as usize;
|
||||
if self.data.len() >= len {
|
||||
let slice = &self.data.as_slice()[..len];
|
||||
if len > 0 {
|
||||
self.data.nth(len - 1);
|
||||
}
|
||||
|
||||
Ok(slice)
|
||||
Ok(slice.try_into().expect("len was created from u32, so can't happen"))
|
||||
} else {
|
||||
self.kill();
|
||||
Err(Error::EarlyEndOfScript)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn next_push_data_len(&mut self, len: usize, min_push_len: usize) -> Option<Result<Instruction<'a>, Error>> {
|
||||
let n = match read_uint_iter(&mut self.data, len) {
|
||||
pub(super) fn next_push_data_len(&mut self, len: PushDataLenLen, min_push_len: usize) -> Option<Result<Instruction<'a>, Error>> {
|
||||
let n = match read_uint_iter(&mut self.data, len as usize) {
|
||||
Ok(n) => n,
|
||||
// We do exhaustive matching to not forget to handle new variants if we extend
|
||||
// `UintError` type.
|
||||
|
@ -94,10 +96,24 @@ impl<'a> Instructions<'a> {
|
|||
self.kill();
|
||||
return Some(Err(Error::NonMinimalPush));
|
||||
}
|
||||
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))
|
||||
let result = n
|
||||
.try_into()
|
||||
.map_err(|_| Error::NumericOverflow)
|
||||
.and_then(|n| self.take_slice_or_kill(n))
|
||||
.map(Instruction::PushBytes);
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allowed length of push data length.
|
||||
///
|
||||
/// This makes it easier to prove correctness of `next_push_data_len`.
|
||||
pub(super) enum PushDataLenLen {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Four = 4,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Instructions<'a> {
|
||||
type Item = Result<Instruction<'a>, Error>;
|
||||
|
||||
|
@ -110,8 +126,6 @@ impl<'a> Iterator for Instructions<'a> {
|
|||
opcodes::Class::PushBytes(n) => {
|
||||
// make sure safety argument holds across refactorings
|
||||
let n: u32 = n;
|
||||
// casting is safe because we don't support 16-bit architectures
|
||||
let n = n as usize;
|
||||
|
||||
let op_byte = self.data.as_slice().first();
|
||||
match (self.enforce_minimal, op_byte, n) {
|
||||
|
@ -122,7 +136,7 @@ impl<'a> Iterator for Instructions<'a> {
|
|||
(_, None, 0) => {
|
||||
// the iterator is already empty, may as well use this information to avoid
|
||||
// whole take_slice_or_kill function
|
||||
Some(Ok(Instruction::PushBytes(&[])))
|
||||
Some(Ok(Instruction::PushBytes(PushBytes::empty())))
|
||||
},
|
||||
_ => {
|
||||
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))
|
||||
|
@ -130,13 +144,13 @@ impl<'a> Iterator for Instructions<'a> {
|
|||
}
|
||||
}
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => {
|
||||
self.next_push_data_len(1, 76)
|
||||
self.next_push_data_len(PushDataLenLen::One, 76)
|
||||
}
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => {
|
||||
self.next_push_data_len(2, 0x100)
|
||||
self.next_push_data_len(PushDataLenLen::Two, 0x100)
|
||||
}
|
||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => {
|
||||
self.next_push_data_len(4, 0x10000)
|
||||
self.next_push_data_len(PushDataLenLen::Four, 0x10000)
|
||||
}
|
||||
// Everything else we can push right through
|
||||
_ => {
|
||||
|
|
|
@ -71,11 +71,13 @@ mod instruction;
|
|||
mod owned;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod push_bytes;
|
||||
|
||||
pub use self::borrowed::*;
|
||||
pub use self::builder::*;
|
||||
pub use self::instruction::*;
|
||||
pub use self::owned::*;
|
||||
pub use self::push_bytes::*;
|
||||
|
||||
/// Encodes an integer in script(minimal CScriptNum) format.
|
||||
///
|
||||
|
|
|
@ -8,7 +8,7 @@ use secp256k1::{Secp256k1, Verification};
|
|||
|
||||
use crate::address::{WitnessVersion, WitnessProgram};
|
||||
use crate::blockdata::opcodes::{self, all::*};
|
||||
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script};
|
||||
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script, PushBytes};
|
||||
use crate::hashes::hex;
|
||||
use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
||||
use crate::key::PublicKey;
|
||||
|
@ -98,7 +98,7 @@ impl ScriptBuf {
|
|||
Builder::new()
|
||||
.push_opcode(OP_DUP)
|
||||
.push_opcode(OP_HASH160)
|
||||
.push_slice(&pubkey_hash[..])
|
||||
.push_slice(pubkey_hash)
|
||||
.push_opcode(OP_EQUALVERIFY)
|
||||
.push_opcode(OP_CHECKSIG)
|
||||
.into_script()
|
||||
|
@ -108,7 +108,7 @@ impl ScriptBuf {
|
|||
pub fn new_p2sh(script_hash: &ScriptHash) -> Self {
|
||||
Builder::new()
|
||||
.push_opcode(OP_HASH160)
|
||||
.push_slice(&script_hash[..])
|
||||
.push_slice(script_hash)
|
||||
.push_opcode(OP_EQUAL)
|
||||
.into_script()
|
||||
}
|
||||
|
@ -116,13 +116,13 @@ impl ScriptBuf {
|
|||
/// Generates P2WPKH-type of scriptPubkey.
|
||||
pub fn new_v0_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self {
|
||||
// pubkey hash is 20 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, &pubkey_hash[..])
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, pubkey_hash)
|
||||
}
|
||||
|
||||
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
|
||||
pub fn new_v0_p2wsh(script_hash: &WScriptHash) -> Self {
|
||||
// script hash is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, &script_hash[..])
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
|
||||
}
|
||||
|
||||
/// Generates P2TR for script spending path using an internal public key and some optional
|
||||
|
@ -130,13 +130,13 @@ impl ScriptBuf {
|
|||
pub fn new_v1_p2tr<C: Verification>(secp: &Secp256k1<C>, internal_key: UntweakedPublicKey, merkle_root: Option<TapNodeHash>) -> Self {
|
||||
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
|
||||
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, &output_key.serialize())
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
|
||||
}
|
||||
|
||||
/// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
|
||||
pub fn new_v1_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
|
||||
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, &output_key.serialize())
|
||||
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
|
||||
}
|
||||
|
||||
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
|
||||
|
@ -152,7 +152,8 @@ impl ScriptBuf {
|
|||
///
|
||||
/// Convenience method used by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and
|
||||
/// `new_v1_p2tr_tweaked`.
|
||||
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self {
|
||||
fn new_witness_program_unchecked<T: AsRef<PushBytes>>(version: WitnessVersion, program: T) -> Self {
|
||||
let program = program.as_ref();
|
||||
debug_assert!(program.len() >= 2 && program.len() <= 40);
|
||||
// In segwit v0, the program must be 20 or 32 bytes long.
|
||||
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
|
||||
|
@ -163,7 +164,7 @@ impl ScriptBuf {
|
|||
}
|
||||
|
||||
/// Generates OP_RETURN-type of scriptPubkey for the given data.
|
||||
pub fn new_op_return(data: &[u8]) -> Self {
|
||||
pub fn new_op_return<T: AsRef<PushBytes>>(data: &T) -> Self {
|
||||
Builder::new()
|
||||
.push_opcode(OP_RETURN)
|
||||
.push_slice(data)
|
||||
|
@ -200,18 +201,16 @@ impl ScriptBuf {
|
|||
///
|
||||
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
|
||||
pub fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
|
||||
if !self.is_v0_p2wpkh() {
|
||||
return None
|
||||
}
|
||||
let script = Builder::new()
|
||||
.push_opcode(OP_DUP)
|
||||
.push_opcode(OP_HASH160)
|
||||
.push_slice(&self.as_bytes()[2..]) // The `self` script is 0x00, 0x14, <pubkey_hash>
|
||||
.push_opcode(OP_EQUALVERIFY)
|
||||
.push_opcode(OP_CHECKSIG)
|
||||
.into_script();
|
||||
|
||||
Some(script)
|
||||
self.v0_p2wpkh().map(|wpkh| {
|
||||
Builder::new()
|
||||
.push_opcode(OP_DUP)
|
||||
.push_opcode(OP_HASH160)
|
||||
// The `self` script is 0x00, 0x14, <pubkey_hash>
|
||||
.push_slice(wpkh)
|
||||
.push_opcode(OP_EQUALVERIFY)
|
||||
.push_opcode(OP_CHECKSIG)
|
||||
.into_script()
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds a single opcode to the script.
|
||||
|
@ -220,17 +219,14 @@ impl ScriptBuf {
|
|||
}
|
||||
|
||||
/// Adds instructions to push some arbitrary data onto the stack.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// The method panics if `data` length is greater or equal to 0x100000000.
|
||||
pub fn push_slice(&mut self, data: &[u8]) {
|
||||
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
|
||||
let data = data.as_ref();
|
||||
self.reserve(Self::reserved_len_for_slice(data.len()));
|
||||
self.push_slice_no_opt(data);
|
||||
}
|
||||
|
||||
/// Pushes the slice without reserving
|
||||
fn push_slice_no_opt(&mut self, data: &[u8]) {
|
||||
fn push_slice_no_opt(&mut self, data: &PushBytes) {
|
||||
// Start with a PUSH opcode
|
||||
match data.len() as u64 {
|
||||
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => { self.0.push(n as u8); },
|
||||
|
@ -253,7 +249,7 @@ impl ScriptBuf {
|
|||
_ => panic!("tried to put a 4bn+ sized object into a script!")
|
||||
}
|
||||
// Then push the raw bytes
|
||||
self.0.extend_from_slice(data);
|
||||
self.0.extend_from_slice(data.as_bytes());
|
||||
}
|
||||
|
||||
/// Computes the sum of `len` and the lenght of an appropriate push opcode.
|
||||
|
|
|
@ -0,0 +1,463 @@
|
|||
//! Contains `PushBytes` & co
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
|
||||
pub use primitive::*;
|
||||
|
||||
/// This module only contains required operations so that outside functions wouldn't accidentally
|
||||
/// break invariants. Therefore auditing this module should be sufficient.
|
||||
mod primitive {
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::PushBytesError;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::ops::{Index, Range, RangeFull, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive};
|
||||
#[cfg(feature = "rust_v_1_53")]
|
||||
use core::ops::Bound;
|
||||
|
||||
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
|
||||
fn check_limit(len: usize) -> Result<(), PushBytesError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
|
||||
fn check_limit(len: usize) -> Result<(), PushBytesError> {
|
||||
if len < 0x100000000 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PushBytesError { len })
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte slices that can be in Bitcoin script.
|
||||
///
|
||||
/// The encoding of Bitcoin script restricts data pushes to be less than 2^32 bytes long.
|
||||
/// This type represents slices that are guaranteed to be within the limit so they can be put in
|
||||
/// the script safely.
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct PushBytes([u8]);
|
||||
|
||||
impl PushBytes {
|
||||
/// Creates `&Self` without checking the length.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// The caller is responsible for checking that the length is less than the [`LIMIT`].
|
||||
unsafe fn from_slice_unchecked(bytes: &[u8]) -> &Self {
|
||||
&*(bytes as *const [u8] as *const PushBytes)
|
||||
}
|
||||
|
||||
/// Creates `&mut Self` without checking the length.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// The caller is responsible for checking that the length is less than the [`LIMIT`].
|
||||
unsafe fn from_mut_slice_unchecked(bytes: &mut [u8]) -> &mut Self {
|
||||
&mut *(bytes as *mut [u8] as *mut PushBytes)
|
||||
}
|
||||
|
||||
/// Creates an empty `PushBytes`.
|
||||
pub fn empty() -> &'static Self {
|
||||
// 0 < LIMIT
|
||||
unsafe { Self::from_slice_unchecked(&[]) }
|
||||
}
|
||||
|
||||
/// Returns the underlying bytes.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the underlying mutbale bytes.
|
||||
pub fn as_mut_bytes(&mut self) -> &mut [u8] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! delegate_index {
|
||||
($($type:ty),* $(,)?) => {
|
||||
$(
|
||||
/// Script subslicing operation - read [slicing safety](#slicing-safety)!
|
||||
impl Index<$type> for PushBytes {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(rust_v_1_46, track_caller)]
|
||||
fn index(&self, index: $type) -> &Self::Output {
|
||||
// Slicing can not make slices longer
|
||||
unsafe {
|
||||
Self::from_slice_unchecked(&self.0[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
delegate_index!(Range<usize>, RangeFrom<usize>, RangeTo<usize>, RangeFull, RangeInclusive<usize>, RangeToInclusive<usize>);
|
||||
#[cfg(feature = "rust_v_1_53")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rust_v_1_53")))]
|
||||
delegate_index!((Bound<usize>, Bound<usize>));
|
||||
|
||||
impl Index<usize> for PushBytes {
|
||||
type Output = u8;
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(rust_v_1_46, track_caller)]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for &'a PushBytes {
|
||||
type Error = PushBytesError;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
check_limit(bytes.len())?;
|
||||
// We've just checked the length
|
||||
Ok(unsafe { PushBytes::from_slice_unchecked(bytes) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a mut [u8]> for &'a mut PushBytes {
|
||||
type Error = PushBytesError;
|
||||
|
||||
fn try_from(bytes: &'a mut [u8]) -> Result<Self, Self::Error> {
|
||||
check_limit(bytes.len())?;
|
||||
// We've just checked the length
|
||||
Ok(unsafe { PushBytes::from_mut_slice_unchecked(bytes) })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_array {
|
||||
($($len:literal),* $(,)?) => {
|
||||
$(
|
||||
impl<'a> From<&'a [u8; $len]> for &'a PushBytes {
|
||||
fn from(bytes: &'a [u8; $len]) -> Self {
|
||||
// Check that the macro wasn't called with a wrong number.
|
||||
const _: () = [(); 1][($len >= 0x100000000u64) as usize];
|
||||
// We know the size of array statically and we checked macro input.
|
||||
unsafe { PushBytes::from_slice_unchecked(bytes) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut [u8; $len]> for &'a mut PushBytes {
|
||||
fn from(bytes: &'a mut [u8; $len]) -> Self {
|
||||
// Macro check already above, no need to duplicate.
|
||||
// We know the size of array statically and we checked macro input.
|
||||
unsafe { PushBytes::from_mut_slice_unchecked(bytes) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PushBytes> for [u8; $len] {
|
||||
fn as_ref(&self) -> &PushBytes {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<PushBytes> for [u8; $len] {
|
||||
fn as_mut(&mut self) -> &mut PushBytes {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; $len]> for PushBytesBuf {
|
||||
fn from(bytes: [u8; $len]) -> Self {
|
||||
PushBytesBuf(Vec::from(&bytes as &[_]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8; $len]> for PushBytesBuf {
|
||||
fn from(bytes: &'a [u8; $len]) -> Self {
|
||||
PushBytesBuf(Vec::from(bytes as &[_]))
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
// Sizes up to 73 to support all pubkey and signature sizes
|
||||
from_array! {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
|
||||
71, 72, 73,
|
||||
}
|
||||
|
||||
/// Owned, growable counterpart to `PushBytes`.
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct PushBytesBuf(Vec<u8>);
|
||||
|
||||
impl PushBytesBuf {
|
||||
/// Creates a new empty `PushBytesBuf`.
|
||||
pub fn new() -> Self {
|
||||
PushBytesBuf(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates a new empty `PushBytesBuf` with reserved capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
PushBytesBuf(Vec::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Reserve capacity for `additional_capacity` bytes.
|
||||
pub fn reserve(&mut self, additional_capacity: usize) {
|
||||
self.0.reserve(additional_capacity)
|
||||
}
|
||||
|
||||
/// Try pushing a single byte.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// This method fails if `self` would exceed the limit.
|
||||
#[allow(deprecated)]
|
||||
pub fn push(&mut self, byte: u8) -> Result<(), PushBytesError> {
|
||||
// This is OK on 32 bit archs since vec has its own check and this check is pointless.
|
||||
check_limit(self.0.len().saturating_add(1))?;
|
||||
self.0.push(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Try appending a slice to `PushBytesBuf`
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// This method fails if `self` would exceed the limit.
|
||||
pub fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), PushBytesError> {
|
||||
let len = self.0.len().saturating_add(bytes.len());
|
||||
check_limit(len)?;
|
||||
self.0.extend_from_slice(bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove the last byte from buffer if any.
|
||||
pub fn pop(&mut self) -> Option<u8> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
/// Remove the byte at `index` and return it.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// This method panics if `index` is out of bounds.
|
||||
#[cfg_attr(rust_v_1_46, track_caller)]
|
||||
pub fn remove(&mut self, index: usize) -> u8 {
|
||||
self.0.remove(index)
|
||||
}
|
||||
|
||||
/// Remove all bytes from buffer without affecting capacity.
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
/// Remove bytes from buffer past `len`.
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
self.0.truncate(len)
|
||||
}
|
||||
|
||||
/// Extracts `PushBytes` slice
|
||||
pub fn as_push_bytes(&self) -> &PushBytes {
|
||||
// length guaranteed by our invariant
|
||||
unsafe { PushBytes::from_slice_unchecked(&self.0) }
|
||||
}
|
||||
|
||||
/// Extracts mutable `PushBytes` slice
|
||||
pub fn as_mut_push_bytes(&mut self) -> &mut PushBytes {
|
||||
// length guaranteed by our invariant
|
||||
unsafe { PushBytes::from_mut_slice_unchecked(&mut self.0) }
|
||||
}
|
||||
|
||||
/// Accesses inner `Vec` - provided for `super` to impl other methods.
|
||||
pub(super) fn inner(&self) -> &Vec<u8> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PushBytesBuf> for Vec<u8> {
|
||||
fn from(value: PushBytesBuf) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for PushBytesBuf {
|
||||
type Error = PushBytesError;
|
||||
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
// check len
|
||||
let _: &PushBytes = vec.as_slice().try_into()?;
|
||||
Ok(PushBytesBuf(vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for PushBytes {
|
||||
type Owned = PushBytesBuf;
|
||||
|
||||
fn to_owned(&self) -> Self::Owned {
|
||||
PushBytesBuf(self.0.to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PushBytes {
|
||||
/// Returns the number of bytes in buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_bytes().len()
|
||||
}
|
||||
|
||||
/// Returns true if the buffer contains zero bytes.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.as_bytes().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl PushBytesBuf {
|
||||
/// Returns the number of bytes in buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner().len()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the buffer can contain without reallocating.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.inner().capacity()
|
||||
}
|
||||
|
||||
/// Returns true if the buffer contains zero bytes.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for PushBytes {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for PushBytes {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self.as_mut_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PushBytesBuf {
|
||||
type Target = PushBytes;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for PushBytesBuf {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PushBytes> for PushBytes {
|
||||
fn as_ref(&self) -> &PushBytes {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<PushBytes> for PushBytes {
|
||||
fn as_mut(&mut self) -> &mut PushBytes {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PushBytes> for PushBytesBuf {
|
||||
fn as_ref(&self) -> &PushBytes {
|
||||
self.as_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<PushBytes> for PushBytesBuf {
|
||||
fn as_mut(&mut self) -> &mut PushBytes {
|
||||
self.as_mut_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<PushBytes> for PushBytesBuf {
|
||||
fn borrow(&self) -> &PushBytes {
|
||||
self.as_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowMut<PushBytes> for PushBytesBuf {
|
||||
fn borrow_mut(&mut self) -> &mut PushBytes {
|
||||
self.as_mut_push_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// Reports information about failed conversion into `PushBytes`.
|
||||
///
|
||||
/// This should not be needed by general public, except as an additional bound on `TryFrom` when
|
||||
/// converting to `WitnessProgram`.
|
||||
pub trait PushBytesErrorReport {
|
||||
/// How many bytes the input had.
|
||||
fn input_len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl PushBytesErrorReport for core::convert::Infallible {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
pub use error::*;
|
||||
|
||||
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
|
||||
mod error {
|
||||
use core::fmt;
|
||||
|
||||
/// Error returned on attempt to create too large `PushBytes`.
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PushBytesError {
|
||||
never: core::convert::Infallible,
|
||||
}
|
||||
|
||||
impl super::PushBytesErrorReport for PushBytesError {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
match self.never {}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PushBytesError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.never {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
|
||||
mod error {
|
||||
use core::fmt;
|
||||
|
||||
/// Error returned on attempt to create too large `PushBytes`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PushBytesError {
|
||||
/// How long the input was.
|
||||
pub(super) len: usize
|
||||
}
|
||||
|
||||
impl super::PushBytesErrorReport for PushBytesError {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PushBytesError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "attempt to prepare {} bytes to be pushed into script but the limit is 2^32-1", self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::error::impl_std_error!(PushBytesError);
|
|
@ -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());
|
||||
|
@ -98,9 +97,9 @@ fn p2pk_pubkey_bytes_incorrect_key_size_returns_none() {
|
|||
|
||||
#[test]
|
||||
fn p2pk_pubkey_bytes_invalid_key_returns_some() {
|
||||
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_pubkey_bytes().is_some());
|
||||
|
@ -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]
|
||||
|
@ -198,7 +197,7 @@ fn script_builder() {
|
|||
// from txid 3bb5e6434c11fb93f64574af5d116736510717f2c595eb45b52c28e31622dfff which was in my mempool when I wrote the test
|
||||
let script = Builder::new().push_opcode(OP_DUP)
|
||||
.push_opcode(OP_HASH160)
|
||||
.push_slice(&hex!("16e1ae70ff0fa102905d4af297f6912bda6cce19"))
|
||||
.push_slice(hex!("16e1ae70ff0fa102905d4af297f6912bda6cce19"))
|
||||
.push_opcode(OP_EQUALVERIFY)
|
||||
.push_opcode(OP_CHECKSIG)
|
||||
.into_script();
|
||||
|
@ -231,7 +230,7 @@ fn script_generators() {
|
|||
|
||||
// Test data are taken from the second output of
|
||||
// 2ccb3a1f745eb4eefcf29391460250adda5fab78aaddb902d25d3cd97d9d8e61 transaction
|
||||
let data = Vec::<u8>::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");
|
||||
|
@ -299,7 +298,7 @@ fn script_builder_verify() {
|
|||
assert_eq!(checkmultisig2.to_hex_string(), "af");
|
||||
|
||||
let trick_slice = Builder::new()
|
||||
.push_slice(&[0xae]) // OP_CHECKMULTISIG
|
||||
.push_slice([0xae]) // OP_CHECKMULTISIG
|
||||
.push_verify()
|
||||
.into_script();
|
||||
assert_eq!(trick_slice.to_hex_string(), "01ae69");
|
||||
|
@ -314,7 +313,7 @@ fn script_serialize() {
|
|||
let hex_script = hex!("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52");
|
||||
let script: Result<ScriptBuf, _> = deserialize(&hex_script);
|
||||
assert!(script.is_ok());
|
||||
assert_eq!(serialize(&script.unwrap()), hex_script);
|
||||
assert_eq!(serialize(&script.unwrap()), &hex_script as &[u8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -499,19 +498,19 @@ fn test_iterator() {
|
|||
|
||||
unwrap_all!(v_zero, v_zeropush, v_min, v_nonmin_alt, slop_v_min, slop_v_nonmin, slop_v_nonmin_alt);
|
||||
|
||||
assert_eq!(v_zero, vec![(0, Instruction::PushBytes(&[]))]);
|
||||
assert_eq!(v_zeropush, vec![(0, Instruction::PushBytes(&[0]))]);
|
||||
assert_eq!(v_zero, vec![(0, Instruction::PushBytes(PushBytes::empty()))]);
|
||||
assert_eq!(v_zeropush, vec![(0, Instruction::PushBytes([0].as_ref()))]);
|
||||
|
||||
assert_eq!(
|
||||
v_min,
|
||||
vec![(0, Instruction::PushBytes(&[105])), (2, Instruction::Op(opcodes::OP_NOP3))]
|
||||
vec![(0, Instruction::PushBytes([105].as_ref())), (2, Instruction::Op(opcodes::OP_NOP3))]
|
||||
);
|
||||
|
||||
assert_eq!(v_nonmin.unwrap_err(), Error::NonMinimalPush);
|
||||
|
||||
assert_eq!(
|
||||
v_nonmin_alt,
|
||||
vec![(0, Instruction::PushBytes(&[105, 0])), (3, Instruction::Op(opcodes::OP_NOP3))]
|
||||
vec![(0, Instruction::PushBytes([105, 0].as_ref())), (3, Instruction::Op(opcodes::OP_NOP3))]
|
||||
);
|
||||
|
||||
assert_eq!(v_min, slop_v_min);
|
||||
|
@ -526,7 +525,7 @@ fn test_iterator() {
|
|||
|
||||
#[test]
|
||||
fn script_ord() {
|
||||
let script_1 = Builder::new().push_slice(&[1, 2, 3, 4]).into_script();
|
||||
let script_1 = Builder::new().push_slice([1, 2, 3, 4]).into_script();
|
||||
let script_2 = Builder::new().push_int(10).into_script();
|
||||
let script_3 = Builder::new().push_int(15).into_script();
|
||||
let script_4 = Builder::new().push_opcode(OP_RETURN).into_script();
|
||||
|
@ -547,23 +546,24 @@ 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]
|
||||
fn defult_dust_value_tests() {
|
||||
// Check that our dust_value() calculator correctly calculates the dust limit on common
|
||||
// well-known scriptPubKey types.
|
||||
let script_p2wpkh = Builder::new().push_int(0).push_slice(&[42; 20]).into_script();
|
||||
let script_p2wpkh = Builder::new().push_int(0).push_slice([42; 20]).into_script();
|
||||
assert!(script_p2wpkh.is_v0_p2wpkh());
|
||||
assert_eq!(script_p2wpkh.dust_value(), crate::Amount::from_sat(294));
|
||||
|
||||
let script_p2pkh = Builder::new()
|
||||
.push_opcode(OP_DUP)
|
||||
.push_opcode(OP_HASH160)
|
||||
.push_slice(&[42; 20])
|
||||
.push_slice([42; 20])
|
||||
.push_opcode(OP_EQUALVERIFY)
|
||||
.push_opcode(OP_CHECKSIG)
|
||||
.into_script();
|
||||
|
@ -611,7 +611,7 @@ fn script_extend() {
|
|||
let script_5_items = [
|
||||
Instruction::Op(OP_DUP),
|
||||
Instruction::Op(OP_HASH160),
|
||||
Instruction::PushBytes(&[42; 20]),
|
||||
Instruction::PushBytes([42; 20].as_ref()),
|
||||
Instruction::Op(OP_EQUALVERIFY),
|
||||
Instruction::Op(OP_CHECKSIG),
|
||||
];
|
||||
|
@ -621,7 +621,7 @@ fn script_extend() {
|
|||
let script_6_items = [
|
||||
Instruction::Op(OP_DUP),
|
||||
Instruction::Op(OP_HASH160),
|
||||
Instruction::PushBytes(&[42; 20]),
|
||||
Instruction::PushBytes([42; 20].as_ref()),
|
||||
Instruction::Op(OP_EQUALVERIFY),
|
||||
Instruction::Op(OP_CHECKSIG),
|
||||
Instruction::Op(OP_NOP),
|
||||
|
@ -632,7 +632,7 @@ fn script_extend() {
|
|||
let script_7_items = [
|
||||
Instruction::Op(OP_DUP),
|
||||
Instruction::Op(OP_HASH160),
|
||||
Instruction::PushBytes(&[42; 20]),
|
||||
Instruction::PushBytes([42; 20].as_ref()),
|
||||
Instruction::Op(OP_EQUALVERIFY),
|
||||
Instruction::Op(OP_CHECKSIG),
|
||||
Instruction::Op(OP_NOP),
|
||||
|
|
|
@ -9,11 +9,15 @@ use core::str::FromStr;
|
|||
use core::{fmt, iter};
|
||||
|
||||
use bitcoin_internals::write_err;
|
||||
use bitcoin_internals::hex::display::DisplayHex;
|
||||
use secp256k1;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::hashes::hex::{self, FromHex};
|
||||
use crate::sighash::{EcdsaSighashType, NonStandardSighashType};
|
||||
use crate::script::PushBytes;
|
||||
|
||||
const MAX_SIG_LEN: usize = 73;
|
||||
|
||||
/// An ECDSA signature with the corresponding hash type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -47,6 +51,23 @@ impl Signature {
|
|||
}
|
||||
|
||||
/// Serializes an ECDSA signature (inner secp256k1 signature in DER format).
|
||||
///
|
||||
/// This does **not** perform extra heap allocation.
|
||||
pub fn serialize(&self) -> SerializedSignature {
|
||||
let mut buf = [0u8; MAX_SIG_LEN];
|
||||
let signature = self.sig.serialize_der();
|
||||
buf[..signature.len()].copy_from_slice(&signature);
|
||||
buf[signature.len()] = self.hash_ty as u8;
|
||||
SerializedSignature {
|
||||
data: buf,
|
||||
len: signature.len() + 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an ECDSA signature (inner secp256k1 signature in DER format) into `Vec`.
|
||||
///
|
||||
/// Note: this performs an extra heap allocation, you might prefer the
|
||||
/// [`serialize`](Self::serialize) method instead.
|
||||
pub fn to_vec(self) -> Vec<u8> {
|
||||
// TODO: add support to serialize to a writer to SerializedSig
|
||||
self.sig.serialize_der()
|
||||
|
@ -77,6 +98,125 @@ impl FromStr for Signature {
|
|||
}
|
||||
}
|
||||
|
||||
/// Holds signature serialized in-line (not in `Vec`).
|
||||
///
|
||||
/// This avoids allocation and allows proving maximum size of the signature (73 bytes).
|
||||
/// The type can be used largely as a byte slice. It implements all standard traits one would
|
||||
/// expect and has familiar methods.
|
||||
/// However, the usual use case is to push it into a script. This can be done directly passing it
|
||||
/// into [`push_slice`](crate::script::ScriptBuf::push_slice).
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SerializedSignature {
|
||||
data: [u8; MAX_SIG_LEN],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl SerializedSignature {
|
||||
/// Returns an iterator over bytes of the signature.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, u8> {
|
||||
self.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for SerializedSignature {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data[..self.len]
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for SerializedSignature {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data[..self.len]
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SerializedSignature {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for SerializedSignature {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PushBytes> for SerializedSignature {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &PushBytes {
|
||||
&<&PushBytes>::from(&self.data)[..self.len()]
|
||||
}
|
||||
}
|
||||
|
||||
impl core::borrow::Borrow<[u8]> for SerializedSignature {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl core::borrow::BorrowMut<[u8]> for SerializedSignature {
|
||||
#[inline]
|
||||
fn borrow_mut(&mut self) -> &mut [u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SerializedSignature {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
|
||||
}
|
||||
|
||||
impl fmt::Display for SerializedSignature {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for SerializedSignature {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::LowerHex::fmt(&(**self).as_hex(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for SerializedSignature {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::UpperHex::fmt(&(**self).as_hex(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SerializedSignature {
|
||||
#[inline]
|
||||
fn eq(&self, other: &SerializedSignature) -> bool { **self == **other }
|
||||
}
|
||||
|
||||
impl Eq for SerializedSignature {}
|
||||
|
||||
impl core::hash::Hash for SerializedSignature {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&**self, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a SerializedSignature {
|
||||
type IntoIter = core::slice::Iter<'a, u8>;
|
||||
type Item = &'a u8;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
(*self).iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// A key-related error.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
|
|
|
@ -27,6 +27,27 @@ macro_rules! impl_hashencode {
|
|||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! impl_asref_push_bytes {
|
||||
($($hashtype:ident),*) => {
|
||||
$(
|
||||
impl AsRef<$crate::blockdata::script::PushBytes> for $hashtype {
|
||||
fn as_ref(&self) -> &$crate::blockdata::script::PushBytes {
|
||||
use $crate::hashes::Hash;
|
||||
self.as_inner().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$hashtype> for $crate::blockdata::script::PushBytesBuf {
|
||||
fn from(hash: $hashtype) -> Self {
|
||||
use $crate::hashes::Hash;
|
||||
hash.as_inner().into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// newtypes module is solely here so we can rustfmt::skip.
|
||||
pub use newtypes::*;
|
||||
|
||||
|
@ -75,4 +96,6 @@ See [`hashes::Hash::DISPLAY_BACKWARD`] for more details.
|
|||
|
||||
impl_hashencode!(FilterHash);
|
||||
impl_hashencode!(FilterHeader);
|
||||
|
||||
impl_asref_push_bytes!(PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash);
|
||||
}
|
||||
|
|
|
@ -1641,15 +1641,15 @@ mod tests {
|
|||
let cache = cache.segwit_cache();
|
||||
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
|
||||
assert_eq!(
|
||||
cache.prevouts.into_inner().as_ref(),
|
||||
cache.prevouts.into_inner(),
|
||||
&Vec::from_hex("96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.sequences.into_inner().as_ref(),
|
||||
cache.sequences.into_inner(),
|
||||
&Vec::from_hex("52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.outputs.into_inner().as_ref(),
|
||||
cache.outputs.into_inner(),
|
||||
&Vec::from_hex("863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5").unwrap()[..],
|
||||
);
|
||||
}
|
||||
|
@ -1677,15 +1677,15 @@ mod tests {
|
|||
let cache = cache.segwit_cache();
|
||||
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
|
||||
assert_eq!(
|
||||
cache.prevouts.into_inner().as_ref(),
|
||||
cache.prevouts.into_inner(),
|
||||
&Vec::from_hex("b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.sequences.into_inner().as_ref(),
|
||||
cache.sequences.into_inner(),
|
||||
&Vec::from_hex("18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.outputs.into_inner().as_ref(),
|
||||
cache.outputs.into_inner(),
|
||||
&Vec::from_hex("de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83").unwrap()[..],
|
||||
);
|
||||
}
|
||||
|
@ -1718,15 +1718,15 @@ mod tests {
|
|||
let cache = cache.segwit_cache();
|
||||
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
|
||||
assert_eq!(
|
||||
cache.prevouts.into_inner().as_ref(),
|
||||
cache.prevouts.into_inner(),
|
||||
&Vec::from_hex("74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.sequences.into_inner().as_ref(),
|
||||
cache.sequences.into_inner(),
|
||||
&Vec::from_hex("3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044").unwrap()[..],
|
||||
);
|
||||
assert_eq!(
|
||||
cache.outputs.into_inner().as_ref(),
|
||||
cache.outputs.into_inner(),
|
||||
&Vec::from_hex("bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc").unwrap()[..],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use bitcoin::bip32::{ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivationPath, KeySource};
|
||||
use bitcoin::blockdata::opcodes::OP_0;
|
||||
|
@ -15,6 +16,7 @@ use bitcoin::{
|
|||
absolute, Amount, Denomination, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence,
|
||||
Transaction, TxIn, TxOut, Witness,
|
||||
};
|
||||
use bitcoin::script::PushBytes;
|
||||
|
||||
const NETWORK: Network = Network::Testnet;
|
||||
|
||||
|
@ -422,9 +424,9 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
|
|||
let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect();
|
||||
let script_sig = script::Builder::new()
|
||||
.push_opcode(OP_0) // OP_CHECKMULTISIG bug pops +1 value when evaluating so push OP_0.
|
||||
.push_slice(&sigs[0].to_vec())
|
||||
.push_slice(&sigs[1].to_vec())
|
||||
.push_slice(psbt.inputs[0].redeem_script.clone().unwrap().as_bytes())
|
||||
.push_slice(sigs[0].serialize())
|
||||
.push_slice(sigs[1].serialize())
|
||||
.push_slice(<&PushBytes>::try_from(psbt.inputs[0].redeem_script.as_ref().unwrap().as_bytes()).unwrap())
|
||||
.into_script();
|
||||
|
||||
psbt.inputs[0].final_script_sig = Some(script_sig);
|
||||
|
@ -437,7 +439,7 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
|
|||
// Input 1: SegWit UTXO
|
||||
|
||||
let script_sig = script::Builder::new()
|
||||
.push_slice(psbt.inputs[1].redeem_script.as_ref().unwrap().as_bytes())
|
||||
.push_slice(<&PushBytes>::try_from(psbt.inputs[1].redeem_script.as_ref().unwrap().as_bytes()).unwrap())
|
||||
.into_script();
|
||||
|
||||
psbt.inputs[1].final_script_sig = Some(script_sig);
|
||||
|
|
Loading…
Reference in New Issue