From 2ac5ee73d3423291ebf5d3f9a2fb6132f80dc052 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 12 Sep 2016 10:57:12 +0200 Subject: [PATCH] script pretty printing --- src/script/interpreter.rs | 15 +----- src/script/mod.rs | 2 +- src/script/opcode.rs | 11 ++++- src/script/script.rs | 97 +++++++++++++++++++++++++++++++++++++++ src/transaction.rs | 28 +++++------ 5 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/script/interpreter.rs b/src/script/interpreter.rs index ba328dee..4c25c662 100644 --- a/src/script/interpreter.rs +++ b/src/script/interpreter.rs @@ -3,7 +3,7 @@ use keys::{Public, Signature}; use hash::{H256, h256_from_u8}; use transaction::{Transaction, SEQUENCE_LOCKTIME_DISABLE_FLAG}; use crypto::{sha1, sha256, dhash160, dhash256, ripemd160}; -use script::{script, Script, Num, VerificationFlags, Opcode, Error}; +use script::{script, Script, Num, VerificationFlags, Opcode, Error, read_usize}; #[derive(Debug, PartialEq, Clone, Copy)] #[repr(u8)] @@ -336,19 +336,6 @@ fn require_len(stack: &Vec>, len: usize) -> Result<(), Error> { } } -fn read_usize(data: &[u8], size: usize) -> Result { - if data.len() < size { - return Err(Error::BadOpcode); - } - - let result = data - .iter() - .take(size) - .enumerate() - .fold(0, |acc, (i, x)| acc + ((*x as usize) << (i * 8))); - Ok(result) -} - pub fn eval_script( stack: &mut Vec>, script: &Script, diff --git a/src/script/mod.rs b/src/script/mod.rs index d6c1c350..ce475c4e 100644 --- a/src/script/mod.rs +++ b/src/script/mod.rs @@ -14,7 +14,7 @@ pub use self::flags::VerificationFlags; pub use self::interpreter::eval_script; pub use self::opcode::Opcode; pub use self::num::Num; -pub use self::script::{Script, ScriptWitness}; +pub use self::script::{Script, ScriptWitness, read_usize}; pub use self::sign::SignatureData; pub use self::standard::TransactionType; diff --git a/src/script/opcode.rs b/src/script/opcode.rs index c6a9b91f..25194eb3 100644 --- a/src/script/opcode.rs +++ b/src/script/opcode.rs @@ -1,7 +1,10 @@ +//! Script opcodes. +use std::fmt; + /// Script opcodes. #[repr(u8)] #[allow(non_camel_case_types)] -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] pub enum Opcode { // push value OP_0 = 0x00, @@ -219,6 +222,12 @@ pub enum Opcode { OP_INVALIDOPCODE = 0xff, } +impl fmt::Display for Opcode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + impl Opcode { pub fn from_u8(u: u8) -> Option { use self::Opcode::*; diff --git a/src/script/script.rs b/src/script/script.rs index 65bd6d6e..ebfec89c 100644 --- a/src/script/script.rs +++ b/src/script/script.rs @@ -1,5 +1,7 @@ //! Serialized script, used inside transaction inputs and outputs. +use std::fmt; +use hex::ToHex; use script::{Opcode, Error, Num}; /// Maximum number of bytes pushable to the stack @@ -104,6 +106,71 @@ impl Script { } } +pub fn read_usize(data: &[u8], size: usize) -> Result { + if data.len() < size { + return Err(Error::BadOpcode); + } + + let result = data + .iter() + .take(size) + .enumerate() + .fold(0, |acc, (i, x)| acc + ((*x as usize) << (i * 8))); + Ok(result) +} + +macro_rules! try_or_fmt { + ($fmt: expr, $expr: expr) => { + match $expr { + Ok(o) => o, + Err(e) => return e.fmt($fmt), + } + } +} + +impl fmt::Debug for Script { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.data.to_hex()) + } +} + +impl fmt::Display for Script { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut pc = 0; + while pc < self.len() { + let opcode = try_or_fmt!(f, self.get_opcode(pc)); + + match opcode { + Opcode::OP_PUSHDATA1 | + Opcode::OP_PUSHDATA2 | + Opcode::OP_PUSHDATA4 => { + let len = match opcode { + Opcode::OP_PUSHDATA1 => 1, + Opcode::OP_PUSHDATA2 => 2, + _ => 4, + }; + + let slice = try_or_fmt!(f, self.take(pc + 1, len)); + let n = try_or_fmt!(f, read_usize(slice, len)); + let bytes = try_or_fmt!(f, self.take_checked(pc + 1 + len, n)); + pc += len + n; + try!(writeln!(f, "{:?} 0x{}", opcode, bytes.to_hex())); + }, + o if o >= Opcode::OP_0 && o <= Opcode::OP_PUSHBYTES_75 => { + let bytes = try_or_fmt!(f, self.take_checked(pc + 1, opcode as usize)); + pc += opcode as usize; + try!(writeln!(f, "{:?} 0x{}", opcode, bytes.to_hex())); + }, + _ => { + try!(writeln!(f, "{:?}", opcode)); + }, + } + pc += 1; + } + Ok(()) + } +} + pub struct ScriptWitness { script: Vec>, } @@ -111,6 +178,7 @@ pub struct ScriptWitness { #[cfg(test)] mod tests { use hex::FromHex; + use script::{Builder, Opcode}; use super::Script; #[test] @@ -128,4 +196,33 @@ mod tests { assert!(Script::new(data).is_pay_to_witness_script_hash()); assert!(!Script::new(data2).is_pay_to_witness_script_hash()); } + + #[test] + fn test_script_debug() { + use std::fmt::Write; + + let script = Builder::default() + .push_num(3.into()) + .push_num(2.into()) + .push_opcode(Opcode::OP_ADD) + .into_script(); + let s = "0103010293"; + let mut res = String::new(); + write!(&mut res, "{:?}", script).unwrap(); + assert_eq!(s.to_string(), res); + } + + #[test] + fn test_script_display() { + let script = Builder::default() + .push_num(3.into()) + .push_num(2.into()) + .push_opcode(Opcode::OP_ADD) + .into_script(); + let s = r#"OP_PUSHBYTES_1 0x03 +OP_PUSHBYTES_1 0x02 +OP_ADD +"#; + assert_eq!(script.to_string(), s.to_string()); + } } diff --git a/src/transaction.rs b/src/transaction.rs index 8f3b6414..f37852aa 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -44,7 +44,7 @@ impl Deserializable for OutPoint { #[derive(Debug)] pub struct TransactionInput { previous_output: OutPoint, - signature_script: Vec, + script_sig: Vec, sequence: u32, } @@ -52,8 +52,8 @@ impl Serializable for TransactionInput { fn serialize(&self, stream: &mut Stream) { stream .append(&self.previous_output) - .append(&CompactInteger::from(self.signature_script.len())) - .append_bytes(&self.signature_script) + .append(&CompactInteger::from(self.script_sig.len())) + .append_bytes(&self.script_sig) .append(&self.sequence); } } @@ -61,13 +61,13 @@ impl Serializable for TransactionInput { impl Deserializable for TransactionInput { fn deserialize(reader: &mut Reader) -> Result where Self: Sized { let previous_output = try!(reader.read()); - let signature_script_len = try!(reader.read::()); - let signature_script = try!(reader.read_bytes(signature_script_len.into())).to_vec(); + let script_sig_len = try!(reader.read::()); + let script_sig = try!(reader.read_bytes(script_sig_len.into())).to_vec(); let sequence = try!(reader.read()); let result = TransactionInput { previous_output: previous_output, - signature_script: signature_script, + script_sig: script_sig, sequence: sequence, }; @@ -78,27 +78,27 @@ impl Deserializable for TransactionInput { #[derive(Debug)] pub struct TransactionOutput { value: u64, - pk_script: Vec, + script_pubkey: Vec, } impl Serializable for TransactionOutput { fn serialize(&self, stream: &mut Stream) { stream .append(&self.value) - .append(&CompactInteger::from(self.pk_script.len())) - .append_bytes(&self.pk_script); + .append(&CompactInteger::from(self.script_pubkey.len())) + .append_bytes(&self.script_pubkey); } } impl Deserializable for TransactionOutput { fn deserialize(reader: &mut Reader) -> Result where Self: Sized { let value = try!(reader.read()); - let pk_script_len = try!(reader.read::()); - let pk_script = try!(reader.read_bytes(pk_script_len.into())).to_vec(); + let script_pubkey_len = try!(reader.read::()); + let script_pubkey = try!(reader.read_bytes(script_pubkey_len.into())).to_vec(); let result = TransactionOutput { value: value, - pk_script: pk_script, + script_pubkey: script_pubkey, }; Ok(result) @@ -179,10 +179,10 @@ mod tests { assert_eq!(t.transaction_outputs.len(), 1); let tx_input = &t.transaction_inputs[0]; assert_eq!(tx_input.sequence, 4294967295); - assert_eq!(tx_input.signature_script, "48304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501".from_hex().unwrap()); + assert_eq!(tx_input.script_sig, "48304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501".from_hex().unwrap()); let tx_output = &t.transaction_outputs[0]; assert_eq!(tx_output.value, 5000000000); - assert_eq!(tx_output.pk_script, "76a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac".from_hex().unwrap()); + assert_eq!(tx_output.script_pubkey, "76a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac".from_hex().unwrap()); } #[test]