All script generation functionality moved to standalone methods in Script

This commit is contained in:
Dr Maxim Orlovsky 2020-01-10 18:53:01 +01:00
parent addb54ffc7
commit 9143fd3888
2 changed files with 89 additions and 33 deletions

View File

@ -29,7 +29,7 @@ use std::{error, fmt, io};
#[cfg(feature = "serde")] use serde; #[cfg(feature = "serde")] use serde;
use hash_types::{ScriptHash, WScriptHash}; use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
use blockdata::opcodes; use blockdata::opcodes;
use consensus::{encode, Decodable, Encodable}; use consensus::{encode, Decodable, Encodable};
use hashes::Hash; use hashes::Hash;
@ -216,6 +216,57 @@ impl Script {
/// Creates a new empty script /// Creates a new empty script
pub fn new() -> Script { Script(vec![].into_boxed_slice()) } pub fn new() -> Script { Script(vec![].into_boxed_slice()) }
/// Generates P2PK-type of scriptPubkey
pub fn with_pk(pubkey: &PublicKey) -> Script {
Builder::new()
.push_key(pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
/// Generates P2PKH-type of scriptPubkey
pub fn with_pkh(pubkey_hash: &PubkeyHash) -> Script {
Builder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&pubkey_hash[..])
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
/// Generates P2SH-type of scriptPubkey with a given hash of the redeem script
pub fn with_sh(script_hash: &ScriptHash) -> Script {
Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&script_hash[..])
.push_opcode(opcodes::all::OP_EQUAL)
.into_script()
}
/// Generates P2WPKH-type of scriptPubkey
pub fn with_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
Script::with_witness_program(::bech32::u5::try_from_u8(0).unwrap(), &pubkey_hash.to_vec())
}
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
pub fn with_v0_wsh(script_hash: &WScriptHash) -> Script {
Script::with_witness_program(::bech32::u5::try_from_u8(0).unwrap(), &script_hash.to_vec())
}
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
pub fn with_witness_program(ver: ::bech32::u5, program: &Vec<u8>) -> Script {
let mut verop = ver.to_u8();
assert!(verop <= 16);
if verop > 0 {
verop = 0x50 + verop;
}
Builder::new()
.push_opcode(verop.into())
.push_slice(&program)
.into_script()
}
/// The length in bytes of the script /// The length in bytes of the script
pub fn len(&self) -> usize { self.0.len() } pub fn len(&self) -> usize { self.0.len() }
@ -233,18 +284,13 @@ impl Script {
/// Compute the P2SH output corresponding to this redeem script /// Compute the P2SH output corresponding to this redeem script
pub fn to_p2sh(&self) -> Script { pub fn to_p2sh(&self) -> Script {
Builder::new().push_opcode(opcodes::all::OP_HASH160) Script::with_sh(&ScriptHash::hash(&self.0))
.push_slice(&ScriptHash::hash(&self.0)[..])
.push_opcode(opcodes::all::OP_EQUAL)
.into_script()
} }
/// Compute the P2WSH output corresponding to this witnessScript (aka the "witness redeem /// Compute the P2WSH output corresponding to this witnessScript (aka the "witness redeem
/// script") /// script")
pub fn to_v0_p2wsh(&self) -> Script { pub fn to_v0_p2wsh(&self) -> Script {
Builder::new().push_int(0) Script::with_v0_wsh(&WScriptHash::hash(&self.0))
.push_slice(&WScriptHash::hash(&self.0)[..])
.into_script()
} }
/// Checks whether a script pubkey is a p2sh output /// Checks whether a script pubkey is a p2sh output
@ -565,7 +611,7 @@ impl<'a> Iterator for Instructions<'a> {
impl Builder { impl Builder {
/// Creates a new empty script /// Creates a new empty script
pub fn new() -> Builder { pub fn new() -> Self {
Builder(vec![], None) Builder(vec![], None)
} }
@ -771,14 +817,15 @@ impl Decodable for Script {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::str::FromStr; use std::str::FromStr;
use hashes::hex::FromHex;
use super::*; use super::*;
use super::build_scriptint; use super::build_scriptint;
use hashes::hex::FromHex;
use consensus::encode::{deserialize, serialize}; use consensus::encode::{deserialize, serialize};
use blockdata::opcodes; use blockdata::opcodes;
use util::key::PublicKey; use util::key::PublicKey;
use util::psbt::serialize::Serialize;
#[test] #[test]
fn script() { fn script() {
@ -828,6 +875,31 @@ mod test {
assert_eq!(&format!("{:x}", script), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac"); assert_eq!(&format!("{:x}", script), "76a91416e1ae70ff0fa102905d4af297f6912bda6cce1988ac");
} }
#[test]
fn script_generators() {
let pubkey = PublicKey::from_str("0234e6a79c5359c613762d537e0e19d86c77c1666d8c9ab050f23acd198e97f93e").unwrap();
assert!(Script::with_pk(&pubkey).is_p2pk());
let pubkey_hash = PubkeyHash::hash(&pubkey.serialize());
assert!(Script::with_pkh(&pubkey_hash).is_p2pkh());
let wpubkey_hash = WPubkeyHash::hash(&pubkey.serialize());
assert!(Script::with_v0_wpkh(&wpubkey_hash).is_v0_p2wpkh());
let script = Builder::new().push_opcode(opcodes::all::OP_NUMEQUAL)
.push_verify()
.into_script();
let script_hash = ScriptHash::hash(&script.serialize());
let p2sh = Script::with_sh(&script_hash);
assert!(p2sh.is_p2sh());
assert_eq!(script.to_p2sh(), p2sh);
let wscript_hash = WScriptHash::hash(&script.serialize());
let p2wsh = Script::with_v0_wsh(&wscript_hash);
assert!(p2wsh.is_v0_p2wsh());
assert_eq!(script.to_v0_p2wsh(), p2wsh);
}
#[test] #[test]
fn script_builder_verify() { fn script_builder_verify() {
let simple = Builder::new() let simple = Builder::new()

View File

@ -41,9 +41,7 @@ use std::str::FromStr;
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::opcodes;
use blockdata::script; use blockdata::script;
use network::constants::Network; use network::constants::Network;
use util::base58; use util::base58;
@ -157,11 +155,11 @@ impl FromStr for AddressType {
/// 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 {
/// pay-to-pkhash address /// P2PKH address
PubkeyHash(PubkeyHash), PubkeyHash(PubkeyHash),
/// P2SH address /// P2SH address
ScriptHash(ScriptHash), ScriptHash(ScriptHash),
/// Segwit address /// Segwit addresses
WitnessProgram { WitnessProgram {
/// The witness program version /// The witness program version
version: bech32::u5, version: bech32::u5,
@ -200,29 +198,15 @@ impl Payload {
/// Generates a script pubkey spending to this [Payload]. /// Generates a script pubkey spending to this [Payload].
pub fn script_pubkey(&self) -> script::Script { pub fn script_pubkey(&self) -> script::Script {
match *self { match *self {
Payload::PubkeyHash(ref hash) => script::Builder::new() Payload::PubkeyHash(ref hash) =>
.push_opcode(opcodes::all::OP_DUP) script::Script::with_pkh(hash),
.push_opcode(opcodes::all::OP_HASH160) Payload::ScriptHash(ref hash) =>
.push_slice(&hash[..]) script::Script::with_sh(hash),
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG),
Payload::ScriptHash(ref hash) => script::Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&hash[..])
.push_opcode(opcodes::all::OP_EQUAL),
Payload::WitnessProgram { Payload::WitnessProgram {
version: ver, version: ver,
program: ref prog, program: ref prog,
} => { } => script::Script::with_witness_program(ver, prog)
assert!(ver.to_u8() <= 16);
let mut verop = ver.to_u8();
if verop > 0 {
verop += 0x50;
} }
script::Builder::new().push_opcode(verop.into()).push_slice(&prog)
}
}
.into_script()
} }
} }