// Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without // any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. // If not, see . // //! # Script //! //! Scripts define Bitcoin's digital signature scheme: a signature is formed //! from a script (the second half of which is defined by a coin to be spent, //! and the first half provided by the spending transaction), and is valid //! iff the script leaves `TRUE` on the stack after being evaluated. //! Bitcoin's script is a stack-based assembly language similar in spirit to //! Forth. //! //! This module provides the structures and functions needed to support scripts. //! use std::char::from_digit; use std::io::IoResult; use serialize::json; use network::serialize::Serializable; use blockdata::opcodes; use util::thinvec::ThinVec; #[deriving(PartialEq, Show, Clone)] /// A Bitcoin script pub struct Script(ThinVec); impl Script { /// Creates a new empty script pub fn new() -> Script { Script(ThinVec::new()) } /// Adds instructions to push an integer onto the stack. Integers are /// encoded as little-endian signed-magnitude numbers, but there are /// dedicated opcodes to push some small integers. pub fn push_int(&mut self, data: int) { // We can special-case -1, 1-16 if data == -1 || (data >= 1 && data <=16) { let &Script(ref mut raw) = self; raw.push(data as u8 + opcodes::TRUE); return; } // We can also special-case zero if data == 0 { let &Script(ref mut raw) = self; raw.push(opcodes::FALSE); return; } // Otherwise encode it as data self.push_scriptint(data); } /// Adds instructions to push an integer onto the stack, using the explicit /// encoding regardless of the availability of dedicated opcodes. pub fn push_scriptint(&mut self, data: int) { let neg = data < 0; let mut abs = if neg { -data } else { data } as uint; let mut v = vec![]; while abs > 0xFF { v.push((abs & 0xFF) as u8); abs >>= 8; } // If the number's value causes the sign bit to be set, we need an extra // byte to get the correct value and correct sign bit if abs & 0x80 != 0 { v.push(abs as u8); v.push(if neg { 0x80u8 } else { 0u8 }); } // Otherwise we just set the sign bit ourselves else { abs |= if neg { 0x80 } else { 0 }; v.push(abs as u8); } // Finally we put the encoded int onto the stack self.push_slice(v.as_slice()); } /// Adds instructions to push some arbitrary data onto the stack pub fn push_slice(&mut self, data: &[u8]) { let &Script(ref mut raw) = self; // Start with a PUSH opcode match data.len() { n if n < opcodes::PUSHDATA1 as uint => { raw.push(n as u8); }, n if n < 0x100 => { raw.push(opcodes::PUSHDATA1); raw.push(n as u8); }, n if n < 0x10000 => { raw.push(opcodes::PUSHDATA2); raw.push((n % 0x100) as u8); raw.push((n / 0x100) as u8); }, n if n < 0x100000000 => { raw.push(opcodes::PUSHDATA4); raw.push((n % 0x100) as u8); raw.push(((n / 0x100) % 0x100) as u8); raw.push(((n / 0x10000) % 0x100) as u8); raw.push((n / 0x1000000) as u8); } _ => fail!("tried to put a 4bn+ sized object into a script!") } // Then push the acraw raw.extend(data.iter().map(|n| *n)); } /// Adds an individual opcode to the script pub fn push_opcode(&mut self, data: u8) { let &Script(ref mut raw) = self; raw.push(data); } } // User-facing serialization impl json::ToJson for Script { // TODO: put this in a struct alongside an opcode decode fn to_json(&self) -> json::Json { let &Script(ref raw) = self; let mut ret = String::new(); for dat in raw.iter() { ret.push_char(from_digit((dat / 0x10) as uint, 16).unwrap()); ret.push_char(from_digit((dat & 0x0f) as uint, 16).unwrap()); } json::String(ret) } } // Network serialization impl Serializable for Script { fn serialize(&self) -> Vec { let &Script(ref data) = self; data.serialize() } fn deserialize>(iter: I) -> IoResult