script pretty printing

This commit is contained in:
debris 2016-09-12 10:57:12 +02:00
parent 585ea7a0c7
commit 2ac5ee73d3
5 changed files with 123 additions and 30 deletions

View File

@ -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<Vec<u8>>, len: usize) -> Result<(), Error> {
}
}
fn read_usize(data: &[u8], size: usize) -> Result<usize, Error> {
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<Vec<u8>>,
script: &Script,

View File

@ -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;

View File

@ -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<Self> {
use self::Opcode::*;

View File

@ -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<usize, Error> {
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<Vec<u8>>,
}
@ -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());
}
}

View File

@ -44,7 +44,7 @@ impl Deserializable for OutPoint {
#[derive(Debug)]
pub struct TransactionInput {
previous_output: OutPoint,
signature_script: Vec<u8>,
script_sig: Vec<u8>,
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<Self, ReaderError> where Self: Sized {
let previous_output = try!(reader.read());
let signature_script_len = try!(reader.read::<CompactInteger>());
let signature_script = try!(reader.read_bytes(signature_script_len.into())).to_vec();
let script_sig_len = try!(reader.read::<CompactInteger>());
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<u8>,
script_pubkey: Vec<u8>,
}
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<Self, ReaderError> where Self: Sized {
let value = try!(reader.read());
let pk_script_len = try!(reader.read::<CompactInteger>());
let pk_script = try!(reader.read_bytes(pk_script_len.into())).to_vec();
let script_pubkey_len = try!(reader.read::<CompactInteger>());
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]