Merge pull request #617 from LNP-BP/feat/witness-version
WitnessVersion type
This commit is contained in:
commit
2a655f4b58
|
@ -41,6 +41,7 @@ use policy::DUST_RELAY_TX_FEE;
|
||||||
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||||
|
|
||||||
use util::ecdsa::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
|
use util::address::WitnessVersion;
|
||||||
|
|
||||||
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||||
/// A Bitcoin script
|
/// A Bitcoin script
|
||||||
|
@ -268,23 +269,18 @@ impl Script {
|
||||||
|
|
||||||
/// Generates P2WPKH-type of scriptPubkey
|
/// Generates P2WPKH-type of scriptPubkey
|
||||||
pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
|
pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
|
||||||
Script::new_witness_program(::bech32::u5::try_from_u8(0).unwrap(), &pubkey_hash.to_vec())
|
Script::new_witness_program(WitnessVersion::V0, &pubkey_hash.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
|
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
|
||||||
pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script {
|
pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script {
|
||||||
Script::new_witness_program(::bech32::u5::try_from_u8(0).unwrap(), &script_hash.to_vec())
|
Script::new_witness_program(WitnessVersion::V0, &script_hash.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
|
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
|
||||||
pub fn new_witness_program(ver: ::bech32::u5, program: &[u8]) -> Script {
|
pub fn new_witness_program(version: WitnessVersion, program: &[u8]) -> Script {
|
||||||
let mut verop = ver.to_u8();
|
|
||||||
assert!(verop <= 16, "incorrect witness version provided: {}", verop);
|
|
||||||
if verop > 0 {
|
|
||||||
verop = 0x50 + verop;
|
|
||||||
}
|
|
||||||
Builder::new()
|
Builder::new()
|
||||||
.push_opcode(verop.into())
|
.push_opcode(version.into())
|
||||||
.push_slice(&program)
|
.push_slice(&program)
|
||||||
.into_script()
|
.into_script()
|
||||||
}
|
}
|
||||||
|
@ -371,17 +367,17 @@ impl Script {
|
||||||
// push opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new
|
// push opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new
|
||||||
// special meaning. The value of the first push is called the "version byte". The following
|
// special meaning. The value of the first push is called the "version byte". The following
|
||||||
// byte vector pushed is called the "witness program".
|
// byte vector pushed is called the "witness program".
|
||||||
let min_vernum: u8 = opcodes::all::OP_PUSHNUM_1.into_u8();
|
let script_len = self.0.len();
|
||||||
let max_vernum: u8 = opcodes::all::OP_PUSHNUM_16.into_u8();
|
if script_len < 4 || script_len > 42 {
|
||||||
self.0.len() >= 4
|
return false
|
||||||
&& self.0.len() <= 42
|
}
|
||||||
// Version 0 or PUSHNUM_1-PUSHNUM_16
|
let ver_opcode = opcodes::All::from(self.0[0]); // Version 0 or PUSHNUM_1-PUSHNUM_16
|
||||||
&& (self.0[0] == 0 || self.0[0] >= min_vernum && self.0[0] <= max_vernum)
|
let push_opbyte = self.0[1]; // Second byte push opcode 2-40 bytes
|
||||||
// Second byte push opcode 2-40 bytes
|
WitnessVersion::from_opcode(ver_opcode).is_ok()
|
||||||
&& self.0[1] >= opcodes::all::OP_PUSHBYTES_2.into_u8()
|
&& push_opbyte >= opcodes::all::OP_PUSHBYTES_2.into_u8()
|
||||||
&& self.0[1] <= opcodes::all::OP_PUSHBYTES_40.into_u8()
|
&& push_opbyte <= opcodes::all::OP_PUSHBYTES_40.into_u8()
|
||||||
// Check that the rest of the script has the correct size
|
// Check that the rest of the script has the correct size
|
||||||
&& self.0.len() - 2 == self.0[1] as usize
|
&& script_len - 2 == push_opbyte as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a script pubkey is a p2wsh output
|
/// Checks whether a script pubkey is a p2wsh output
|
||||||
|
|
|
@ -36,20 +36,22 @@
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use core::num::ParseIntError;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
#[cfg(feature = "std")] use std::error;
|
#[cfg(feature = "std")] use std::error;
|
||||||
|
|
||||||
use bech32;
|
use bech32;
|
||||||
use hashes::Hash;
|
use hashes::Hash;
|
||||||
use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
||||||
use blockdata::script;
|
use blockdata::{script, opcodes};
|
||||||
use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST};
|
use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST};
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::base58;
|
use util::base58;
|
||||||
use util::ecdsa;
|
use util::ecdsa;
|
||||||
|
use blockdata::script::Instruction;
|
||||||
|
|
||||||
/// Address error.
|
/// Address error.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Base58 encoding error
|
/// Base58 encoding error
|
||||||
Base58(base58::Error),
|
Base58(base58::Error),
|
||||||
|
@ -59,6 +61,10 @@ pub enum Error {
|
||||||
EmptyBech32Payload,
|
EmptyBech32Payload,
|
||||||
/// Script version must be 0 to 16 inclusive
|
/// Script version must be 0 to 16 inclusive
|
||||||
InvalidWitnessVersion(u8),
|
InvalidWitnessVersion(u8),
|
||||||
|
/// Unable to parse witness version from string
|
||||||
|
UnparsableWitnessVersion(ParseIntError),
|
||||||
|
/// Bitcoin script opcode does not match any known witness version, the script is malformed
|
||||||
|
MalformedWitnessVersion,
|
||||||
/// The witness program must be between 2 and 40 bytes in length.
|
/// The witness program must be between 2 and 40 bytes in length.
|
||||||
InvalidWitnessProgramLength(usize),
|
InvalidWitnessProgramLength(usize),
|
||||||
/// A v0 witness program must be either of length 20 or 32.
|
/// A v0 witness program must be either of length 20 or 32.
|
||||||
|
@ -74,6 +80,8 @@ impl fmt::Display for Error {
|
||||||
Error::Bech32(ref e) => write!(f, "bech32: {}", e),
|
Error::Bech32(ref e) => write!(f, "bech32: {}", e),
|
||||||
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
|
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
|
||||||
Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v),
|
Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v),
|
||||||
|
Error::UnparsableWitnessVersion(ref e) => write!(f, "Incorrect format of a witness version byte: {}", e),
|
||||||
|
Error::MalformedWitnessVersion => f.write_str("bitcoin script opcode does not match any known witness version, the script is malformed"),
|
||||||
Error::InvalidWitnessProgramLength(l) => write!(f,
|
Error::InvalidWitnessProgramLength(l) => write!(f,
|
||||||
"the witness program must be between 2 and 40 bytes in length: length={}", l,
|
"the witness program must be between 2 and 40 bytes in length: length={}", l,
|
||||||
),
|
),
|
||||||
|
@ -93,6 +101,7 @@ impl ::std::error::Error for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Base58(ref e) => Some(e),
|
Error::Base58(ref e) => Some(e),
|
||||||
Error::Bech32(ref e) => Some(e),
|
Error::Bech32(ref e) => Some(e),
|
||||||
|
Error::UnparsableWitnessVersion(ref e) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,6 +158,176 @@ impl FromStr for AddressType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Version of the WitnessProgram: first byte of `scriptPubkey` in
|
||||||
|
/// transaction output for transactions starting with opcodes ranging from 0
|
||||||
|
/// to 16 (inclusive).
|
||||||
|
///
|
||||||
|
/// Structure helps to limit possible version of the witness according to the
|
||||||
|
/// specification; if a plain `u8` type was be used instead it would mean that
|
||||||
|
/// version may be > 16, which is incorrect.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum WitnessVersion {
|
||||||
|
/// Initial version of Witness Program. Used for P2WPKH and P2WPK outputs
|
||||||
|
V0 = 0,
|
||||||
|
/// Version of Witness Program used for Taproot P2TR outputs
|
||||||
|
V1 = 1,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V2 = 2,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V3 = 3,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V4 = 4,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V5 = 5,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V6 = 6,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V7 = 7,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V8 = 8,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V9 = 9,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V10 = 10,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V11 = 11,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V12 = 12,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V13 = 13,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V14 = 14,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V15 = 15,
|
||||||
|
/// Future (unsupported) version of Witness Program
|
||||||
|
V16 = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without
|
||||||
|
/// any prefix or suffix.
|
||||||
|
impl fmt::Display for WitnessVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", *self as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FromStr for WitnessVersion {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let version = s.parse().map_err(|err| Error::UnparsableWitnessVersion(err))?;
|
||||||
|
WitnessVersion::from_num(version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WitnessVersion {
|
||||||
|
/// Converts 5-bit unsigned integer value matching single symbol from Bech32(m) address encoding
|
||||||
|
/// ([`bech32::u5`]) into [`WitnessVersion`] variant.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Version of the Witness program
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If the integer does not corresponds to any witness version, errors with
|
||||||
|
/// [`Error::InvalidWitnessVersion`]
|
||||||
|
pub fn from_u5(value: ::bech32::u5) -> Result<Self, Error> {
|
||||||
|
WitnessVersion::from_num(value.to_u8())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts 8-bit unsigned integer value into [`WitnessVersion`] variant.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Version of the Witness program
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If the integer does not corresponds to any witness version, errors with
|
||||||
|
/// [`Error::InvalidWitnessVersion`]
|
||||||
|
pub fn from_num(no: u8) -> Result<Self, Error> {
|
||||||
|
Ok(match no {
|
||||||
|
0 => WitnessVersion::V0,
|
||||||
|
1 => WitnessVersion::V1,
|
||||||
|
2 => WitnessVersion::V2,
|
||||||
|
3 => WitnessVersion::V3,
|
||||||
|
4 => WitnessVersion::V4,
|
||||||
|
5 => WitnessVersion::V5,
|
||||||
|
6 => WitnessVersion::V6,
|
||||||
|
7 => WitnessVersion::V7,
|
||||||
|
8 => WitnessVersion::V8,
|
||||||
|
9 => WitnessVersion::V9,
|
||||||
|
10 => WitnessVersion::V10,
|
||||||
|
11 => WitnessVersion::V11,
|
||||||
|
12 => WitnessVersion::V12,
|
||||||
|
13 => WitnessVersion::V13,
|
||||||
|
14 => WitnessVersion::V14,
|
||||||
|
15 => WitnessVersion::V15,
|
||||||
|
16 => WitnessVersion::V16,
|
||||||
|
wrong => Err(Error::InvalidWitnessVersion(wrong))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts bitcoin script opcode into [`WitnessVersion`] variant.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Version of the Witness program (for opcodes in range of `OP_0`..`OP_16`)
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If the opcode does not correspond to any witness version, errors with
|
||||||
|
/// [`Error::UnparsableWitnessVersion`]
|
||||||
|
pub fn from_opcode(opcode: opcodes::All) -> Result<Self, Error> {
|
||||||
|
match opcode.into_u8() {
|
||||||
|
0 => Ok(WitnessVersion::V0),
|
||||||
|
version if version >= opcodes::all::OP_PUSHNUM_1.into_u8() && version <= opcodes::all::OP_PUSHNUM_16.into_u8() =>
|
||||||
|
WitnessVersion::from_num(version - opcodes::all::OP_PUSHNUM_1.into_u8() + 1),
|
||||||
|
_ => Err(Error::MalformedWitnessVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts bitcoin script [`Instruction`] (parsed opcode) into [`WitnessVersion`] variant.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Version of the Witness program for [`Instruction::Op`] and [`Instruction::PushBytes`] with
|
||||||
|
/// byte value within `1..=16` range.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If the opcode does not correspond to any witness version, errors with
|
||||||
|
/// [`Error::UnparsableWitnessVersion`] for the rest of opcodes
|
||||||
|
pub fn from_instruction(instruction: Instruction) -> Result<Self, Error> {
|
||||||
|
match instruction {
|
||||||
|
Instruction::Op(op) => WitnessVersion::from_opcode(op),
|
||||||
|
Instruction::PushBytes(bytes) if bytes.len() == 0 => Ok(WitnessVersion::V0),
|
||||||
|
Instruction::PushBytes(_) => Err(Error::MalformedWitnessVersion),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns integer version number representation for a given [`WitnessVersion`] value.
|
||||||
|
///
|
||||||
|
/// NB: this is not the same as an integer representation of the opcode signifying witness
|
||||||
|
/// version in bitcoin script. Thus, there is no function to directly convert witness version
|
||||||
|
/// into a byte since the conversion requires context (bitcoin script or just a version number)
|
||||||
|
pub fn into_num(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WitnessVersion> for ::bech32::u5 {
|
||||||
|
/// Converts [`WitnessVersion`] instance into corresponding Bech32(m) u5-value ([`bech32::u5`])
|
||||||
|
fn from(version: WitnessVersion) -> Self {
|
||||||
|
::bech32::u5::try_from_u8(version.into_num()).expect("WitnessVersion must be 0..=16")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WitnessVersion> for opcodes::All {
|
||||||
|
/// Converts [`WitnessVersion`] instance into corresponding Bitcoin scriptopcode (`OP_0`..`OP_16`)
|
||||||
|
fn from(version: WitnessVersion) -> opcodes::All {
|
||||||
|
match version {
|
||||||
|
WitnessVersion::V0 => opcodes::all::OP_PUSHBYTES_0,
|
||||||
|
no => opcodes::All::from(opcodes::all::OP_PUSHNUM_1.into_u8() + no.into_num() - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The method used to produce an address
|
/// The method used to produce an address
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
|
@ -159,7 +338,7 @@ pub enum Payload {
|
||||||
/// Segwit addresses
|
/// Segwit addresses
|
||||||
WitnessProgram {
|
WitnessProgram {
|
||||||
/// The witness program version
|
/// The witness program version
|
||||||
version: bech32::u5,
|
version: WitnessVersion,
|
||||||
/// The witness program
|
/// The witness program
|
||||||
program: Vec<u8>,
|
program: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
@ -169,23 +348,17 @@ impl Payload {
|
||||||
/// Get a [Payload] from an output script (scriptPubkey).
|
/// Get a [Payload] from an output script (scriptPubkey).
|
||||||
pub fn from_script(script: &script::Script) -> Option<Payload> {
|
pub fn from_script(script: &script::Script) -> Option<Payload> {
|
||||||
Some(if script.is_p2pkh() {
|
Some(if script.is_p2pkh() {
|
||||||
Payload::PubkeyHash(PubkeyHash::from_slice(&script.as_bytes()[3..23]).unwrap())
|
let mut hash_inner = [0u8; 20];
|
||||||
|
hash_inner.copy_from_slice(&script.as_bytes()[3..23]);
|
||||||
|
Payload::PubkeyHash(PubkeyHash::from_inner(hash_inner))
|
||||||
} else if script.is_p2sh() {
|
} else if script.is_p2sh() {
|
||||||
Payload::ScriptHash(ScriptHash::from_slice(&script.as_bytes()[2..22]).unwrap())
|
let mut hash_inner = [0u8; 20];
|
||||||
|
hash_inner.copy_from_slice(&script.as_bytes()[2..22]);
|
||||||
|
Payload::ScriptHash(ScriptHash::from_inner(hash_inner))
|
||||||
} else if script.is_witness_program() {
|
} else if script.is_witness_program() {
|
||||||
// We can unwrap the u5 check and assume script length
|
|
||||||
// because [Script::is_witness_program] makes sure of this.
|
|
||||||
Payload::WitnessProgram {
|
Payload::WitnessProgram {
|
||||||
version: {
|
version: WitnessVersion::from_opcode(opcodes::All::from(script[0])).ok()?,
|
||||||
// Since we passed the [is_witness_program] check,
|
program: script[2..].to_vec(),
|
||||||
// the first byte is either 0x00 or 0x50 + version.
|
|
||||||
let mut verop = script.as_bytes()[0];
|
|
||||||
if verop > 0x50 {
|
|
||||||
verop -= 0x50;
|
|
||||||
}
|
|
||||||
bech32::u5::try_from_u8(verop).expect("checked before")
|
|
||||||
},
|
|
||||||
program: script.as_bytes()[2..].to_vec(),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -200,9 +373,9 @@ impl Payload {
|
||||||
Payload::ScriptHash(ref hash) =>
|
Payload::ScriptHash(ref hash) =>
|
||||||
script::Script::new_p2sh(hash),
|
script::Script::new_p2sh(hash),
|
||||||
Payload::WitnessProgram {
|
Payload::WitnessProgram {
|
||||||
version: ver,
|
version,
|
||||||
program: ref prog,
|
program: ref prog,
|
||||||
} => script::Script::new_witness_program(ver, prog)
|
} => script::Script::new_witness_program(version, prog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,7 +429,7 @@ impl Address {
|
||||||
Ok(Address {
|
Ok(Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
version: WitnessVersion::V0,
|
||||||
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
|
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -289,7 +462,7 @@ impl Address {
|
||||||
Address {
|
Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
version: WitnessVersion::V0,
|
||||||
program: WScriptHash::hash(&script[..])[..].to_vec(),
|
program: WScriptHash::hash(&script[..])[..].to_vec(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -310,18 +483,18 @@ impl Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the address type of the address.
|
/// Get the address type of the address.
|
||||||
/// None if unknown or non-standard.
|
/// None if unknown, non-standard or related to the future witness version.
|
||||||
pub fn address_type(&self) -> Option<AddressType> {
|
pub fn address_type(&self) -> Option<AddressType> {
|
||||||
match self.payload {
|
match self.payload {
|
||||||
Payload::PubkeyHash(_) => Some(AddressType::P2pkh),
|
Payload::PubkeyHash(_) => Some(AddressType::P2pkh),
|
||||||
Payload::ScriptHash(_) => Some(AddressType::P2sh),
|
Payload::ScriptHash(_) => Some(AddressType::P2sh),
|
||||||
Payload::WitnessProgram {
|
Payload::WitnessProgram {
|
||||||
version: ver,
|
version,
|
||||||
program: ref prog,
|
program: ref prog,
|
||||||
} => {
|
} => {
|
||||||
// BIP-141 p2wpkh or p2wsh addresses.
|
// BIP-141 p2wpkh or p2wsh addresses.
|
||||||
match ver.to_u8() {
|
match version {
|
||||||
0 => match prog.len() {
|
WitnessVersion::V0 => match prog.len() {
|
||||||
20 => Some(AddressType::P2wpkh),
|
20 => Some(AddressType::P2wpkh),
|
||||||
32 => Some(AddressType::P2wsh),
|
32 => Some(AddressType::P2wsh),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -335,7 +508,7 @@ impl Address {
|
||||||
/// Check whether or not the address is following Bitcoin
|
/// Check whether or not the address is following Bitcoin
|
||||||
/// standardness rules.
|
/// standardness rules.
|
||||||
///
|
///
|
||||||
/// Segwit addresses with unassigned witness versions or non-standard
|
/// SegWit addresses with unassigned witness versions or non-standard
|
||||||
/// program sizes are considered non-standard.
|
/// program sizes are considered non-standard.
|
||||||
pub fn is_standard(&self) -> bool {
|
pub fn is_standard(&self) -> bool {
|
||||||
self.address_type().is_some()
|
self.address_type().is_some()
|
||||||
|
@ -428,7 +601,7 @@ impl fmt::Display for Address {
|
||||||
base58::check_encode_slice_to_fmt(fmt, &prefixed[..])
|
base58::check_encode_slice_to_fmt(fmt, &prefixed[..])
|
||||||
}
|
}
|
||||||
Payload::WitnessProgram {
|
Payload::WitnessProgram {
|
||||||
version: ver,
|
version,
|
||||||
program: ref prog,
|
program: ref prog,
|
||||||
} => {
|
} => {
|
||||||
let hrp = match self.network {
|
let hrp = match self.network {
|
||||||
|
@ -436,7 +609,7 @@ impl fmt::Display for Address {
|
||||||
Network::Testnet | Network::Signet => "tb",
|
Network::Testnet | Network::Signet => "tb",
|
||||||
Network::Regtest => "bcrt",
|
Network::Regtest => "bcrt",
|
||||||
};
|
};
|
||||||
let bech_ver = if ver.to_u8() > 0 { bech32::Variant::Bech32m } else { bech32::Variant::Bech32 };
|
let bech_ver = if version.into_num() > 0 { bech32::Variant::Bech32m } else { bech32::Variant::Bech32 };
|
||||||
let mut upper_writer;
|
let mut upper_writer;
|
||||||
let writer = if fmt.alternate() {
|
let writer = if fmt.alternate() {
|
||||||
upper_writer = UpperWriter(fmt);
|
upper_writer = UpperWriter(fmt);
|
||||||
|
@ -445,7 +618,7 @@ impl fmt::Display for Address {
|
||||||
fmt as &mut dyn fmt::Write
|
fmt as &mut dyn fmt::Write
|
||||||
};
|
};
|
||||||
let mut bech32_writer = bech32::Bech32Writer::new(hrp, bech_ver, writer)?;
|
let mut bech32_writer = bech32::Bech32Writer::new(hrp, bech_ver, writer)?;
|
||||||
bech32::WriteBase32::write_u5(&mut bech32_writer, ver)?;
|
bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?;
|
||||||
bech32::ToBase32::write_base32(&prog, &mut bech32_writer)
|
bech32::ToBase32::write_base32(&prog, &mut bech32_writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,28 +666,24 @@ impl FromStr for Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the script version and program (converted from 5-bit to 8-bit)
|
// Get the script version and program (converted from 5-bit to 8-bit)
|
||||||
let (version, program): (bech32::u5, Vec<u8>) = {
|
let (version, program): (WitnessVersion, Vec<u8>) = {
|
||||||
let (v, p5) = payload.split_at(1);
|
let (v, p5) = payload.split_at(1);
|
||||||
(v[0], bech32::FromBase32::from_base32(p5)?)
|
(WitnessVersion::from_u5(v[0])?, bech32::FromBase32::from_base32(p5)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic segwit checks.
|
|
||||||
if version.to_u8() > 16 {
|
|
||||||
return Err(Error::InvalidWitnessVersion(version.to_u8()));
|
|
||||||
}
|
|
||||||
if program.len() < 2 || program.len() > 40 {
|
if program.len() < 2 || program.len() > 40 {
|
||||||
return Err(Error::InvalidWitnessProgramLength(program.len()));
|
return Err(Error::InvalidWitnessProgramLength(program.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific segwit v0 check.
|
// Specific segwit v0 check.
|
||||||
if version.to_u8() == 0 && (program.len() != 20 && program.len() != 32) {
|
if version == WitnessVersion::V0 && (program.len() != 20 && program.len() != 32) {
|
||||||
return Err(Error::InvalidSegwitV0ProgramLength(program.len()));
|
return Err(Error::InvalidSegwitV0ProgramLength(program.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bech32 encoding check
|
// Bech32 encoding check
|
||||||
if (version.to_u8() > 0 && variant != bech32::Variant::Bech32m) ||
|
if (version.into_num() > 0 && variant != bech32::Variant::Bech32m) ||
|
||||||
(version.to_u8() == 0 && variant != bech32::Variant::Bech32) {
|
(version.into_num() == 0 && variant != bech32::Variant::Bech32) {
|
||||||
return Err(Error::InvalidWitnessVersion(version.to_u8()))
|
return Err(Error::InvalidWitnessVersion(version.into_num()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Address {
|
return Ok(Address {
|
||||||
|
@ -710,14 +879,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_existent_segwit_version() {
|
fn test_non_existent_segwit_version() {
|
||||||
let version = 13;
|
|
||||||
// 40-byte program
|
// 40-byte program
|
||||||
let program = hex!(
|
let program = hex!(
|
||||||
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
|
"654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
|
||||||
);
|
);
|
||||||
let addr = Address {
|
let addr = Address {
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(version).expect("0<32"),
|
version: WitnessVersion::V13,
|
||||||
program: program,
|
program: program,
|
||||||
},
|
},
|
||||||
network: Network::Bitcoin,
|
network: Network::Bitcoin,
|
||||||
|
@ -887,7 +1055,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
let segwit_payload = (0..=16).map(|version| {
|
let segwit_payload = (0..=16).map(|version| {
|
||||||
Payload::WitnessProgram {
|
Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(version).unwrap(),
|
version: WitnessVersion::from_num(version).unwrap(),
|
||||||
program: vec![]
|
program: vec![]
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
|
Loading…
Reference in New Issue