Pull script tracing code out of Transcation so it can be used independently

This commit is contained in:
Andrew Poelstra 2014-08-22 12:14:39 -07:00
parent 611b1f57c9
commit e3445ebef7
2 changed files with 42 additions and 53 deletions

View File

@ -114,6 +114,19 @@ pub struct TraceIteration {
stack: Vec<String> stack: Vec<String>
} }
/// A full trace of a script execution
#[deriving(PartialEq, Eq, Show, Clone)]
pub struct ScriptTrace {
/// A copy of the script
pub script: Script,
/// A copy of the script's initial stack, hex-encoded
pub initial_stack: Vec<String>,
/// A list of iterations
pub iterations: Vec<TraceIteration>,
/// An error if one was returned, or None
pub error: Option<ScriptError>
}
impl_json!(TraceIteration, index, opcode, executed, effect, stack) impl_json!(TraceIteration, index, opcode, executed, effect, stack)
/// Hashtype of a transaction, encoded in the last byte of a signature, /// Hashtype of a transaction, encoded in the last byte of a signature,
@ -580,6 +593,24 @@ impl Script {
raw.as_slice() raw.as_slice()
} }
/// Trace a script
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
input_context: Option<(&Transaction, uint)>)
-> ScriptTrace {
let mut trace = ScriptTrace {
script: self.clone(),
initial_stack: stack.iter().map(|elem| elem.as_slice().to_hex()).collect(),
iterations: vec![],
error: None
};
match self.evaluate(stack, input_context, Some(&mut trace.iterations)) {
Ok(_) => {},
Err(e) => { trace.error = Some(e.clone()); }
}
trace
}
/// Evaluate the script, modifying the stack in place /// Evaluate the script, modifying the stack in place
pub fn evaluate<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>, pub fn evaluate<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
input_context: Option<(&Transaction, uint)>, input_context: Option<(&Transaction, uint)>,

View File

@ -24,11 +24,10 @@
//! //!
use std::default::Default; use std::default::Default;
use serialize::hex::ToHex;
use serialize::json; use serialize::json;
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
use blockdata::script::{mod, Script, ScriptError, TraceIteration, read_scriptbool}; use blockdata::script::{mod, Script, ScriptError, ScriptTrace, read_scriptbool};
use blockdata::utxoset::UtxoSet; use blockdata::utxoset::UtxoSet;
use network::encodable::ConsensusEncodable; use network::encodable::ConsensusEncodable;
use network::serialize::BitcoinHash; use network::serialize::BitcoinHash;
@ -107,15 +106,6 @@ impl json::ToJson for TransactionError {
} }
} }
/// A trace of a script execution
#[deriving(PartialEq, Eq, Show, Clone)]
pub struct ScriptTrace {
script: Script,
initial_stack: Vec<String>,
iterations: Vec<TraceIteration>,
error: Option<ScriptError>
}
/// A trace of a transaction input's script execution /// A trace of a transaction input's script execution
#[deriving(PartialEq, Eq, Clone, Show)] #[deriving(PartialEq, Eq, Clone, Show)]
pub struct InputTrace { pub struct InputTrace {
@ -223,16 +213,10 @@ impl Transaction {
let mut p2sh_script = Script::new(); let mut p2sh_script = Script::new();
let mut stack = Vec::with_capacity(6); let mut stack = Vec::with_capacity(6);
trace.sig_trace.script = input.script_sig.clone(); trace.sig_trace = input.script_sig.trace(&mut stack, Some((self, n)));
match input.script_sig.evaluate(&mut stack, let err = trace.sig_trace.error.as_ref().map(|e| e.clone());
Some((self, n)), err.map(|e| trace.error = Some(InputScriptFailure(e)));
Some(&mut trace.sig_trace.iterations)) {
Ok(_) => {}
Err(e) => {
trace.sig_trace.error = Some(e.clone());
trace.error = Some(InputScriptFailure(e));
}
}
if txo.script_pubkey.is_p2sh() && stack.len() > 0 { if txo.script_pubkey.is_p2sh() && stack.len() > 0 {
p2sh_stack = stack.clone(); p2sh_stack = stack.clone();
p2sh_script = match p2sh_stack.pop() { p2sh_script = match p2sh_stack.pop() {
@ -242,21 +226,9 @@ impl Transaction {
}; };
} }
if trace.error.is_none() { if trace.error.is_none() {
let mut pk_trace = ScriptTrace { trace.pubkey_trace = Some(txo.script_pubkey.trace(&mut stack, Some((self, n))));
script: txo.script_pubkey.clone(), let err = trace.pubkey_trace.get_ref().error.as_ref().map(|e| e.clone());
initial_stack: stack.iter().map(|elem| elem.as_slice().to_hex()).collect(), err.map(|e| trace.error = Some(OutputScriptFailure(e)));
iterations: vec![],
error: None
};
match txo.script_pubkey.evaluate(&mut stack,
Some((self, n)),
Some(&mut pk_trace.iterations)) {
Ok(_) => {}
Err(e) => {
pk_trace.error = Some(e.clone());
trace.error = Some(OutputScriptFailure(e));
}
}
match stack.pop() { match stack.pop() {
Some(v) => { Some(v) => {
if !read_scriptbool(v.as_slice()) { if !read_scriptbool(v.as_slice()) {
@ -265,23 +237,10 @@ impl Transaction {
} }
None => { trace.error = Some(ScriptReturnedEmptyStack); } None => { trace.error = Some(ScriptReturnedEmptyStack); }
} }
trace.pubkey_trace = Some(pk_trace);
if trace.error.is_none() && txo.script_pubkey.is_p2sh() { if trace.error.is_none() && txo.script_pubkey.is_p2sh() {
let mut p2sh_trace = ScriptTrace { trace.p2sh_trace = Some(p2sh_script.trace(&mut p2sh_stack, Some((self, n))));
script: p2sh_script.clone(), let err = trace.p2sh_trace.get_ref().error.as_ref().map(|e| e.clone());
initial_stack: p2sh_stack.iter().map(|elem| elem.as_slice().to_hex()).collect(), err.map(|e| trace.error = Some(P2shScriptFailure(e)));
iterations: vec![],
error: None
};
match p2sh_script.evaluate(&mut p2sh_stack,
Some((self, n)),
Some(&mut p2sh_trace.iterations)) {
Ok(_) => {}
Err(e) => {
p2sh_trace.error = Some(e.clone());
trace.error = Some(P2shScriptFailure(e));
}
}
match p2sh_stack.pop() { match p2sh_stack.pop() {
Some(v) => { Some(v) => {
if !read_scriptbool(v.as_slice()) { if !read_scriptbool(v.as_slice()) {
@ -290,7 +249,6 @@ impl Transaction {
} }
None => { trace.error = Some(P2shScriptReturnedEmptyStack); } None => { trace.error = Some(P2shScriptReturnedEmptyStack); }
} }
trace.p2sh_trace = Some(p2sh_trace);
} }
} }
} }