Implement pruning
This commit is contained in:
parent
d9d7416e32
commit
fc04462682
|
@ -363,6 +363,32 @@ macro_rules! stack_opcode(
|
||||||
});
|
});
|
||||||
)
|
)
|
||||||
|
|
||||||
|
macro_rules! stack_opcode_provable(
|
||||||
|
($stack:ident($min:expr):
|
||||||
|
$(copy $c:expr)*
|
||||||
|
$(swap ($a:expr, $b:expr))*
|
||||||
|
$(perm ($first:expr $(->$i:expr)*) )*
|
||||||
|
$(drop $d:expr)*
|
||||||
|
) => ({
|
||||||
|
// Record top
|
||||||
|
let top = $stack.len();
|
||||||
|
// Do copies
|
||||||
|
$( if top >= $c {
|
||||||
|
let elem = $stack[top - $c].clone();
|
||||||
|
$stack.push(elem);
|
||||||
|
} )*
|
||||||
|
// Do swaps
|
||||||
|
$( if top >= $a && top >= $b { $stack.as_mut_slice().swap(top - $a, top - $b); } )*
|
||||||
|
// Do permutations
|
||||||
|
if top >= $min {
|
||||||
|
$( let first = $first;
|
||||||
|
$( $stack.as_mut_slice().swap(top - first, top - $i); )* )*
|
||||||
|
}
|
||||||
|
// Do drops last so that dropped values will be available above
|
||||||
|
$( if top >= $d { $stack.remove(top - $d); } )*
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
/// Macro to translate numerical operations into stack ones
|
/// Macro to translate numerical operations into stack ones
|
||||||
macro_rules! num_opcode(
|
macro_rules! num_opcode(
|
||||||
($stack:ident($($var:ident),*): $op:expr) => ({
|
($stack:ident($($var:ident),*): $op:expr) => ({
|
||||||
|
@ -376,6 +402,26 @@ macro_rules! num_opcode(
|
||||||
});
|
});
|
||||||
)
|
)
|
||||||
|
|
||||||
|
macro_rules! num_opcode_provable(
|
||||||
|
($stack:ident($($var:ident),*): $op:expr) => ({
|
||||||
|
let mut failed = false;
|
||||||
|
$(
|
||||||
|
let $var = match read_scriptint(match $stack.pop() {
|
||||||
|
Some(elem) => elem,
|
||||||
|
// Out of stack elems: fine, just don't push a new one
|
||||||
|
None => { failed = true; Slice(script_false) }
|
||||||
|
}.as_slice()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
// Overflow is overflow, "provably unspendable"
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
)*
|
||||||
|
if !failed {
|
||||||
|
$stack.push(Owned(build_scriptint($op)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
/// Macro to translate hashing operations into stack ones
|
/// Macro to translate hashing operations into stack ones
|
||||||
macro_rules! hash_opcode(
|
macro_rules! hash_opcode(
|
||||||
($stack:ident, $hash:ident) => ({
|
($stack:ident, $hash:ident) => ({
|
||||||
|
@ -395,6 +441,24 @@ macro_rules! hash_opcode(
|
||||||
});
|
});
|
||||||
)
|
)
|
||||||
|
|
||||||
|
macro_rules! hash_opcode_provable(
|
||||||
|
($stack:ident, $hash:ident) => ({
|
||||||
|
match $stack.pop() {
|
||||||
|
None => { }
|
||||||
|
Some(v) => {
|
||||||
|
let mut engine = $hash::new();
|
||||||
|
engine.input(v.as_slice());
|
||||||
|
let mut ret = Vec::with_capacity(engine.output_bits() / 8);
|
||||||
|
// Force-set the length even though the vector is uninitialized
|
||||||
|
// This is OK only because u8 has no destructor
|
||||||
|
unsafe { ret.set_len(engine.output_bits() / 8); }
|
||||||
|
engine.result(ret.as_mut_slice());
|
||||||
|
$stack.push(Owned(ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
// OP_VERIFY macro
|
// OP_VERIFY macro
|
||||||
macro_rules! op_verify (
|
macro_rules! op_verify (
|
||||||
($stack:expr) => (
|
($stack:expr) => (
|
||||||
|
@ -406,6 +470,16 @@ macro_rules! op_verify (
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
macro_rules! op_verify_provable (
|
||||||
|
($stack:expr) => (
|
||||||
|
match $stack.last().map(|v| read_scriptbool(v.as_slice())) {
|
||||||
|
None => { }
|
||||||
|
Some(false) => { return true; }
|
||||||
|
Some(true) => { $stack.pop(); }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
impl Script {
|
impl Script {
|
||||||
/// Creates a new empty script
|
/// Creates a new empty script
|
||||||
pub fn new() -> Script { Script(ThinVec::new()) }
|
pub fn new() -> Script { Script(ThinVec::new()) }
|
||||||
|
@ -638,7 +712,7 @@ impl Script {
|
||||||
if stack.len() < 2 { return Err(PopEmptyStack); }
|
if stack.len() < 2 { return Err(PopEmptyStack); }
|
||||||
let a = stack.pop().unwrap();
|
let a = stack.pop().unwrap();
|
||||||
let b = stack.pop().unwrap();
|
let b = stack.pop().unwrap();
|
||||||
stack.push(Owned(build_scriptint(if a == b { 1 } else { 0 })));
|
stack.push(Slice(if a == b { script_true } else { script_false }));
|
||||||
if op == opcodes::OP_EQUALVERIFY { op_verify!(stack); }
|
if op == opcodes::OP_EQUALVERIFY { op_verify!(stack); }
|
||||||
}
|
}
|
||||||
opcodes::OP_1ADD => num_opcode!(stack(a): a + 1),
|
opcodes::OP_1ADD => num_opcode!(stack(a): a + 1),
|
||||||
|
@ -785,6 +859,283 @@ impl Script {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate the script to determine whether any possible input will cause it
|
||||||
|
/// to accept. Returns true if it is guaranteed to fail; false otherwise.
|
||||||
|
pub fn provably_unspendable(&self) -> bool {
|
||||||
|
let &Script(ref raw) = self;
|
||||||
|
|
||||||
|
fn recurse<'a>(script: &'a [u8], mut stack: Vec<MaybeOwned<'a>>) -> bool {
|
||||||
|
let mut exec_stack = vec![true];
|
||||||
|
let mut alt_stack = vec![];
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
while index < script.len() {
|
||||||
|
let executing = exec_stack.iter().all(|e| *e);
|
||||||
|
let byte = script[index];
|
||||||
|
index += 1;
|
||||||
|
// The definitions of all these categories are in opcodes.rs
|
||||||
|
//println!("read {} as {} as {} ... stack before op is {}", byte, allops::Opcode::from_u8(byte), allops::Opcode::from_u8(byte).classify(), stack);
|
||||||
|
match (executing, allops::Opcode::from_u8(byte).classify()) {
|
||||||
|
// Illegal operations mean failure regardless of execution state
|
||||||
|
(_, opcodes::IllegalOp) => { return true; }
|
||||||
|
// Push number
|
||||||
|
(true, opcodes::PushNum(n)) => stack.push(Owned(build_scriptint(n as i64))),
|
||||||
|
// Return operations mean failure, but only if executed
|
||||||
|
(true, opcodes::ReturnOp) => { return true; }
|
||||||
|
// Data-reading statements still need to read, even when not executing
|
||||||
|
(_, opcodes::PushBytes(n)) => {
|
||||||
|
if script.len() < index + n { return true; }
|
||||||
|
if executing { stack.push(Slice(script.slice(index, index + n))); }
|
||||||
|
index += n;
|
||||||
|
}
|
||||||
|
(_, opcodes::Ordinary(opcodes::OP_PUSHDATA1)) => {
|
||||||
|
if script.len() < index + 1 { return true; }
|
||||||
|
let n = match read_uint(script.slice_from(index).iter(), 1) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
if script.len() < index + 1 + n { return true; }
|
||||||
|
if executing { stack.push(Slice(script.slice(index + 1, index + n + 1))); }
|
||||||
|
index += 1 + n;
|
||||||
|
}
|
||||||
|
(_, opcodes::Ordinary(opcodes::OP_PUSHDATA2)) => {
|
||||||
|
if script.len() < index + 2 { return true; }
|
||||||
|
let n = match read_uint(script.slice_from(index).iter(), 2) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
if script.len() < index + 2 + n { return true; }
|
||||||
|
if executing { stack.push(Slice(script.slice(index + 2, index + n + 2))); }
|
||||||
|
index += 2 + n;
|
||||||
|
}
|
||||||
|
(_, opcodes::Ordinary(opcodes::OP_PUSHDATA4)) => {
|
||||||
|
let n = match read_uint(script.slice_from(index).iter(), 4) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
if script.len() < index + 4 + n { return true; }
|
||||||
|
if executing { stack.push(Slice(script.slice(index + 4, index + n + 4))); }
|
||||||
|
index += 4 + n;
|
||||||
|
}
|
||||||
|
// If-statements take effect when not executing
|
||||||
|
(false, opcodes::Ordinary(opcodes::OP_IF)) => exec_stack.push(false),
|
||||||
|
(false, opcodes::Ordinary(opcodes::OP_NOTIF)) => exec_stack.push(false),
|
||||||
|
(false, opcodes::Ordinary(opcodes::OP_ELSE)) => {
|
||||||
|
match exec_stack.mut_last() {
|
||||||
|
Some(ref_e) => { *ref_e = !*ref_e }
|
||||||
|
None => { return true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, opcodes::Ordinary(opcodes::OP_ENDIF)) => {
|
||||||
|
if exec_stack.pop().is_none() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No-ops and non-executed operations do nothing
|
||||||
|
(true, opcodes::NoOp) | (false, _) => {}
|
||||||
|
// Actual opcodes
|
||||||
|
(true, opcodes::Ordinary(op)) => {
|
||||||
|
match op {
|
||||||
|
opcodes::OP_PUSHDATA1 | opcodes::OP_PUSHDATA2 | opcodes::OP_PUSHDATA4 => {
|
||||||
|
// handled above
|
||||||
|
}
|
||||||
|
opcodes::OP_IF => {
|
||||||
|
match stack.pop().map(|v| read_scriptbool(v.as_slice())) {
|
||||||
|
None => {
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return recurse(script.slice_from(index - 1), stack) &&
|
||||||
|
recurse(script.slice_from(index - 1), stack_true);
|
||||||
|
}
|
||||||
|
Some(b) => exec_stack.push(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opcodes::OP_NOTIF => {
|
||||||
|
match stack.pop().map(|v| read_scriptbool(v.as_slice())) {
|
||||||
|
None => {
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return recurse(script.slice_from(index - 1), stack) &&
|
||||||
|
recurse(script.slice_from(index - 1), stack_true);
|
||||||
|
}
|
||||||
|
Some(b) => exec_stack.push(!b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opcodes::OP_ELSE => {
|
||||||
|
match exec_stack.mut_last() {
|
||||||
|
Some(ref_e) => { *ref_e = !*ref_e }
|
||||||
|
None => { return true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opcodes::OP_ENDIF => {
|
||||||
|
if exec_stack.pop().is_none() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opcodes::OP_VERIFY => op_verify_provable!(stack),
|
||||||
|
opcodes::OP_TOALTSTACK => { stack.pop().map(|elem| alt_stack.push(elem)); }
|
||||||
|
opcodes::OP_FROMALTSTACK => { alt_stack.pop().map(|elem| stack.push(elem)); }
|
||||||
|
opcodes::OP_2DROP => stack_opcode_provable!(stack(2): drop 1 drop 2),
|
||||||
|
opcodes::OP_2DUP => stack_opcode_provable!(stack(2): copy 2 copy 1),
|
||||||
|
opcodes::OP_3DUP => stack_opcode_provable!(stack(3): copy 3 copy 2 copy 1),
|
||||||
|
opcodes::OP_2OVER => stack_opcode_provable!(stack(4): copy 4 copy 3),
|
||||||
|
opcodes::OP_2ROT => stack_opcode_provable!(stack(6): perm (1 -> 3 -> 5)
|
||||||
|
perm (2 -> 4 -> 6)),
|
||||||
|
opcodes::OP_2SWAP => stack_opcode_provable!(stack(4): swap (2, 4)
|
||||||
|
swap (1, 3)),
|
||||||
|
opcodes::OP_DROP => stack_opcode_provable!(stack(1): drop 1),
|
||||||
|
opcodes::OP_DUP => stack_opcode_provable!(stack(1): copy 1),
|
||||||
|
opcodes::OP_NIP => stack_opcode_provable!(stack(2): drop 2),
|
||||||
|
opcodes::OP_OVER => stack_opcode_provable!(stack(2): copy 2),
|
||||||
|
opcodes::OP_PICK => {
|
||||||
|
let n = match stack.pop() {
|
||||||
|
Some(data) => match read_scriptint(data.as_slice()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
},
|
||||||
|
None => { return false; }
|
||||||
|
};
|
||||||
|
if n < 0 { return true; }
|
||||||
|
let n = n as uint;
|
||||||
|
stack_opcode_provable!(stack(n + 1): copy n + 1)
|
||||||
|
}
|
||||||
|
opcodes::OP_ROLL => {
|
||||||
|
let n = match stack.pop() {
|
||||||
|
Some(data) => match read_scriptint(data.as_slice()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
},
|
||||||
|
None => { return false; }
|
||||||
|
};
|
||||||
|
if n < 0 { return true; }
|
||||||
|
let n = n as uint;
|
||||||
|
stack_opcode_provable!(stack(n + 1): copy n + 1 drop n + 1)
|
||||||
|
}
|
||||||
|
opcodes::OP_ROT => stack_opcode_provable!(stack(3): perm (1 -> 2 -> 3)),
|
||||||
|
opcodes::OP_SWAP => stack_opcode_provable!(stack(2): swap (1, 2)),
|
||||||
|
opcodes::OP_TUCK => stack_opcode_provable!(stack(2): copy 2 copy 1 drop 2),
|
||||||
|
opcodes::OP_IFDUP => {
|
||||||
|
match stack.last().map(|v| read_scriptbool(v.as_slice())) {
|
||||||
|
None => {
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return recurse(script.slice_from(index - 1), stack) &&
|
||||||
|
recurse(script.slice_from(index - 1), stack_true);
|
||||||
|
}
|
||||||
|
Some(false) => {}
|
||||||
|
Some(true) => { stack_opcode_provable!(stack(1): copy 1); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not clear what we can meaningfully do with these (I guess add an
|
||||||
|
// `Unknown` variant to `MaybeOwned` and change all the code to deal
|
||||||
|
// with it), and they aren't common, so just say "not unspendable".
|
||||||
|
opcodes::OP_DEPTH | opcodes::OP_SIZE => { return false; }
|
||||||
|
opcodes::OP_EQUAL | opcodes::OP_EQUALVERIFY => {
|
||||||
|
if stack.len() < 2 {
|
||||||
|
stack.pop();
|
||||||
|
stack.pop();
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return (op == opcodes::OP_EQUALVERIFY || recurse(script.slice_from(index), stack)) &&
|
||||||
|
recurse(script.slice_from(index), stack_true);
|
||||||
|
}
|
||||||
|
let a = stack.pop().unwrap();
|
||||||
|
let b = stack.pop().unwrap();
|
||||||
|
stack.push(Slice(if a == b { script_true } else { script_false }));
|
||||||
|
if op == opcodes::OP_EQUALVERIFY { op_verify_provable!(stack); }
|
||||||
|
}
|
||||||
|
opcodes::OP_1ADD => num_opcode_provable!(stack(a): a + 1),
|
||||||
|
opcodes::OP_1SUB => num_opcode_provable!(stack(a): a - 1),
|
||||||
|
opcodes::OP_NEGATE => num_opcode_provable!(stack(a): -a),
|
||||||
|
opcodes::OP_ABS => num_opcode_provable!(stack(a): a.abs()),
|
||||||
|
opcodes::OP_NOT => num_opcode_provable!(stack(a): if a == 0 {1} else {0}),
|
||||||
|
opcodes::OP_0NOTEQUAL => num_opcode_provable!(stack(a): if a != 0 {1} else {0}),
|
||||||
|
opcodes::OP_ADD => num_opcode_provable!(stack(b, a): a + b),
|
||||||
|
opcodes::OP_SUB => num_opcode_provable!(stack(b, a): a - b),
|
||||||
|
opcodes::OP_BOOLAND => num_opcode_provable!(stack(b, a): if a != 0 && b != 0 {1} else {0}),
|
||||||
|
opcodes::OP_BOOLOR => num_opcode_provable!(stack(b, a): if a != 0 || b != 0 {1} else {0}),
|
||||||
|
opcodes::OP_NUMEQUAL => num_opcode_provable!(stack(b, a): if a == b {1} else {0}),
|
||||||
|
opcodes::OP_NUMNOTEQUAL => num_opcode_provable!(stack(b, a): if a != b {1} else {0}),
|
||||||
|
opcodes::OP_NUMEQUALVERIFY => {
|
||||||
|
num_opcode_provable!(stack(b, a): if a == b {1} else {0});
|
||||||
|
op_verify_provable!(stack);
|
||||||
|
}
|
||||||
|
opcodes::OP_LESSTHAN => num_opcode_provable!(stack(b, a): if a < b {1} else {0}),
|
||||||
|
opcodes::OP_GREATERTHAN => num_opcode_provable!(stack(b, a): if a > b {1} else {0}),
|
||||||
|
opcodes::OP_LESSTHANOREQUAL => num_opcode_provable!(stack(b, a): if a <= b {1} else {0}),
|
||||||
|
opcodes::OP_GREATERTHANOREQUAL => num_opcode_provable!(stack(b, a): if a >= b {1} else {0}),
|
||||||
|
opcodes::OP_MIN => num_opcode_provable!(stack(b, a): if a < b {a} else {b}),
|
||||||
|
opcodes::OP_MAX => num_opcode_provable!(stack(b, a): if a > b {a} else {b}),
|
||||||
|
opcodes::OP_WITHIN => num_opcode_provable!(stack(c, b, a): if b <= a && a < c {1} else {0}),
|
||||||
|
opcodes::OP_RIPEMD160 => hash_opcode_provable!(stack, Ripemd160),
|
||||||
|
opcodes::OP_SHA1 => hash_opcode_provable!(stack, Sha1),
|
||||||
|
opcodes::OP_SHA256 => hash_opcode_provable!(stack, Sha256),
|
||||||
|
opcodes::OP_HASH160 => {
|
||||||
|
hash_opcode_provable!(stack, Sha256);
|
||||||
|
hash_opcode_provable!(stack, Ripemd160);
|
||||||
|
}
|
||||||
|
opcodes::OP_HASH256 => {
|
||||||
|
hash_opcode_provable!(stack, Sha256);
|
||||||
|
hash_opcode_provable!(stack, Sha256);
|
||||||
|
}
|
||||||
|
// Ignore code separators since we won't check signatures
|
||||||
|
opcodes::OP_CODESEPARATOR => {}
|
||||||
|
opcodes::OP_CHECKSIG | opcodes::OP_CHECKSIGVERIFY => {
|
||||||
|
stack.pop();
|
||||||
|
stack.pop();
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return (op == opcodes::OP_CHECKSIGVERIFY || recurse(script.slice_from(index), stack)) &&
|
||||||
|
recurse(script.slice_from(index), stack_true);
|
||||||
|
}
|
||||||
|
opcodes::OP_CHECKMULTISIG | opcodes::OP_CHECKMULTISIGVERIFY => {
|
||||||
|
// Read all the keys
|
||||||
|
if stack.len() >= 1 {
|
||||||
|
let n_keys = match read_scriptint(stack.pop().unwrap().as_slice()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
if n_keys < 0 || n_keys > 20 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for _ in range(0, n_keys) { stack.pop(); }
|
||||||
|
|
||||||
|
// Read all the signatures
|
||||||
|
if stack.len() >= 1 {
|
||||||
|
let n_sigs = match read_scriptint(stack.pop().unwrap().as_slice()) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => { return true; }
|
||||||
|
};
|
||||||
|
if n_sigs < 0 || n_sigs > n_keys {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for _ in range(0, n_sigs) { stack.pop(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop one more element off the stack to be replicate a consensus bug
|
||||||
|
stack.pop();
|
||||||
|
|
||||||
|
let mut stack_true = stack.clone();
|
||||||
|
stack_true.push(Slice(script_true));
|
||||||
|
stack.push(Slice(script_false));
|
||||||
|
return (op == opcodes::OP_CHECKMULTISIGVERIFY || recurse(script.slice_from(index), stack)) &&
|
||||||
|
recurse(script.slice_from(index), stack_true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
recurse(raw.as_slice(), vec![])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// User-facing serialization
|
// User-facing serialization
|
||||||
|
@ -1077,6 +1428,12 @@ mod test {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn provably_unspendable_test() {
|
||||||
|
assert_eq!(Script(ThinVec::from_vec("a9149eb21980dc9d413d8eac27314938b9da920ee53e87".from_hex().unwrap())).provably_unspendable(), false);
|
||||||
|
assert_eq!(Script(ThinVec::from_vec("6aa9149eb21980dc9d413d8eac27314938b9da920ee53e87".from_hex().unwrap())).provably_unspendable(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,10 +56,11 @@ pub struct UtxoSet {
|
||||||
spent_txos: Vec<Vec<((Sha256dHash, u32), TxOut)>>,
|
spent_txos: Vec<Vec<((Sha256dHash, u32), TxOut)>>,
|
||||||
// The last index into the above buffer that was assigned to
|
// The last index into the above buffer that was assigned to
|
||||||
spent_idx: u64,
|
spent_idx: u64,
|
||||||
n_utxos: u64
|
n_utxos: u64,
|
||||||
|
n_pruned: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, spent_txos, spent_idx, table)
|
impl_consensus_encoding!(UtxoSet, last_hash, n_utxos, n_pruned, spent_txos, spent_idx, table)
|
||||||
|
|
||||||
impl UtxoSet {
|
impl UtxoSet {
|
||||||
/// Constructs a new UTXO set
|
/// Constructs a new UTXO set
|
||||||
|
@ -73,7 +74,8 @@ impl UtxoSet {
|
||||||
last_hash: genesis_block(network).header.bitcoin_hash(),
|
last_hash: genesis_block(network).header.bitcoin_hash(),
|
||||||
spent_txos: Vec::from_elem(rewind_limit, vec![]),
|
spent_txos: Vec::from_elem(rewind_limit, vec![]),
|
||||||
spent_idx: 0,
|
spent_idx: 0,
|
||||||
n_utxos: 0
|
n_utxos: 0,
|
||||||
|
n_pruned: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +87,14 @@ impl UtxoSet {
|
||||||
let mut new_node = ThinVec::with_capacity(tx.output.len() as u32);
|
let mut new_node = ThinVec::with_capacity(tx.output.len() as u32);
|
||||||
for (vout, txo) in tx.output.iter().enumerate() {
|
for (vout, txo) in tx.output.iter().enumerate() {
|
||||||
// Unsafe since we are not uninitializing the old data in the vector
|
// Unsafe since we are not uninitializing the old data in the vector
|
||||||
|
if txo.script_pubkey.provably_unspendable() {
|
||||||
|
new_node.init(vout as uint, None);
|
||||||
|
self.n_utxos -= 1;
|
||||||
|
self.n_pruned += 1;
|
||||||
|
} else {
|
||||||
new_node.init(vout as uint, Some(txo.clone()));
|
new_node.init(vout as uint, Some(txo.clone()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
new_node
|
new_node
|
||||||
};
|
};
|
||||||
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
|
// Get the old value, if any (this is suprisingly possible, c.f. BIP30
|
||||||
|
@ -345,6 +353,12 @@ impl UtxoSet {
|
||||||
pub fn n_utxos(&self) -> uint {
|
pub fn n_utxos(&self) -> uint {
|
||||||
self.n_utxos as uint
|
self.n_utxos as uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the number of UTXOs ever pruned from the set (this is not updated
|
||||||
|
/// during reorgs, so it may return a higher number than is realistic).
|
||||||
|
pub fn n_pruned(&self) -> uint {
|
||||||
|
self.n_pruned as uint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in New Issue