Merge rust-bitcoin/rust-bitcoin#2310: use arrayvec to represent witness programs internally
01df1417c7
use arrayvec to represent witness programs (conduition) Pull request description: Fixes https://github.com/rust-bitcoin/rust-bitcoin/issues/2261 Introduces a new constructor, `WitnessProgram::from_bytes`, which creates a witness program by copying the program content from a byte slice. ACKs for top commit: Kixunil: ACK01df1417c7
apoelstra: ACK01df1417c7
Tree-SHA512: 73b8f2785674cd99c3f5dfe0e2180ed256942a0c29bcb1d357e0bd84fddee5e62f3f230c6cd55a37322bc3a6011467e9b7dcf24d903b20f35c095a1a1f9a29ce
This commit is contained in:
commit
5108f6ecdd
|
@ -44,7 +44,7 @@ use crate::blockdata::constants::{
|
|||
};
|
||||
use crate::blockdata::script::witness_program::WitnessProgram;
|
||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
||||
use crate::blockdata::script::{self, PushBytesBuf, Script, ScriptBuf, ScriptHash};
|
||||
use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash};
|
||||
use crate::crypto::key::{
|
||||
CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey,
|
||||
};
|
||||
|
@ -520,10 +520,8 @@ impl Address {
|
|||
} else if script.is_witness_program() {
|
||||
let opcode = script.first_opcode().expect("is_witness_program guarantees len > 4");
|
||||
|
||||
let buf = PushBytesBuf::try_from(script.as_bytes()[2..].to_vec())
|
||||
.expect("is_witness_program guarantees len <= 42 bytes");
|
||||
let version = WitnessVersion::try_from(opcode)?;
|
||||
let program = WitnessProgram::new(version, buf)?;
|
||||
let program = WitnessProgram::new(version, &script.as_bytes()[2..])?;
|
||||
Ok(Address::from_witness_program(program, network))
|
||||
} else {
|
||||
Err(Error::UnrecognizedScript)
|
||||
|
@ -727,8 +725,7 @@ impl FromStr for Address<NetworkUnchecked> {
|
|||
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
||||
if let Ok((hrp, witness_version, data)) = bech32::segwit::decode(s) {
|
||||
let version = WitnessVersion::try_from(witness_version)?;
|
||||
let buf = PushBytesBuf::try_from(data).expect("bech32 guarantees valid program length");
|
||||
let program = WitnessProgram::new(version, buf)
|
||||
let program = WitnessProgram::new(version, &data)
|
||||
.expect("bech32 guarantees valid program length for witness");
|
||||
|
||||
let hrp = KnownHrp::from_hrp(hrp)?;
|
||||
|
@ -924,10 +921,10 @@ mod tests {
|
|||
#[test]
|
||||
fn test_non_existent_segwit_version() {
|
||||
// 40-byte program
|
||||
let program = PushBytesBuf::from(hex!(
|
||||
let program = hex!(
|
||||
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
|
||||
));
|
||||
let program = WitnessProgram::new(WitnessVersion::V13, program).expect("valid program");
|
||||
);
|
||||
let program = WitnessProgram::new(WitnessVersion::V13, &program).expect("valid program");
|
||||
|
||||
let addr = Address::from_witness_program(program, Bitcoin);
|
||||
roundtrips(&addr, Bitcoin);
|
||||
|
|
|
@ -10,13 +10,20 @@
|
|||
use core::fmt;
|
||||
|
||||
use hashes::Hash as _;
|
||||
use internals::array_vec::ArrayVec;
|
||||
use secp256k1::{Secp256k1, Verification};
|
||||
|
||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
||||
use crate::blockdata::script::{PushBytes, PushBytesBuf, PushBytesErrorReport, Script};
|
||||
use crate::blockdata::script::{PushBytes, Script};
|
||||
use crate::crypto::key::{CompressedPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
||||
use crate::taproot::TapNodeHash;
|
||||
|
||||
/// The minimum byte size of a segregated witness program.
|
||||
pub const MIN_SIZE: usize = 2;
|
||||
|
||||
/// The maximum byte size of a segregated witness program.
|
||||
pub const MAX_SIZE: usize = 40;
|
||||
|
||||
/// The segregated witness program.
|
||||
///
|
||||
/// The segregated witness program is technically only the program bytes _excluding_ the witness
|
||||
|
@ -27,43 +34,41 @@ pub struct WitnessProgram {
|
|||
/// The segwit version associated with this witness program.
|
||||
version: WitnessVersion,
|
||||
/// The witness program (between 2 and 40 bytes).
|
||||
program: PushBytesBuf,
|
||||
program: ArrayVec<u8, MAX_SIZE>,
|
||||
}
|
||||
|
||||
impl WitnessProgram {
|
||||
/// Creates a new witness program.
|
||||
pub fn new<P>(version: WitnessVersion, program: P) -> Result<Self, Error>
|
||||
where
|
||||
P: TryInto<PushBytesBuf>,
|
||||
<P as TryInto<PushBytesBuf>>::Error: PushBytesErrorReport,
|
||||
{
|
||||
/// Creates a new witness program, copying the content from the given byte slice.
|
||||
pub fn new(version: WitnessVersion, bytes: &[u8]) -> Result<Self, Error> {
|
||||
use Error::*;
|
||||
|
||||
let program = program.try_into().map_err(|error| InvalidLength(error.input_len()))?;
|
||||
if program.len() < 2 || program.len() > 40 {
|
||||
return Err(InvalidLength(program.len()));
|
||||
let program_len = bytes.len();
|
||||
if program_len < MIN_SIZE || program_len > MAX_SIZE {
|
||||
return Err(InvalidLength(program_len));
|
||||
}
|
||||
|
||||
// Specific segwit v0 check. These addresses can never spend funds sent to them.
|
||||
if version == WitnessVersion::V0 && (program.len() != 20 && program.len() != 32) {
|
||||
return Err(InvalidSegwitV0Length(program.len()));
|
||||
if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
|
||||
return Err(InvalidSegwitV0Length(program_len));
|
||||
}
|
||||
|
||||
let program = ArrayVec::from_slice(bytes);
|
||||
Ok(WitnessProgram { version, program })
|
||||
}
|
||||
|
||||
/// Creates a [`WitnessProgram`] from a 20 byte pubkey hash.
|
||||
fn new_p2wpkh(program: [u8; 20]) -> Self {
|
||||
WitnessProgram { version: WitnessVersion::V0, program: program.into() }
|
||||
WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
|
||||
}
|
||||
|
||||
/// Creates a [`WitnessProgram`] from a 32 byte script hash.
|
||||
fn new_p2wsh(program: [u8; 32]) -> Self {
|
||||
WitnessProgram { version: WitnessVersion::V0, program: program.into() }
|
||||
WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
|
||||
}
|
||||
|
||||
/// Creates a [`WitnessProgram`] from a 32 byte serialize taproot xonly pubkey.
|
||||
fn new_p2tr(program: [u8; 32]) -> Self {
|
||||
WitnessProgram { version: WitnessVersion::V1, program: program.into() }
|
||||
WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&program) }
|
||||
}
|
||||
|
||||
/// Creates a [`WitnessProgram`] from `pk` for a P2WPKH output.
|
||||
|
@ -99,7 +104,12 @@ impl WitnessProgram {
|
|||
pub fn version(&self) -> WitnessVersion { self.version }
|
||||
|
||||
/// Returns the witness program.
|
||||
pub fn program(&self) -> &PushBytes { &self.program }
|
||||
pub fn program(&self) -> &PushBytes {
|
||||
self.program
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("witness programs are always smaller than max size of PushBytes")
|
||||
}
|
||||
|
||||
/// Returns true if this witness program is for a P2WPKH output.
|
||||
pub fn is_p2wpkh(&self) -> bool {
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
// Exclude clippy lints we don't think are valuable
|
||||
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
|
||||
#![allow(clippy::uninhabited_references)] // falsely claims that 100% safe code is UB
|
||||
#![allow(clippy::manual_range_contains)] // more readable than clippy's format
|
||||
|
||||
// Disable 16-bit support at least for now as we can't guarantee it yet.
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
|
|
|
@ -2087,7 +2087,7 @@ mod tests {
|
|||
psbt.inputs[0].bip32_derivation = map;
|
||||
|
||||
// Second input is unspendable by us e.g., from another wallet that supports future upgrades.
|
||||
let unknown_prog = WitnessProgram::new(WitnessVersion::V4, vec![0xaa; 34]).unwrap();
|
||||
let unknown_prog = WitnessProgram::new(WitnessVersion::V4, &[0xaa; 34]).unwrap();
|
||||
let txout_unknown_future = TxOut {
|
||||
value: Amount::from_sat(10),
|
||||
script_pubkey: ScriptBuf::new_witness_program(&unknown_prog),
|
||||
|
|
Loading…
Reference in New Issue