parity-zcash/script/src/builder.rs

149 lines
4.4 KiB
Rust

//! Script builder
use bytes::Bytes;
use zebra_keys::AddressHash;
use {Num, Opcode, Script};
/// Script builder
#[derive(Default)]
pub struct Builder {
data: Bytes,
}
impl Builder {
/// Builds p2pkh script pubkey
pub fn build_p2pkh(address: &AddressHash) -> Script {
Builder::default()
.push_opcode(Opcode::OP_DUP)
.push_opcode(Opcode::OP_HASH160)
.push_bytes(&**address)
.push_opcode(Opcode::OP_EQUALVERIFY)
.push_opcode(Opcode::OP_CHECKSIG)
.into_script()
}
/// Builds p2sh script pubkey
pub fn build_p2sh(address: &AddressHash) -> Script {
Builder::default()
.push_opcode(Opcode::OP_HASH160)
.push_bytes(&**address)
.push_opcode(Opcode::OP_EQUAL)
.into_script()
}
/// Builds op_return script
pub fn build_nulldata(bytes: &[u8]) -> Script {
Builder::default()
.push_opcode(Opcode::OP_RETURN)
.push_bytes(bytes)
.into_script()
}
/// Pushes opcode to the end of script
pub fn push_opcode(mut self, opcode: Opcode) -> Self {
self.data.push(opcode as u8);
self
}
/// Appends bool push operation to the end of script
pub fn push_bool(mut self, value: bool) -> Self {
if value {
self.data.push(Opcode::OP_1 as u8);
} else {
self.data.push(Opcode::OP_0 as u8);
}
self
}
/// Push integer to the end of script
pub fn push_i64(mut self, int: i64) -> Self {
if int == -1 || (int >= 1 && int <= 16) {
let shift: i64 = (Opcode::OP_1 as u8 - 1) as i64;
self.data.push((int + shift) as u8);
self
} else if int == 0 {
self.data.push(Opcode::OP_0 as u8);
self
} else {
self.push_num(int.into())
}
}
/// Appends num push operation to the end of script
pub fn push_num(self, num: Num) -> Self {
self.push_data(&num.to_bytes())
}
/// Appends bytes push operation to the end of script
pub fn push_bytes(mut self, bytes: &[u8]) -> Self {
let len = bytes.len();
if len < 1 || len > 75 {
panic!(format!("Cannot push {} bytes", len));
}
let opcode: Opcode = Opcode::from_u8(((Opcode::OP_PUSHBYTES_1 as usize) + len - 1) as u8)
.expect("value is within [OP_PUSHBYTES_1; OP_PUSHBYTES_75] interval; qed");
self.data.push(opcode as u8);
self.data.extend_from_slice(bytes);
self
}
/// Appends data push operation to the end of script
pub fn push_data(mut self, data: &[u8]) -> Self {
let len = data.len();
if len < Opcode::OP_PUSHDATA1 as usize {
self.data.push(len as u8);
} else if len < 0x100 {
self.data.push(Opcode::OP_PUSHDATA1 as u8);
self.data.push(len as u8);
} else if len < 0x10000 {
self.data.push(Opcode::OP_PUSHDATA2 as u8);
self.data.push(len as u8);
self.data.push((len >> 8) as u8);
} else if len < 0x1_0000_0000 {
self.data.push(Opcode::OP_PUSHDATA4 as u8);
self.data.push(len as u8);
self.data.push((len >> 8) as u8);
self.data.push((len >> 16) as u8);
self.data.push((len >> 24) as u8);
} else {
panic!("Cannot push more than 0x1_0000_0000 bytes");
}
self.data.extend_from_slice(data);
self
}
/// Appends `OP_RETURN` operation to the end of script
pub fn return_bytes(mut self, bytes: &[u8]) -> Self {
let len = bytes.len();
if len < 1 || len > 75 {
panic!(format!("Cannot push {} bytes", len));
}
let opcode: Opcode = Opcode::from_u8(((Opcode::OP_PUSHBYTES_1 as usize) + len - 1) as u8)
.expect("value is within [OP_PUSHBYTES_1; OP_PUSHBYTES_75] interval; qed");
self.data.push(Opcode::OP_RETURN as u8);
self.data.push(opcode as u8);
self.data.extend_from_slice(bytes);
self
}
/// Pushes invalid opcode to the end of script
pub fn push_invalid_opcode(mut self) -> Self {
self.data.push(0xff);
self
}
/// Builds final script
pub fn into_script(self) -> Script {
Script::new(self.data)
}
/// Builds final script bytes
pub fn into_bytes(self) -> Bytes {
self.data
}
}