use arrayvec to represent witness programs
This commit is contained in:
parent
471da86e5a
commit
01df1417c7
|
@ -44,7 +44,7 @@ use crate::blockdata::constants::{
|
||||||
};
|
};
|
||||||
use crate::blockdata::script::witness_program::WitnessProgram;
|
use crate::blockdata::script::witness_program::WitnessProgram;
|
||||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
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::{
|
use crate::crypto::key::{
|
||||||
CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey,
|
CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey,
|
||||||
};
|
};
|
||||||
|
@ -520,10 +520,8 @@ impl Address {
|
||||||
} else if script.is_witness_program() {
|
} else if script.is_witness_program() {
|
||||||
let opcode = script.first_opcode().expect("is_witness_program guarantees len > 4");
|
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 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))
|
Ok(Address::from_witness_program(program, network))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnrecognizedScript)
|
Err(Error::UnrecognizedScript)
|
||||||
|
@ -727,8 +725,7 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
|
||||||
if let Ok((hrp, witness_version, data)) = bech32::segwit::decode(s) {
|
if let Ok((hrp, witness_version, data)) = bech32::segwit::decode(s) {
|
||||||
let version = WitnessVersion::try_from(witness_version)?;
|
let version = WitnessVersion::try_from(witness_version)?;
|
||||||
let buf = PushBytesBuf::try_from(data).expect("bech32 guarantees valid program length");
|
let program = WitnessProgram::new(version, &data)
|
||||||
let program = WitnessProgram::new(version, buf)
|
|
||||||
.expect("bech32 guarantees valid program length for witness");
|
.expect("bech32 guarantees valid program length for witness");
|
||||||
|
|
||||||
let hrp = KnownHrp::from_hrp(hrp)?;
|
let hrp = KnownHrp::from_hrp(hrp)?;
|
||||||
|
@ -924,10 +921,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_existent_segwit_version() {
|
fn test_non_existent_segwit_version() {
|
||||||
// 40-byte program
|
// 40-byte program
|
||||||
let program = PushBytesBuf::from(hex!(
|
let program = hex!(
|
||||||
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
|
"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);
|
let addr = Address::from_witness_program(program, Bitcoin);
|
||||||
roundtrips(&addr, Bitcoin);
|
roundtrips(&addr, Bitcoin);
|
||||||
|
|
|
@ -10,13 +10,20 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use hashes::Hash as _;
|
use hashes::Hash as _;
|
||||||
|
use internals::array_vec::ArrayVec;
|
||||||
use secp256k1::{Secp256k1, Verification};
|
use secp256k1::{Secp256k1, Verification};
|
||||||
|
|
||||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
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::crypto::key::{CompressedPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
||||||
use crate::taproot::TapNodeHash;
|
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.
|
||||||
///
|
///
|
||||||
/// The segregated witness program is technically only the program bytes _excluding_ the witness
|
/// 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.
|
/// The segwit version associated with this witness program.
|
||||||
version: WitnessVersion,
|
version: WitnessVersion,
|
||||||
/// The witness program (between 2 and 40 bytes).
|
/// The witness program (between 2 and 40 bytes).
|
||||||
program: PushBytesBuf,
|
program: ArrayVec<u8, MAX_SIZE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WitnessProgram {
|
impl WitnessProgram {
|
||||||
/// Creates a new witness program.
|
/// Creates a new witness program, copying the content from the given byte slice.
|
||||||
pub fn new<P>(version: WitnessVersion, program: P) -> Result<Self, Error>
|
pub fn new(version: WitnessVersion, bytes: &[u8]) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
P: TryInto<PushBytesBuf>,
|
|
||||||
<P as TryInto<PushBytesBuf>>::Error: PushBytesErrorReport,
|
|
||||||
{
|
|
||||||
use Error::*;
|
use Error::*;
|
||||||
|
|
||||||
let program = program.try_into().map_err(|error| InvalidLength(error.input_len()))?;
|
let program_len = bytes.len();
|
||||||
if program.len() < 2 || program.len() > 40 {
|
if program_len < MIN_SIZE || program_len > MAX_SIZE {
|
||||||
return Err(InvalidLength(program.len()));
|
return Err(InvalidLength(program_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific segwit v0 check. These addresses can never spend funds sent to them.
|
// Specific segwit v0 check. These addresses can never spend funds sent to them.
|
||||||
if version == WitnessVersion::V0 && (program.len() != 20 && program.len() != 32) {
|
if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
|
||||||
return Err(InvalidSegwitV0Length(program.len()));
|
return Err(InvalidSegwitV0Length(program_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let program = ArrayVec::from_slice(bytes);
|
||||||
Ok(WitnessProgram { version, program })
|
Ok(WitnessProgram { version, program })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`WitnessProgram`] from a 20 byte pubkey hash.
|
/// Creates a [`WitnessProgram`] from a 20 byte pubkey hash.
|
||||||
fn new_p2wpkh(program: [u8; 20]) -> Self {
|
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.
|
/// Creates a [`WitnessProgram`] from a 32 byte script hash.
|
||||||
fn new_p2wsh(program: [u8; 32]) -> Self {
|
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.
|
/// Creates a [`WitnessProgram`] from a 32 byte serialize taproot xonly pubkey.
|
||||||
fn new_p2tr(program: [u8; 32]) -> Self {
|
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.
|
/// Creates a [`WitnessProgram`] from `pk` for a P2WPKH output.
|
||||||
|
@ -99,7 +104,12 @@ impl WitnessProgram {
|
||||||
pub fn version(&self) -> WitnessVersion { self.version }
|
pub fn version(&self) -> WitnessVersion { self.version }
|
||||||
|
|
||||||
/// Returns the witness program.
|
/// 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.
|
/// Returns true if this witness program is for a P2WPKH output.
|
||||||
pub fn is_p2wpkh(&self) -> bool {
|
pub fn is_p2wpkh(&self) -> bool {
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
// Exclude clippy lints we don't think are valuable
|
// 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::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::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.
|
// Disable 16-bit support at least for now as we can't guarantee it yet.
|
||||||
#[cfg(target_pointer_width = "16")]
|
#[cfg(target_pointer_width = "16")]
|
||||||
|
|
|
@ -2087,7 +2087,7 @@ mod tests {
|
||||||
psbt.inputs[0].bip32_derivation = map;
|
psbt.inputs[0].bip32_derivation = map;
|
||||||
|
|
||||||
// Second input is unspendable by us e.g., from another wallet that supports future upgrades.
|
// 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 {
|
let txout_unknown_future = TxOut {
|
||||||
value: Amount::from_sat(10),
|
value: Amount::from_sat(10),
|
||||||
script_pubkey: ScriptBuf::new_witness_program(&unknown_prog),
|
script_pubkey: ScriptBuf::new_witness_program(&unknown_prog),
|
||||||
|
|
Loading…
Reference in New Issue