Checkpoint commit
Work is stalled on some other library work (to give better lifetime requirements on `eventual::Future` and avoid some unsafety), so committing here. There are only three errors left in this round :) Also all the indenting is done, so there should be no more massive rewrite commits. Depending how invasive the lifetime-error fixes are, I may even be able to do sanely sized commits from here on.
This commit is contained in:
parent
200e0fe8e3
commit
08a20f8764
|
@ -11,6 +11,9 @@ path = "src/lib.rs"
|
||||||
[dependencies.secp256k1]
|
[dependencies.secp256k1]
|
||||||
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
|
git = "https://github.com/apoelstra/bitcoin-secp256k1-rs.git"
|
||||||
|
|
||||||
|
[dependencies.eventual]
|
||||||
|
git = "https://github.com/carllerche/eventual"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "*"
|
byteorder = "*"
|
||||||
num_cpus = "*"
|
num_cpus = "*"
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl BlockHeader {
|
||||||
let mut ret = !self.target();
|
let mut ret = !self.target();
|
||||||
let mut ret1 = self.target();
|
let mut ret1 = self.target();
|
||||||
ret1.increment();
|
ret1.increment();
|
||||||
ret = ret.div(&ret1);
|
ret = ret / ret1;
|
||||||
ret.increment();
|
ret.increment();
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ impl BlockHeader {
|
||||||
impl BitcoinHash for BlockHeader {
|
impl BitcoinHash for BlockHeader {
|
||||||
fn bitcoin_hash(&self) -> Sha256dHash {
|
fn bitcoin_hash(&self) -> Sha256dHash {
|
||||||
use network::serialize::serialize;
|
use network::serialize::serialize;
|
||||||
Sha256dHash::from_data(serialize(self).unwrap().as_slice())
|
Sha256dHash::from_data(&serialize(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl BlockchainNode {
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut scan = self.next;
|
let mut scan = self.next;
|
||||||
while scan.is_not_null() {
|
while !scan.is_null() {
|
||||||
if (*scan).block.header == (*chain.best_tip).block.header {
|
if (*scan).block.header == (*chain.best_tip).block.header {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -145,15 +145,15 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Blockchain {
|
||||||
let best = match tree.lookup(&best_hash.into_le(), 256) {
|
let best = match tree.lookup(&best_hash.into_le(), 256) {
|
||||||
Some(node) => &**node as NodePtr,
|
Some(node) => &**node as NodePtr,
|
||||||
None => {
|
None => {
|
||||||
return Err(d.error(format!("best tip {:x} not in tree", best_hash).as_slice()));
|
return Err(d.error(format!("best tip {:x} not in tree", best_hash)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Lookup genesis
|
// Lookup genesis
|
||||||
if tree.lookup(&genesis_hash.into_le(), 256).is_none() {
|
if tree.lookup(&genesis_hash.into_le(), 256).is_none() {
|
||||||
return Err(d.error(format!("genesis {:x} not in tree", genesis_hash).as_slice()));
|
return Err(d.error(format!("genesis {:x} not in tree", genesis_hash)));
|
||||||
}
|
}
|
||||||
// Reconnect all prev pointers
|
// Reconnect all prev pointers
|
||||||
let raw_tree = &tree as *const _;
|
let raw_tree = &tree as *const BlockTree;
|
||||||
for node in tree.mut_iter() {
|
for node in tree.mut_iter() {
|
||||||
let hash = node.block.header.prev_blockhash.into_le();
|
let hash = node.block.header.prev_blockhash.into_le();
|
||||||
let prevptr =
|
let prevptr =
|
||||||
|
@ -166,7 +166,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Blockchain {
|
||||||
// Reconnect next pointers on the main chain
|
// Reconnect next pointers on the main chain
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut scan = best;
|
let mut scan = best;
|
||||||
while (*scan).prev.is_not_null() {
|
while !(*scan).prev.is_null() {
|
||||||
let prev = (*scan).prev as *mut BlockchainNode;
|
let prev = (*scan).prev as *mut BlockchainNode;
|
||||||
(*prev).next = scan;
|
(*prev).next = scan;
|
||||||
scan = prev as NodePtr;
|
scan = prev as NodePtr;
|
||||||
|
@ -175,7 +175,7 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Blockchain {
|
||||||
// Check that "genesis" is the genesis
|
// Check that "genesis" is the genesis
|
||||||
if (*scan).bitcoin_hash() != genesis_hash {
|
if (*scan).bitcoin_hash() != genesis_hash {
|
||||||
return Err(d.error(format!("no path from tip {:x} to genesis {:x}",
|
return Err(d.error(format!("no path from tip {:x} to genesis {:x}",
|
||||||
best_hash, genesis_hash).as_slice()));
|
best_hash, genesis_hash)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ impl Iterator for LocatorHashIter {
|
||||||
// Rewind once (if we are at the genesis, this will set self.index to None)
|
// Rewind once (if we are at the genesis, this will set self.index to None)
|
||||||
self.index = unsafe { (*self.index).prev };
|
self.index = unsafe { (*self.index).prev };
|
||||||
// If we are not at the genesis, rewind `self.skip` times, or until we are.
|
// If we are not at the genesis, rewind `self.skip` times, or until we are.
|
||||||
if self.index.is_not_null() {
|
if !self.index.is_null() {
|
||||||
for _ in 1..self.skip {
|
for _ in 1..self.skip {
|
||||||
unsafe {
|
unsafe {
|
||||||
if (*self.index).prev.is_null() {
|
if (*self.index).prev.is_null() {
|
||||||
|
@ -318,7 +318,7 @@ impl<'tree> Iterator for RevStaleBlockIter<'tree> {
|
||||||
let ret = Some(&(*self.index).block);
|
let ret = Some(&(*self.index).block);
|
||||||
let next_index = (*self.index).prev;
|
let next_index = (*self.index).prev;
|
||||||
// Check if the next block is going to be on the main chain
|
// Check if the next block is going to be on the main chain
|
||||||
if next_index.is_not_null() &&
|
if !next_index.is_null() &&
|
||||||
(*next_index).next != self.index &&
|
(*next_index).next != self.index &&
|
||||||
(&*next_index).is_on_main_chain(self.chain) {
|
(&*next_index).is_on_main_chain(self.chain) {
|
||||||
self.index = ptr::null();
|
self.index = ptr::null();
|
||||||
|
@ -334,7 +334,7 @@ impl<'tree> Iterator for RevStaleBlockIter<'tree> {
|
||||||
/// which drops the precision to something that can be encoded precisely in
|
/// which drops the precision to something that can be encoded precisely in
|
||||||
/// the nBits block header field. Savour the perversity. This is in Bitcoin
|
/// the nBits block header field. Savour the perversity. This is in Bitcoin
|
||||||
/// consensus code. What. Gaah!
|
/// consensus code. What. Gaah!
|
||||||
fn satoshi_the_precision(n: &Uint256) -> Uint256 {
|
fn satoshi_the_precision(n: Uint256) -> Uint256 {
|
||||||
// Shift by B bits right then left to turn the low bits to zero
|
// Shift by B bits right then left to turn the low bits to zero
|
||||||
let bits = 8 * ((n.bits() + 7) / 8 - 3);
|
let bits = 8 * ((n.bits() + 7) / 8 - 3);
|
||||||
let mut ret = n >> bits;
|
let mut ret = n >> bits;
|
||||||
|
@ -475,16 +475,16 @@ impl Blockchain {
|
||||||
let max = max_target(self.network);
|
let max = max_target(self.network);
|
||||||
if target > max { target = max };
|
if target > max { target = max };
|
||||||
// Compactify (make expressible in the 8+24 nBits float format
|
// Compactify (make expressible in the 8+24 nBits float format
|
||||||
satoshi_the_precision(&target)
|
satoshi_the_precision(target)
|
||||||
// On non-diffchange blocks, Testnet has a rule that any 20-minute-long
|
// On non-diffchange blocks, Testnet has a rule that any 20-minute-long
|
||||||
// block intervals result the difficulty
|
// block intervals result the difficulty
|
||||||
} else if self.network == Network::BitcoinTestnet &&
|
} else if self.network == Network::Testnet &&
|
||||||
block.header.time > unsafe { (*prev).block.header.time } + 2*TARGET_BLOCK_SPACING {
|
block.header.time > unsafe { (*prev).block.header.time } + 2*TARGET_BLOCK_SPACING {
|
||||||
max_target(self.network)
|
max_target(self.network)
|
||||||
// On the other hand, if we are in Testnet and the block interval is less
|
// On the other hand, if we are in Testnet and the block interval is less
|
||||||
// than 20 minutes, we need to scan backward to find a block for which the
|
// than 20 minutes, we need to scan backward to find a block for which the
|
||||||
// previous rule did not apply, to find the "real" difficulty.
|
// previous rule did not apply, to find the "real" difficulty.
|
||||||
} else if self.network == Network::BitcoinTestnet {
|
} else if self.network == Network::Testnet {
|
||||||
// Scan back DIFFCHANGE_INTERVAL blocks
|
// Scan back DIFFCHANGE_INTERVAL blocks
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut scan = prev;
|
let mut scan = prev;
|
||||||
|
@ -500,7 +500,7 @@ impl Blockchain {
|
||||||
};
|
};
|
||||||
// Create node
|
// Create node
|
||||||
let ret = Box::new(BlockchainNode {
|
let ret = Box::new(BlockchainNode {
|
||||||
total_work: block.header.work().add(unsafe { &(*prev).total_work }),
|
total_work: block.header.work() + unsafe { (*prev).total_work },
|
||||||
block: block,
|
block: block,
|
||||||
required_difficulty: difficulty,
|
required_difficulty: difficulty,
|
||||||
height: unsafe { (*prev).height + 1 },
|
height: unsafe { (*prev).height + 1 },
|
||||||
|
@ -538,7 +538,7 @@ impl Blockchain {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut scan = self.best_tip;
|
let mut scan = self.best_tip;
|
||||||
// Scan backward
|
// Scan backward
|
||||||
while (*scan).prev.is_not_null() {
|
while !(*scan).prev.is_null() {
|
||||||
// If we hit the old best, there is no need to reorg.
|
// If we hit the old best, there is no need to reorg.
|
||||||
if scan == self.best_tip { break; }
|
if scan == self.best_tip { break; }
|
||||||
// Otherwise set the next-ptr and carry on
|
// Otherwise set the next-ptr and carry on
|
||||||
|
|
|
@ -23,7 +23,7 @@ use std::default::Default;
|
||||||
use std::num::from_u64;
|
use std::num::from_u64;
|
||||||
|
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::ScriptBuilder;
|
||||||
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
||||||
use blockdata::block::{Block, BlockHeader};
|
use blockdata::block::{Block, BlockHeader};
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
|
@ -60,24 +60,24 @@ fn bitcoin_genesis_tx() -> Transaction {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
let mut in_script = Script::new();
|
let mut in_script = ScriptBuilder::new();
|
||||||
in_script.push_scriptint(486604799);
|
in_script.push_scriptint(486604799);
|
||||||
in_script.push_scriptint(4);
|
in_script.push_scriptint(4);
|
||||||
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||||
ret.input.push(TxIn {
|
ret.input.push(TxIn {
|
||||||
prev_hash: Default::default(),
|
prev_hash: Default::default(),
|
||||||
prev_index: 0xFFFFFFFF,
|
prev_index: 0xFFFFFFFF,
|
||||||
script_sig: in_script,
|
script_sig: in_script.into_script(),
|
||||||
sequence: MAX_SEQUENCE
|
sequence: MAX_SEQUENCE
|
||||||
});
|
});
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
let mut out_script = Script::new();
|
let mut out_script = ScriptBuilder::new();
|
||||||
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
||||||
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
out_script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||||
ret.output.push(TxOut {
|
ret.output.push(TxOut {
|
||||||
value: 50 * COIN_VALUE,
|
value: 50 * COIN_VALUE,
|
||||||
script_pubkey: out_script
|
script_pubkey: out_script.into_script()
|
||||||
});
|
});
|
||||||
|
|
||||||
// end
|
// end
|
||||||
|
|
|
@ -592,10 +592,10 @@ impl All {
|
||||||
// 16 opcodes
|
// 16 opcodes
|
||||||
} else if All::OP_PUSHNUM_1 as u8 <= *self as u8 &&
|
} else if All::OP_PUSHNUM_1 as u8 <= *self as u8 &&
|
||||||
*self as u8 <= All::OP_PUSHNUM_16 as u8 {
|
*self as u8 <= All::OP_PUSHNUM_16 as u8 {
|
||||||
Class::PushNum(1 + *self as isize - All::OP_PUSHNUM_1 as isize)
|
Class::PushNum(1 + *self as i32 - All::OP_PUSHNUM_1 as i32)
|
||||||
// 76 opcodes
|
// 76 opcodes
|
||||||
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
|
} else if *self as u8 <= All::OP_PUSHBYTES_75 as u8 {
|
||||||
Class::PushBytes(*self as usize)
|
Class::PushBytes(*self as u32)
|
||||||
// 60 opcodes
|
// 60 opcodes
|
||||||
} else {
|
} else {
|
||||||
Class::Ordinary(unsafe { transmute(*self) })
|
Class::Ordinary(unsafe { transmute(*self) })
|
||||||
|
@ -636,9 +636,9 @@ pub static OP_TRUE: All = All::OP_PUSHNUM_1;
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Class {
|
pub enum Class {
|
||||||
/// Pushes the given number onto the stack
|
/// Pushes the given number onto the stack
|
||||||
PushNum(isize),
|
PushNum(i32),
|
||||||
/// Pushes the given number of bytes onto the stack
|
/// Pushes the given number of bytes onto the stack
|
||||||
PushBytes(usize),
|
PushBytes(u32),
|
||||||
/// Fails the script if executed
|
/// Fails the script if executed
|
||||||
ReturnOp,
|
ReturnOp,
|
||||||
/// Fails the script even if not executed
|
/// Fails the script even if not executed
|
||||||
|
|
|
@ -35,7 +35,7 @@ use crypto::digest::Digest;
|
||||||
use crypto::ripemd160::Ripemd160;
|
use crypto::ripemd160::Ripemd160;
|
||||||
use crypto::sha1::Sha1;
|
use crypto::sha1::Sha1;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use secp256k1::Secp256k1;
|
use secp256k1::{self, Secp256k1};
|
||||||
use secp256k1::key::PublicKey;
|
use secp256k1::key::PublicKey;
|
||||||
use serde;
|
use serde;
|
||||||
|
|
||||||
|
@ -56,13 +56,16 @@ impl Clone for Script {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Display)]
|
||||||
|
/// An object which can be used to construct a script piece by piece
|
||||||
|
pub struct ScriptBuilder(Vec<u8>);
|
||||||
|
|
||||||
impl hash::Hash for Script {
|
impl hash::Hash for Script {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where H: hash::Hasher
|
where H: hash::Hasher
|
||||||
{
|
{
|
||||||
let &Script(ref raw) = self;
|
(&self.0[..]).hash(state);
|
||||||
(&raw[..]).hash(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -70,8 +73,7 @@ impl hash::Hash for Script {
|
||||||
where H: hash::Hasher
|
where H: hash::Hasher
|
||||||
{
|
{
|
||||||
for s in data.iter() {
|
for s in data.iter() {
|
||||||
let &Script(ref raw) = s;
|
(&s.0[..]).hash(state);
|
||||||
(&raw[..]).hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ pub enum Error {
|
||||||
/// OP_CHECKSIG was called with a bad signature
|
/// OP_CHECKSIG was called with a bad signature
|
||||||
BadSignature,
|
BadSignature,
|
||||||
/// An ECDSA error
|
/// An ECDSA error
|
||||||
Ecdsa(::secp256k1::Error),
|
Ecdsa(secp256k1::Error),
|
||||||
/// An OP_ELSE happened while not in an OP_IF tree
|
/// An OP_ELSE happened while not in an OP_IF tree
|
||||||
ElseWithoutIf,
|
ElseWithoutIf,
|
||||||
/// An OP_ENDIF happened while not in an OP_IF tree
|
/// An OP_ENDIF happened while not in an OP_IF tree
|
||||||
|
@ -1558,7 +1560,7 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
|
||||||
for _ in 0..input_index {
|
for _ in 0..input_index {
|
||||||
new_outs.push(Default::default())
|
new_outs.push(Default::default())
|
||||||
}
|
}
|
||||||
new_outs.push(tx_copy.output.swap_remove(input_index).unwrap());
|
new_outs.push(tx_copy.output.swap_remove(input_index));
|
||||||
tx_copy.output = new_outs;
|
tx_copy.output = new_outs;
|
||||||
} else {
|
} else {
|
||||||
sighash_single_bug = true;
|
sighash_single_bug = true;
|
||||||
|
@ -1581,7 +1583,10 @@ fn check_signature(sig_slice: &[u8], pk_slice: &[u8], script: Vec<u8>,
|
||||||
serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap()
|
serialize(&Sha256dHash::from_data(&data_to_sign[..])).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
Secp256k1::verify_raw(&signature_hash[..], sig_slice, &pubkey).map_err(Error::Ecdsa)
|
// We can unwrap -- only failure mode is on length, which is fixed to 32
|
||||||
|
let msg = secp256k1::Message::from_slice(&signature_hash[..]).unwrap();
|
||||||
|
|
||||||
|
Secp256k1::verify_raw(&msg, sig_slice, &pubkey).map_err(Error::Ecdsa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macro to translate English stack instructions into Rust code.
|
// Macro to translate English stack instructions into Rust code.
|
||||||
|
@ -1719,70 +1724,7 @@ impl Script {
|
||||||
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
|
pub fn from_vec(v: Vec<u8>) -> Script { Script(v.into_boxed_slice()) }
|
||||||
|
|
||||||
/// The length in bytes of the script
|
/// The length in bytes of the script
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
let &Script(ref raw) = self;
|
|
||||||
raw.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: i64) {
|
|
||||||
// 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::All::OP_TRUE as u8);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We can also special-case zero
|
|
||||||
if data == 0 {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
raw.push(opcodes::All::OP_FALSE as u8);
|
|
||||||
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: i64) {
|
|
||||||
self.push_slice(&build_scriptint(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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::Ordinary::OP_PUSHDATA1 as usize => { raw.push(n as u8); },
|
|
||||||
n if n < 0x100 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
|
|
||||||
raw.push(n as u8);
|
|
||||||
},
|
|
||||||
n if n < 0x10000 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
|
|
||||||
raw.push((n % 0x100) as u8);
|
|
||||||
raw.push((n / 0x100) as u8);
|
|
||||||
},
|
|
||||||
n if n < 0x100000000 => {
|
|
||||||
raw.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
_ => panic!("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: opcodes::All) {
|
|
||||||
let &Script(ref mut raw) = self;
|
|
||||||
raw.push(data as u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trace a script
|
/// Trace a script
|
||||||
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
|
pub fn trace<'a>(&'a self, stack: &mut Vec<MaybeOwned<'a>>,
|
||||||
|
@ -1807,21 +1749,19 @@ impl Script {
|
||||||
input_context: Option<(&Transaction, usize)>,
|
input_context: Option<(&Transaction, usize)>,
|
||||||
mut trace: Option<&mut Vec<TraceIteration>>)
|
mut trace: Option<&mut Vec<TraceIteration>>)
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
let &Script(ref raw) = self;
|
|
||||||
|
|
||||||
let mut codeseparator_index = 0;
|
let mut codeseparator_index = 0;
|
||||||
let mut exec_stack = vec![];
|
let mut exec_stack = vec![];
|
||||||
let mut alt_stack = vec![];
|
let mut alt_stack = vec![];
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut op_count = 0;
|
let mut op_count = 0;
|
||||||
while index < raw.len() {
|
while index < self.0.len() {
|
||||||
let executing = exec_stack.iter().all(|e| *e);
|
let executing = exec_stack.iter().all(|e| *e);
|
||||||
let byte = unsafe { *raw.get(index) };
|
let byte = self.0[index];
|
||||||
// Write out the trace, except the stack which we don't know yet
|
// Write out the trace, except the stack which we don't know yet
|
||||||
match trace {
|
match trace {
|
||||||
Some(ref mut t) => {
|
Some(ref mut t) => {
|
||||||
let opcode = opcodes::All::Opcode::from_u8(byte);
|
let opcode = opcodes::All::from_u8(byte);
|
||||||
t.push(TraceIteration {
|
t.push(TraceIteration {
|
||||||
index: index,
|
index: index,
|
||||||
opcode: opcode,
|
opcode: opcode,
|
||||||
|
@ -1837,7 +1777,7 @@ impl Script {
|
||||||
op_count += 1;
|
op_count += 1;
|
||||||
index += 1;
|
index += 1;
|
||||||
// The definitions of all these categories are in opcodes.rs
|
// The definitions of all these categories are in opcodes.rs
|
||||||
match (executing, opcodes::All::Opcode::from_u8(byte).classify()) {
|
match (executing, opcodes::All::from_u8(byte).classify()) {
|
||||||
// Illegal operations mean failure regardless of execution state
|
// Illegal operations mean failure regardless of execution state
|
||||||
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
|
(_, opcodes::Class::IllegalOp) => return Err(Error::IllegalOpcode),
|
||||||
// Push number
|
// Push number
|
||||||
|
@ -1846,29 +1786,30 @@ impl Script {
|
||||||
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
||||||
// Data-reading statements still need to read, even when not executing
|
// Data-reading statements still need to read, even when not executing
|
||||||
(_, opcodes::Class::PushBytes(n)) => {
|
(_, opcodes::Class::PushBytes(n)) => {
|
||||||
if raw.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
let n = n as usize;
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index, index + n))); }
|
if self.0.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||||
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index..index + n])); }
|
||||||
index += n;
|
index += n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1)) => {
|
||||||
if raw.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 1 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 1));
|
let n = try!(read_uint(&self.0[index..], 1));
|
||||||
if raw.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 1, index + n + 1))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 1..index + n + 1])); }
|
||||||
index += 1 + n;
|
index += 1 + n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2)) => {
|
||||||
if raw.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 2 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 2));
|
let n = try!(read_uint(&self.0[index..], 2));
|
||||||
if raw.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 2, index + n + 2))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 2..index + n + 2])); }
|
||||||
index += 2 + n;
|
index += 2 + n;
|
||||||
}
|
}
|
||||||
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
|
(_, opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4)) => {
|
||||||
if raw.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 4 { return Err(Error::EarlyEndOfScript); }
|
||||||
let n = try!(read_uint(&raw[index..], 4));
|
let n = try!(read_uint(&self.0[index..], 4));
|
||||||
if raw.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
if self.0.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing { stack.push(MaybeOwned::Borrowed(raw.slice(index + 4, index + n + 4))); }
|
if executing { stack.push(MaybeOwned::Borrowed(&self.0[index + 4..index + n + 4])); }
|
||||||
index += 4 + n;
|
index += 4 + n;
|
||||||
}
|
}
|
||||||
// If-statements take effect when not executing
|
// If-statements take effect when not executing
|
||||||
|
@ -1942,7 +1883,7 @@ impl Script {
|
||||||
opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2),
|
opcodes::Ordinary::OP_OVER => stack_opcode!(stack(2): copy 2),
|
||||||
opcodes::Ordinary::OP_PICK => {
|
opcodes::Ordinary::OP_PICK => {
|
||||||
let n = match stack.pop() {
|
let n = match stack.pop() {
|
||||||
Some(data) => try!(read_scriptint(&data)),
|
Some(data) => try!(read_scriptint(&data[..])),
|
||||||
None => { return Err(Error::PopEmptyStack); }
|
None => { return Err(Error::PopEmptyStack); }
|
||||||
};
|
};
|
||||||
if n < 0 { return Err(Error::NegativePick); }
|
if n < 0 { return Err(Error::NegativePick); }
|
||||||
|
@ -1951,7 +1892,7 @@ impl Script {
|
||||||
}
|
}
|
||||||
opcodes::Ordinary::OP_ROLL => {
|
opcodes::Ordinary::OP_ROLL => {
|
||||||
let n = match stack.pop() {
|
let n = match stack.pop() {
|
||||||
Some(data) => try!(read_scriptint(&data)),
|
Some(data) => try!(read_scriptint(&data[..])),
|
||||||
None => { return Err(Error::PopEmptyStack); }
|
None => { return Err(Error::PopEmptyStack); }
|
||||||
};
|
};
|
||||||
if n < 0 { return Err(Error::NegativeRoll); }
|
if n < 0 { return Err(Error::NegativeRoll); }
|
||||||
|
@ -2033,11 +1974,12 @@ impl Script {
|
||||||
|
|
||||||
// Compute the section of script that needs to be hashed: everything
|
// Compute the section of script that needs to be hashed: everything
|
||||||
// from the last CODESEPARATOR, except the signature itself.
|
// from the last CODESEPARATOR, except the signature itself.
|
||||||
let mut script = (&raw[codeseparator_index..]).to_vec();
|
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||||
let mut remove = Script::new();
|
let mut remove = ScriptBuilder::new();
|
||||||
remove.push_slice(sig_slice);
|
remove.push_slice(sig_slice);
|
||||||
script_find_and_remove(&mut script, &remove);
|
script_find_and_remove(&mut script, &remove[..]);
|
||||||
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
// Also all of the OP_CODESEPARATORS, even the unevaluated ones
|
||||||
|
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||||
|
|
||||||
// This is as far as we can go without a transaction, so fail here
|
// This is as far as we can go without a transaction, so fail here
|
||||||
if input_context.is_none() { return Err(Error::NoTransaction); }
|
if input_context.is_none() { return Err(Error::NoTransaction); }
|
||||||
|
@ -2053,7 +1995,7 @@ impl Script {
|
||||||
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
|
opcodes::Ordinary::OP_CHECKMULTISIG | opcodes::Ordinary::OP_CHECKMULTISIGVERIFY => {
|
||||||
// Read all the keys
|
// Read all the keys
|
||||||
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
||||||
let n_keys = try!(read_scriptint(&stack.pop().unwrap()));
|
let n_keys = try!(read_scriptint(&stack.pop().unwrap()[..]));
|
||||||
if n_keys < 0 || n_keys > 20 {
|
if n_keys < 0 || n_keys > 20 {
|
||||||
return Err(Error::MultisigBadKeyCount(n_keys as isize));
|
return Err(Error::MultisigBadKeyCount(n_keys as isize));
|
||||||
}
|
}
|
||||||
|
@ -2066,7 +2008,7 @@ impl Script {
|
||||||
|
|
||||||
// Read all the signatures
|
// Read all the signatures
|
||||||
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
if stack.len() < 1 { return Err(Error::PopEmptyStack); }
|
||||||
let n_sigs = try!(read_scriptint(&stack.pop().unwrap()));
|
let n_sigs = try!(read_scriptint(&stack.pop().unwrap()[..]));
|
||||||
if n_sigs < 0 || n_sigs > n_keys {
|
if n_sigs < 0 || n_sigs > n_keys {
|
||||||
return Err(Error::MultisigBadSigCount(n_sigs as isize));
|
return Err(Error::MultisigBadSigCount(n_sigs as isize));
|
||||||
}
|
}
|
||||||
|
@ -2082,12 +2024,12 @@ impl Script {
|
||||||
|
|
||||||
// Compute the section of script that needs to be hashed: everything
|
// Compute the section of script that needs to be hashed: everything
|
||||||
// from the last CODESEPARATOR, except the signatures themselves.
|
// from the last CODESEPARATOR, except the signatures themselves.
|
||||||
let mut script = (&raw[codeseparator_index..]).to_vec();
|
let mut script = (&self.0[codeseparator_index..]).to_vec();
|
||||||
for sig in sigs.iter() {
|
for sig in sigs.iter() {
|
||||||
let mut remove = Script::new();
|
let mut remove = ScriptBuilder::new();
|
||||||
remove.push_slice(&sig);
|
remove.push_slice(&sig[..]);
|
||||||
script_find_and_remove(&mut script, &remove);
|
script_find_and_remove(&mut script, &remove[..]);
|
||||||
script_find_and_remove(&mut script, [opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
script_find_and_remove(&mut script, &[opcodes::Ordinary::OP_CODESEPARATOR as u8]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is as far as we can go without a transaction, so fail here
|
// This is as far as we can go without a transaction, so fail here
|
||||||
|
@ -2105,7 +2047,7 @@ impl Script {
|
||||||
// Try to validate the signature with the given key
|
// Try to validate the signature with the given key
|
||||||
(Some(k), Some(s)) => {
|
(Some(k), Some(s)) => {
|
||||||
// Move to the next signature if it is valid for the current key
|
// Move to the next signature if it is valid for the current key
|
||||||
if check_signature(&s, &k, script.clone(), tx, input_index).is_ok() {
|
if check_signature(&s[..], &k[..], script.clone(), tx, input_index).is_ok() {
|
||||||
sig = sig_iter.next();
|
sig = sig_iter.next();
|
||||||
}
|
}
|
||||||
// Move to the next key in any case
|
// Move to the next key in any case
|
||||||
|
@ -2142,12 +2084,11 @@ impl Script {
|
||||||
/// Checks whether a script pubkey is a p2sh output
|
/// Checks whether a script pubkey is a p2sh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_p2sh(&self) -> bool {
|
pub fn is_p2sh(&self) -> bool {
|
||||||
let &Script(ref raw) = self;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
raw.len() == 23 &&
|
self.0.len() == 23 &&
|
||||||
*raw.get(0) == opcodes::All::OP_HASH160 as u8 &&
|
self.0[0] == opcodes::All::OP_HASH160 as u8 &&
|
||||||
*raw.get(1) == opcodes::All::OP_PUSHBYTES_20 as u8 &&
|
self.0[1] == opcodes::All::OP_PUSHBYTES_20 as u8 &&
|
||||||
*raw.get(22) == opcodes::All::OP_EQUAL as u8
|
self.0[22] == opcodes::All::OP_EQUAL as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2186,9 +2127,10 @@ impl Script {
|
||||||
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
(true, opcodes::Class::ReturnOp) => return Err(Error::ExecutedReturn),
|
||||||
// Data-reading statements still need to read, even when not executing
|
// Data-reading statements still need to read, even when not executing
|
||||||
(_, opcodes::Class::PushBytes(n)) => {
|
(_, opcodes::Class::PushBytes(n)) => {
|
||||||
|
let n = n as usize;
|
||||||
if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index, index + n)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index..index + n]));
|
||||||
}
|
}
|
||||||
index += n;
|
index += n;
|
||||||
}
|
}
|
||||||
|
@ -2200,7 +2142,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 1 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 1, index + n + 1)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 1..index + n + 1]));
|
||||||
}
|
}
|
||||||
index += 1 + n;
|
index += 1 + n;
|
||||||
}
|
}
|
||||||
|
@ -2212,7 +2154,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 2 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 2, index + n + 2)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 2..index + n + 2]));
|
||||||
}
|
}
|
||||||
index += 2 + n;
|
index += 2 + n;
|
||||||
}
|
}
|
||||||
|
@ -2223,7 +2165,7 @@ impl Script {
|
||||||
};
|
};
|
||||||
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
if script.len() < index + 4 + n { return Err(Error::EarlyEndOfScript); }
|
||||||
if executing {
|
if executing {
|
||||||
stack.push_alloc(AbstractStackElem::new_raw(script.slice(index + 4, index + n + 4)));
|
stack.push_alloc(AbstractStackElem::new_raw(&script[index + 4..index + n + 4]));
|
||||||
}
|
}
|
||||||
index += 4 + n;
|
index += 4 + n;
|
||||||
}
|
}
|
||||||
|
@ -2495,8 +2437,7 @@ impl Script {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let &Script(ref raw) = self;
|
recurse(&self.0, AbstractStack::new(), vec![], 1)
|
||||||
recurse(&raw, AbstractStack::new(), vec![], 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2504,62 +2445,93 @@ impl Default for Script {
|
||||||
fn default() -> Script { Script(vec![].into_boxed_slice()) }
|
fn default() -> Script { Script(vec![].into_boxed_slice()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_index_newtype!(Script, u8);
|
||||||
|
|
||||||
impl ops::Index<usize> for Script {
|
impl ScriptBuilder {
|
||||||
type Output = u8;
|
/// Creates a new empty script
|
||||||
#[inline]
|
pub fn new() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||||
fn index(&self, index: usize) -> &u8 {
|
|
||||||
let &Script(ref raw) = self;
|
/// Creates a new script from an existing vector
|
||||||
&raw[index]
|
pub fn from_vec(v: Vec<u8>) -> ScriptBuilder { ScriptBuilder(v) }
|
||||||
|
|
||||||
|
/// The length in bytes of the script
|
||||||
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
|
|
||||||
|
/// 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: i64) {
|
||||||
|
// We can special-case -1, 1-16
|
||||||
|
if data == -1 || (data >= 1 && data <=16) {
|
||||||
|
self.0.push(data as u8 + opcodes::OP_TRUE as u8);
|
||||||
|
}
|
||||||
|
// We can also special-case zero
|
||||||
|
else if data == 0 {
|
||||||
|
self.0.push(opcodes::OP_FALSE as u8);
|
||||||
|
}
|
||||||
|
// Otherwise encode it as data
|
||||||
|
else { 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: i64) {
|
||||||
|
self.push_slice(&build_scriptint(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds instructions to push some arbitrary data onto the stack
|
||||||
|
pub fn push_slice(&mut self, data: &[u8]) {
|
||||||
|
// Start with a PUSH opcode
|
||||||
|
match data.len() {
|
||||||
|
n if n < opcodes::Ordinary::OP_PUSHDATA1 as usize => { self.0.push(n as u8); },
|
||||||
|
n if n < 0x100 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA1 as u8);
|
||||||
|
self.0.push(n as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x10000 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA2 as u8);
|
||||||
|
self.0.push((n % 0x100) as u8);
|
||||||
|
self.0.push((n / 0x100) as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x100000000 => {
|
||||||
|
self.0.push(opcodes::Ordinary::OP_PUSHDATA4 as u8);
|
||||||
|
self.0.push((n % 0x100) as u8);
|
||||||
|
self.0.push(((n / 0x100) % 0x100) as u8);
|
||||||
|
self.0.push(((n / 0x10000) % 0x100) as u8);
|
||||||
|
self.0.push((n / 0x1000000) as u8);
|
||||||
|
}
|
||||||
|
_ => panic!("tried to put a 4bn+ sized object into a script!")
|
||||||
|
}
|
||||||
|
// Then push the acraw
|
||||||
|
self.0.extend(data.iter().map(|n| *n));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_opcode(&mut self, data: opcodes::All) {
|
||||||
|
self.0.push(data as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_script(self) -> Script {
|
||||||
|
Script(self.0.into_boxed_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::Range<usize>> for Script {
|
/// Adds an individual opcode to the script
|
||||||
type Output = [u8];
|
impl Default for ScriptBuilder {
|
||||||
#[inline]
|
fn default() -> ScriptBuilder { ScriptBuilder(vec![]) }
|
||||||
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Index<ops::RangeTo<usize>> for Script {
|
impl_index_newtype!(ScriptBuilder, u8);
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ops::RangeTo<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFrom<usize>> for Script {
|
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFull> for Script {
|
|
||||||
type Output = [u8];
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
|
||||||
let &Script(ref raw) = self;
|
|
||||||
&raw[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User-facing serialization
|
// User-facing serialization
|
||||||
impl serde::Serialize for Script {
|
impl serde::Serialize for Script {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
where S: serde::Serializer,
|
where S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let &Script(ref raw) = self;
|
for dat in self.0.iter() {
|
||||||
for dat in raw.iter() {
|
try!(serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap()));
|
||||||
serializer.visit_char(from_digit((dat / 0x10) as u32, 16).unwrap());
|
try!(serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap()));
|
||||||
serializer.visit_char(from_digit((dat & 0x0f) as u32, 16).unwrap());
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2567,8 +2539,7 @@ impl serde::Serialize for Script {
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Script {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Script {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
let &Script(ref data) = self;
|
self.0.consensus_encode(s)
|
||||||
data.consensus_encode(s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ use blockdata::utxoset::UtxoSet;
|
||||||
use network::encodable::ConsensusEncodable;
|
use network::encodable::ConsensusEncodable;
|
||||||
use network::serialize::BitcoinHash;
|
use network::serialize::BitcoinHash;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use wallet::address::Address;
|
use wallet::address::{Address, ToAddress};
|
||||||
|
|
||||||
/// A transaction input, which defines old coins to be consumed
|
/// A transaction input, which defines old coins to be consumed
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
@ -79,9 +79,9 @@ impl TxOut {
|
||||||
/// Determines the template that this output adheres to, if any
|
/// Determines the template that this output adheres to, if any
|
||||||
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
pub fn classify(&self, network: Network) -> ScriptPubkeyTemplate {
|
||||||
if self.script_pubkey.len() == 25 &&
|
if self.script_pubkey.len() == 25 &&
|
||||||
self.script_pubkey.slice_to(3) == &[0x76, 0xa9, 0x14] &&
|
&self.script_pubkey[0..3] == &[0x76, 0xa9, 0x14] &&
|
||||||
self.script_pubkey.slice_from(23) == &[0x88, 0xac] {
|
&self.script_pubkey[23..] == &[0x88, 0xac] {
|
||||||
ScriptPubkeyTemplate::PayToPubkeyHash(self.script_pubkey.slice(3, 23).to_address(network))
|
ScriptPubkeyTemplate::PayToPubkeyHash((&self.script_pubkey[3..23]).to_address(network))
|
||||||
} else {
|
} else {
|
||||||
ScriptPubkeyTemplate::Unknown
|
ScriptPubkeyTemplate::Unknown
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,7 @@ pub enum Error {
|
||||||
/// Script ended with nothing in the stack (input txid, input vout)
|
/// Script ended with nothing in the stack (input txid, input vout)
|
||||||
InputNotFound(Sha256dHash, u32),
|
InputNotFound(Sha256dHash, u32),
|
||||||
}
|
}
|
||||||
|
display_from_debug!(Error);
|
||||||
|
|
||||||
impl serde::Serialize for Error {
|
impl serde::Serialize for Error {
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash::map::Iter;
|
use std::collections::hash::map::Iter;
|
||||||
use std::hash::SipHasher;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use eventual;
|
||||||
|
use eventual::Async;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use std::sync::Future;
|
|
||||||
|
|
||||||
use blockdata::transaction::{self, Transaction, TxOut};
|
use blockdata::transaction::{self, Transaction, TxOut};
|
||||||
use blockdata::constants::genesis_block;
|
use blockdata::constants::genesis_block;
|
||||||
|
@ -78,15 +78,12 @@ impl<'a> Iterator for UtxoIterator<'a> {
|
||||||
type Item = (Sha256dHash, u32, &'a TxOut, u32);
|
type Item = (Sha256dHash, u32, &'a TxOut, u32);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
|
fn next(&mut self) -> Option<(Sha256dHash, u32, &'a TxOut, u32)> {
|
||||||
while self.current.is_some() {
|
while let Some(current) = self.current {
|
||||||
let current = &self.current.unwrap().outputs;
|
while self.tx_index < current.outputs.len() as u32 {
|
||||||
while self.tx_index < current.len() as u32 {
|
|
||||||
self.tx_index += 1;
|
self.tx_index += 1;
|
||||||
if unsafe { current.get(self.tx_index as usize - 1) }.is_some() {
|
if let Some(ref cur) = current.outputs[self.tx_index as usize - 1] {
|
||||||
return Some((self.current_key,
|
return Some((self.current_key, self.tx_index,
|
||||||
self.tx_index,
|
cur, current.height));
|
||||||
unsafe { current.get(self.tx_index as usize - 1) }.as_ref().unwrap(),
|
|
||||||
self.current.unwrap().height));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match self.tx_iter.next() {
|
match self.tx_iter.next() {
|
||||||
|
@ -104,7 +101,7 @@ impl<'a> Iterator for UtxoIterator<'a> {
|
||||||
|
|
||||||
/// The UTXO set
|
/// The UTXO set
|
||||||
pub struct UtxoSet {
|
pub struct UtxoSet {
|
||||||
table: HashMap<Sha256dHash, UtxoNode, SipHasher>,
|
table: HashMap<Sha256dHash, UtxoNode>,
|
||||||
last_hash: Sha256dHash,
|
last_hash: Sha256dHash,
|
||||||
// A circular buffer of deleted utxos, grouped by block
|
// A circular buffer of deleted utxos, grouped by block
|
||||||
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
spent_txos: Vec<Vec<((Sha256dHash, u32), (u32, TxOut))>>,
|
||||||
|
@ -124,9 +121,9 @@ impl UtxoSet {
|
||||||
// must follow suit, otherwise we will accept a transaction spending it
|
// must follow suit, otherwise we will accept a transaction spending it
|
||||||
// while the reference client won't, causing us to fork off the network.
|
// while the reference client won't, causing us to fork off the network.
|
||||||
UtxoSet {
|
UtxoSet {
|
||||||
table: HashMap::with_hasher(SipHasher::new()),
|
table: HashMap::new(),
|
||||||
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![vec![]; rewind_limit],
|
||||||
spent_idx: 0,
|
spent_idx: 0,
|
||||||
n_utxos: 0,
|
n_utxos: 0,
|
||||||
n_pruned: 0
|
n_pruned: 0
|
||||||
|
@ -153,7 +150,7 @@ impl UtxoSet {
|
||||||
};
|
};
|
||||||
// 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
|
||||||
// and the other comments in this file referring to it)
|
// and the other comments in this file referring to it)
|
||||||
let ret = self.table.swap(txid, new_node.into_boxed_slice());
|
let ret = self.table.insert(txid, new_node);
|
||||||
if ret.is_none() {
|
if ret.is_none() {
|
||||||
self.n_utxos += tx.output.len() as u64;
|
self.n_utxos += tx.output.len() as u64;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +162,7 @@ impl UtxoSet {
|
||||||
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
||||||
let (height, ret, should_delete) = {
|
let (height, ret, should_delete) = {
|
||||||
// Locate the UTXO, failing if not found
|
// Locate the UTXO, failing if not found
|
||||||
let node = match self.table.find_mut(&txid) {
|
let node = match self.table.get_mut(&txid) {
|
||||||
Some(node) => node,
|
Some(node) => node,
|
||||||
None => return None
|
None => return None
|
||||||
};
|
};
|
||||||
|
@ -173,7 +170,7 @@ impl UtxoSet {
|
||||||
let ret = {
|
let ret = {
|
||||||
// Check that this specific output is there
|
// Check that this specific output is there
|
||||||
if vout as usize >= node.outputs.len() { return None; }
|
if vout as usize >= node.outputs.len() { return None; }
|
||||||
let replace = unsafe { node.outputs.get_mut(vout as usize) };
|
let replace = &mut node.outputs[vout as usize];
|
||||||
replace.take()
|
replace.take()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,13 +190,13 @@ impl UtxoSet {
|
||||||
/// Get a reference to a UTXO in the set
|
/// Get a reference to a UTXO in the set
|
||||||
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> {
|
pub fn get_utxo<'a>(&'a self, txid: Sha256dHash, vout: u32) -> Option<(usize, &'a TxOut)> {
|
||||||
// Locate the UTXO, failing if not found
|
// Locate the UTXO, failing if not found
|
||||||
let node = match self.table.find(&txid) {
|
let node = match self.table.get(&txid) {
|
||||||
Some(node) => node,
|
Some(node) => node,
|
||||||
None => return None
|
None => return None
|
||||||
};
|
};
|
||||||
// Check that this specific output is there
|
// Check that this specific output is there
|
||||||
if vout as usize >= node.outputs.len() { return None; }
|
if vout as usize >= node.outputs.len() { return None; }
|
||||||
let replace = unsafe { node.outputs.get(vout as usize) };
|
let replace = node.outputs[vout as usize];
|
||||||
Some((node.height as usize, replace.as_ref().unwrap()))
|
Some((node.height as usize, replace.as_ref().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +214,7 @@ impl UtxoSet {
|
||||||
self.last_hash = block.header.bitcoin_hash();
|
self.last_hash = block.header.bitcoin_hash();
|
||||||
let spent_idx = self.spent_idx as usize;
|
let spent_idx = self.spent_idx as usize;
|
||||||
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
|
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
|
||||||
self.spent_txos.get_mut(spent_idx).clear();
|
(&mut self.spent_txos[spent_idx]).clear();
|
||||||
|
|
||||||
// Add all the utxos so that we can have chained transactions within the
|
// Add all the utxos so that we can have chained transactions within the
|
||||||
// same block. (Note that Bitcoin requires chained transactions to be in
|
// same block. (Note that Bitcoin requires chained transactions to be in
|
||||||
|
@ -240,10 +237,10 @@ impl UtxoSet {
|
||||||
} else {
|
} else {
|
||||||
// Otherwise put the replaced txouts into the `deleted` cache
|
// Otherwise put the replaced txouts into the `deleted` cache
|
||||||
// so that rewind will put them back.
|
// so that rewind will put them back.
|
||||||
self.spent_txos.get_mut(spent_idx).reserve_additional(replace.outputs.len());
|
(&mut self.spent_txos[spent_idx]).reserve(replace.outputs.len());
|
||||||
for (n, input) in replace.outputs.iter_mut().enumerate() {
|
for (n, input) in replace.outputs.iter_mut().enumerate() {
|
||||||
match input.take() {
|
match input.take() {
|
||||||
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), (replace.height, txo))); }
|
Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), (replace.height, txo))); }
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,12 +265,14 @@ impl UtxoSet {
|
||||||
let start = 1 + j * n_elems / n_threads;
|
let start = 1 + j * n_elems / n_threads;
|
||||||
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
|
let end = cmp::min(n_elems, 1 + (j + 1) * n_elems / n_threads);
|
||||||
|
|
||||||
let s = self as *mut _ as *const UtxoSet;
|
// WARNING: we are asserting that these variables will outlive the Futures;
|
||||||
let txes = &block.txdata as *const _;
|
// this means that we need to await all Futures before leaving the
|
||||||
future_vec.push(Future::spawn(move || {
|
// function or else risk use-after-free in the async threads.
|
||||||
let txes = unsafe {&*txes};
|
let static_txes = unsafe { &*(&block.txdata as *const Vec<Transaction>) };
|
||||||
for tx in txes.slice(start, end).iter() {
|
let static_self = unsafe { &*(self as *const UtxoSet) };
|
||||||
match tx.validate(unsafe {&*s}) {
|
future_vec.push(eventual::Future::spawn(move || {
|
||||||
|
for tx in static_txes[start..end].iter() {
|
||||||
|
match tx.validate(static_self) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
|
Err(e) => { return Err(Error::InvalidTx(tx.bitcoin_hash(), e)); }
|
||||||
}
|
}
|
||||||
|
@ -283,25 +282,21 @@ impl UtxoSet {
|
||||||
}
|
}
|
||||||
// Return the last error since we need to finish every future before
|
// Return the last error since we need to finish every future before
|
||||||
// leaving this function, and given that, it's easier to return the last.
|
// leaving this function, and given that, it's easier to return the last.
|
||||||
let mut last_error = Ok(());
|
let mut last_err = Ok(());
|
||||||
for res in future_vec.iter_mut().map(|f| f.get()) {
|
for res in future_vec.iter_mut().map(|f| f.await().unwrap()) {
|
||||||
if res.is_err() {
|
if res.is_err() { last_err = res; }
|
||||||
last_error = res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if last_error.is_err() {
|
|
||||||
return last_error;
|
|
||||||
}
|
}
|
||||||
|
if last_err.is_err() { return last_err; }
|
||||||
}
|
}
|
||||||
|
|
||||||
for tx in block.txdata.iter().skip(1) {
|
for tx in block.txdata.iter().skip(1) {
|
||||||
let txid = tx.bitcoin_hash();
|
let txid = tx.bitcoin_hash();
|
||||||
// Put the removed utxos into the stxo cache, in case we need to rewind
|
// Put the removed utxos into the stxo cache, in case we need to rewind
|
||||||
self.spent_txos.get_mut(spent_idx).reserve_additional(tx.input.len());
|
(&self.spent_txos[spent_idx]).reserve(tx.input.len());
|
||||||
for (n, input) in tx.input.iter().enumerate() {
|
for (n, input) in tx.input.iter().enumerate() {
|
||||||
let taken = self.take_utxo(input.prev_hash, input.prev_index);
|
let taken = self.take_utxo(input.prev_hash, input.prev_index);
|
||||||
match taken {
|
match taken {
|
||||||
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(((txid, n as u32), txo)); }
|
Some(txo) => { (&mut self.spent_txos[spent_idx]).push(((txid, n as u32), txo)); }
|
||||||
None => {
|
None => {
|
||||||
if validation >= ValidationLevel::Inputs {
|
if validation >= ValidationLevel::Inputs {
|
||||||
self.rewind(block);
|
self.rewind(block);
|
||||||
|
@ -349,18 +344,17 @@ impl UtxoSet {
|
||||||
// Read deleted txouts
|
// Read deleted txouts
|
||||||
if skipped_genesis {
|
if skipped_genesis {
|
||||||
let mut extract_vec = vec![];
|
let mut extract_vec = vec![];
|
||||||
mem::swap(&mut extract_vec, self.spent_txos.get_mut(self.spent_idx as usize));
|
mem::swap(&mut extract_vec, (&mut self.spent_txos[self.spent_idx as usize]));
|
||||||
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
|
for ((txid, n), (height, txo)) in extract_vec.into_iter() {
|
||||||
// Remove the tx's utxo list and patch the txo into place
|
// Remove the tx's utxo list and patch the txo into place
|
||||||
let new_node =
|
let new_node = match self.table.remove(&txid) {
|
||||||
match self.table.pop(&txid) {
|
|
||||||
Some(mut node) => {
|
Some(mut node) => {
|
||||||
node.outputs[n as usize] = Some(txo);
|
node.outputs[n as usize] = Some(txo);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut thinvec = Vec::with_capacity(n + 1);
|
let mut thinvec = Vec::with_capacity(n as usize + 1);
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
thinvec.push(None);
|
thinvec.push(None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,45 +99,7 @@ macro_rules! impl_array_newtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
|
impl_index_newtype!($thing, $ty);
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
|
|
||||||
type Output = [$ty];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
|
|
||||||
let &$thing(ref dat) = self;
|
|
||||||
&dat[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for $thing {
|
impl PartialEq for $thing {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -234,6 +196,47 @@ macro_rules! impl_array_newtype_show {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_index_newtype {
|
||||||
|
($thing:ident, $ty:ty) => {
|
||||||
|
impl ::std::ops::Index<::std::ops::Range<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::Range<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::RangeTo<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ::std::ops::RangeFrom<usize>) -> &[$ty] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<::std::ops::RangeFull> for $thing {
|
||||||
|
type Output = [$ty];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! display_from_debug {
|
macro_rules! display_from_debug {
|
||||||
($thing:ident) => {
|
($thing:ident) => {
|
||||||
impl ::std::fmt::Display for $thing {
|
impl ::std::fmt::Display for $thing {
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate collections;
|
extern crate collections;
|
||||||
|
extern crate eventual;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate rustc_serialize as serialize;
|
extern crate rustc_serialize as serialize;
|
||||||
|
|
|
@ -34,7 +34,6 @@ macro_rules! nu_select {
|
||||||
}
|
}
|
||||||
)else+
|
)else+
|
||||||
else {
|
else {
|
||||||
|
|
||||||
// Start selecting on as many as we need to before getting a bite.
|
// Start selecting on as many as we need to before getting a bite.
|
||||||
// Keep count of how many, since we need to abort every selection
|
// Keep count of how many, since we need to abort every selection
|
||||||
// that we started.
|
// that we started.
|
||||||
|
|
|
@ -59,7 +59,7 @@ static BASE58_DIGITS: [Option<u8>; 128] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Trait for objects which can be read as base58
|
/// Trait for objects which can be read as base58
|
||||||
pub trait FromBase58 {
|
pub trait FromBase58: Sized {
|
||||||
/// Constructs an object flrom the byte-encoding (base 256)
|
/// Constructs an object flrom the byte-encoding (base 256)
|
||||||
/// representation of its base58 format
|
/// representation of its base58 format
|
||||||
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>;
|
||||||
|
@ -67,7 +67,7 @@ pub trait FromBase58 {
|
||||||
/// Obtain an object from its base58 encoding
|
/// Obtain an object from its base58 encoding
|
||||||
fn from_base58(data: &str) -> Result<Self, Error> {
|
fn from_base58(data: &str) -> Result<Self, Error> {
|
||||||
// 11/15 is just over log_256(58)
|
// 11/15 is just over log_256(58)
|
||||||
let mut scratch = Vec::from_elem(1 + data.len() * 11 / 15, 0u8);
|
let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
|
||||||
// Build in base 256
|
// Build in base 256
|
||||||
for d58 in data.bytes() {
|
for d58 in data.bytes() {
|
||||||
// Compute "X = X * 58 + next_digit" in base 256
|
// Compute "X = X * 58 + next_digit" in base 256
|
||||||
|
@ -132,13 +132,13 @@ pub fn base58_encode_slice(data: &[u8]) -> String {
|
||||||
// Unsafely translate the bytes to a utf8 string
|
// Unsafely translate the bytes to a utf8 string
|
||||||
unsafe {
|
unsafe {
|
||||||
// Copy leading zeroes directly
|
// Copy leading zeroes directly
|
||||||
let mut ret: Vec<u8> = str::from_utf8(data.iter().take_while(|&&x| x == 0)
|
let mut ret: Vec<u8> = data.iter().take_while(|&&x| x == 0)
|
||||||
.map(|_| BASE58_CHARS[0])
|
.map(|_| BASE58_CHARS[0])
|
||||||
.collect()).unwrap();
|
.collect();
|
||||||
// Copy rest of string
|
// Copy rest of string
|
||||||
ret.as_mut_vec().extend(scratch.into_iter().skip_while(|&x| x == 0)
|
ret.extend(scratch.into_iter().skip_while(|&x| x == 0)
|
||||||
.map(|x| BASE58_CHARS[x as usize]));
|
.map(|x| BASE58_CHARS[x as usize]));
|
||||||
ret
|
String::from_utf8(ret).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,7 +247,7 @@ impl<'a, T: BitcoinHash> MerkleRoot for &'a [T] {
|
||||||
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
let mut encoder = RawEncoder::new(Cursor::new(vec![]));
|
||||||
data[idx1].consensus_encode(&mut encoder).unwrap();
|
data[idx1].consensus_encode(&mut encoder).unwrap();
|
||||||
data[idx2].consensus_encode(&mut encoder).unwrap();
|
data[idx2].consensus_encode(&mut encoder).unwrap();
|
||||||
next.push(encoder.unwrap().into_inner().bitcoin_hash());
|
next.push(encoder.into_inner().into_inner().bitcoin_hash());
|
||||||
}
|
}
|
||||||
merkle_root(next)
|
merkle_root(next)
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
|
||||||
if overflow { break; }
|
if overflow { break; }
|
||||||
} else {
|
} else {
|
||||||
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
i += match opcodes::All::from_u8((*haystack)[i]).classify() {
|
||||||
opcodes::Class::PushBytes(n) => n + 1,
|
opcodes::Class::PushBytes(n) => n as usize + 1,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
|
||||||
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::{Script, ScriptBuilder};
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::hash::Ripemd160Hash;
|
use util::hash::Ripemd160Hash;
|
||||||
|
@ -53,13 +53,13 @@ impl Address {
|
||||||
/// Generates a script pubkey spending to this address
|
/// Generates a script pubkey spending to this address
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn script_pubkey(&self) -> Script {
|
pub fn script_pubkey(&self) -> Script {
|
||||||
let mut script = Script::new();
|
let mut script = ScriptBuilder::new();
|
||||||
script.push_opcode(opcodes::All::OP_DUP);
|
script.push_opcode(opcodes::All::OP_DUP);
|
||||||
script.push_opcode(opcodes::All::OP_HASH160);
|
script.push_opcode(opcodes::All::OP_HASH160);
|
||||||
script.push_slice(&self.hash[..]);
|
script.push_slice(&self.hash[..]);
|
||||||
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
script.push_opcode(opcodes::All::OP_EQUALVERIFY);
|
||||||
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
script.push_opcode(opcodes::All::OP_CHECKSIG);
|
||||||
script
|
script.into_script()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue