Merge remote-tracking branch 'parity/master' into bft

This commit is contained in:
keorn
2016-08-31 18:19:24 +02:00
59 changed files with 1920 additions and 750 deletions

View File

@@ -245,11 +245,11 @@ impl<'x> OpenBlock<'x> {
last_hashes: last_hashes,
};
r.block.base.header.parent_hash = parent.hash();
r.block.base.header.number = parent.number + 1;
r.block.base.header.author = author;
r.block.base.header.set_parent_hash(parent.hash());
r.block.base.header.set_number(parent.number() + 1);
r.block.base.header.set_author(author);
r.block.base.header.set_timestamp_now(parent.timestamp());
r.block.base.header.extra_data = extra_data;
r.block.base.header.set_extra_data(extra_data);
r.block.base.header.note_dirty();
engine.populate_from_parent(&mut r.block.base.header, parent, gas_range_target.0, gas_range_target.1);
@@ -309,13 +309,13 @@ impl<'x> OpenBlock<'x> {
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
EnvInfo {
number: self.block.base.header.number,
author: self.block.base.header.author.clone(),
timestamp: self.block.base.header.timestamp,
difficulty: self.block.base.header.difficulty.clone(),
number: self.block.base.header.number(),
author: self.block.base.header.author().clone(),
timestamp: self.block.base.header.timestamp(),
difficulty: self.block.base.header.difficulty().clone(),
last_hashes: self.last_hashes.clone(),
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(),
gas_limit: self.block.base.header.gas_limit().clone(),
}
}
@@ -349,14 +349,13 @@ impl<'x> OpenBlock<'x> {
let unclosed_state = s.block.state.clone();
s.engine.on_close_block(&mut s.block);
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect());
s.block.base.header.set_transactions_root(ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect()));
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
s.block.base.header.uncles_hash = uncle_bytes.sha3();
s.block.base.header.state_root = s.block.state.root().clone();
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect());
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
s.block.base.header.note_dirty();
s.block.base.header.set_uncles_hash(uncle_bytes.sha3());
s.block.base.header.set_state_root(s.block.state.root().clone());
s.block.base.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect()));
s.block.base.header.set_log_bloom(s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator
s.block.base.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used));
ClosedBlock {
block: s.block,
@@ -371,20 +370,19 @@ impl<'x> OpenBlock<'x> {
let mut s = self;
s.engine.on_close_block(&mut s.block);
if s.block.base.header.transactions_root.is_zero() || s.block.base.header.transactions_root == SHA3_NULL_RLP {
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect());
if s.block.base.header.transactions_root().is_zero() || s.block.base.header.transactions_root() == &SHA3_NULL_RLP {
s.block.base.header.set_transactions_root(ordered_trie_root(s.block.base.transactions.iter().map(|e| e.rlp_bytes().to_vec()).collect()));
}
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
if s.block.base.header.uncles_hash.is_zero() {
s.block.base.header.uncles_hash = uncle_bytes.sha3();
if s.block.base.header.uncles_hash().is_zero() {
s.block.base.header.set_uncles_hash(uncle_bytes.sha3());
}
if s.block.base.header.receipts_root.is_zero() || s.block.base.header.receipts_root == SHA3_NULL_RLP {
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect());
if s.block.base.header.receipts_root().is_zero() || s.block.base.header.receipts_root() == &SHA3_NULL_RLP {
s.block.base.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect()));
}
s.block.base.header.state_root = s.block.state.root().clone();
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
s.block.base.header.note_dirty();
s.block.base.header.set_state_root(s.block.state.root().clone());
s.block.base.header.set_log_bloom(s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator
s.block.base.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used));
LockedBlock {
block: s.block,
@@ -625,9 +623,9 @@ mod tests {
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec();
uncle1_header.set_extra_data(b"uncle1".to_vec());
let mut uncle2_header = Header::new();
uncle2_header.extra_data = b"uncle2".to_vec();
uncle2_header.set_extra_data(b"uncle2".to_vec());
open_block.push_uncle(uncle1_header).unwrap();
open_block.push_uncle(uncle2_header).unwrap();
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
@@ -643,7 +641,7 @@ mod tests {
let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes);
let uncles = BlockView::new(&bytes).uncles();
assert_eq!(uncles[1].extra_data, b"uncle2");
assert_eq!(uncles[1].extra_data(), b"uncle2");
let db = e.drain();
assert_eq!(orig_db.keys(), db.keys());

View File

@@ -260,7 +260,7 @@ impl BlockQueue {
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
let block = verifying.pop_front().unwrap().block.unwrap();
if bad.contains(&block.header.parent_hash) {
if bad.contains(block.header.parent_hash()) {
bad.insert(block.header.hash());
}
else {
@@ -313,7 +313,7 @@ impl BlockQueue {
return Err(ImportError::KnownBad.into());
}
if bad.contains(&header.parent_hash) {
if bad.contains(header.parent_hash()) {
bad.insert(h.clone());
return Err(ImportError::KnownBad.into());
}
@@ -351,7 +351,7 @@ impl BlockQueue {
let mut new_verified = VecDeque::new();
for block in verified.drain(..) {
if bad.contains(&block.header.parent_hash) {
if bad.contains(block.header.parent_hash()) {
bad.insert(block.header.hash());
processing.remove(&block.header.hash());
} else {

View File

@@ -1434,7 +1434,7 @@ mod tests {
let mut block_header = bc.block_header(&best_hash);
while !block_header.is_none() {
block_header = bc.block_header(&block_header.unwrap().parent_hash);
block_header = bc.block_header(&block_header.unwrap().parent_hash());
}
assert!(bc.cache_size().blocks > 1024 * 1024);

View File

@@ -44,21 +44,22 @@ impl Encodable for Block {
impl Forkable for Block {
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
let difficulty = self.header.difficulty().clone() - U256::from(fork_number);
self.header.set_difficulty(difficulty);
self
}
}
impl WithBloom for Block {
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
self.header.log_bloom = bloom;
self.header.set_log_bloom(bloom);
self
}
}
impl CompleteBlock for Block {
fn complete(mut self, parent_hash: H256) -> Bytes {
self.header.parent_hash = parent_hash;
self.header.set_parent_hash(parent_hash);
encode(&self).to_vec()
}
}

View File

@@ -73,8 +73,8 @@ pub struct ChainGenerator {
impl ChainGenerator {
fn prepare_block(&self) -> Block {
let mut block = Block::default();
block.header.number = self.number;
block.header.difficulty = self.difficulty;
block.header.set_number(self.number);
block.header.set_difficulty(self.difficulty);
block
}
}

View File

@@ -14,283 +14,350 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use crypto::sha2::Sha256;
use crypto::ripemd160::Ripemd160;
use crypto::sha2::Sha256 as Sha256Digest;
use crypto::ripemd160::Ripemd160 as Ripemd160Digest;
use crypto::digest::Digest;
use util::*;
use ethkey::{Signature, recover};
use ethkey::{Signature, recover as ec_recover};
use ethjson;
/// Definition of a contract whose implementation is built-in.
pub struct Builtin {
/// The gas cost of running this built-in for the given size of input data.
pub cost: Box<Fn(usize) -> U256>, // TODO: U256 should be bignum.
/// Run this built-in function with the input being the first argument and the output
/// being placed into the second.
pub execute: Box<Fn(&[u8], &mut [u8])>,
/// Native implementation of a built-in contract.
pub trait Impl: Send + Sync {
/// execute this built-in on the given input, writing to the given output.
fn execute(&self, input: &[u8], out: &mut [u8]);
}
// Rust does not mark closurer that do not capture as Sync
// We promise that all builtins are thread safe since they only operate on given input.
unsafe impl Sync for Builtin {}
unsafe impl Send for Builtin {}
/// A gas pricing scheme for built-in contracts.
pub trait Pricer: Send + Sync {
/// The gas cost of running this built-in for the given size of input data.
fn cost(&self, in_size: usize) -> U256;
}
impl fmt::Debug for Builtin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Builtin>")
/// A linear pricing model. This computes a price using a base cost and a cost per-word.
struct Linear {
base: usize,
word: usize,
}
impl Pricer for Linear {
fn cost(&self, in_size: usize) -> U256 {
U256::from(self.base) + U256::from(self.word) * U256::from((in_size + 31) / 32)
}
}
/// Pricing scheme and execution definition for a built-in contract.
pub struct Builtin {
pricer: Box<Pricer>,
native: Box<Impl>,
}
impl Builtin {
/// Create a new object from components.
pub fn new(cost: Box<Fn(usize) -> U256>, execute: Box<Fn(&[u8], &mut [u8])>) -> Builtin {
Builtin {cost: cost, execute: execute}
}
/// Create a new object from a builtin-function name with a linear cost associated with input size.
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Builtin {
let cost = Box::new(move|s: usize| -> U256 {
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
});
Self::new(cost, new_builtin_exec(name))
}
/// Simple forwarder for cost.
pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) }
pub fn cost(&self, s: usize) -> U256 { self.pricer.cost(s) }
/// Simple forwarder for execute.
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
pub fn execute(&self, input: &[u8], output: &mut[u8]) { self.native.execute(input, output) }
}
impl From<ethjson::spec::Builtin> for Builtin {
fn from(b: ethjson::spec::Builtin) -> Self {
match b.pricing {
let pricer = match b.pricing {
ethjson::spec::Pricing::Linear(linear) => {
Self::from_named_linear(b.name.as_ref(), linear.base, linear.word)
Box::new(Linear {
base: linear.base,
word: linear.word,
})
}
};
Builtin {
pricer: pricer,
native: ethereum_builtin(&b.name),
}
}
}
/// Copy a bunch of bytes to a destination; if the `src` is too small to fill `dest`,
/// leave the rest unchanged.
pub fn copy_to(src: &[u8], dest: &mut[u8]) {
// NICE: optimise
for i in 0..min(src.len(), dest.len()) {
dest[i] = src[i];
// Ethereum builtin creator.
fn ethereum_builtin(name: &str) -> Box<Impl> {
match name {
"identity" => Box::new(Identity) as Box<Impl>,
"ecrecover" => Box::new(EcRecover) as Box<Impl>,
"sha256" => Box::new(Sha256) as Box<Impl>,
"ripemd160" => Box::new(Ripemd160) as Box<Impl>,
_ => panic!("invalid builtin name: {}", name),
}
}
/// Create a new builtin executor according to `name`.
/// TODO: turn in to a factory with dynamic registration.
pub fn new_builtin_exec(name: &str) -> Box<Fn(&[u8], &mut [u8])> {
match name {
"identity" => Box::new(move|input: &[u8], output: &mut[u8]| {
for i in 0..min(input.len(), output.len()) {
output[i] = input[i];
}
}),
"ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| {
#[repr(packed)]
#[derive(Debug, Default)]
struct InType {
hash: H256,
v: H256,
r: H256,
s: H256,
}
let mut it = InType::default();
it.copy_raw(input);
if it.v == H256::from(&U256::from(27)) || it.v == H256::from(&U256::from(28)) {
let s = Signature::from_rsv(&it.r, &it.s, it.v[31] - 27);
if s.is_valid() {
if let Ok(p) = recover(&s, &it.hash) {
let r = p.as_slice().sha3();
// NICE: optimise and separate out into populate-like function
for i in 0..min(32, output.len()) {
output[i] = if i < 12 {0} else {r[i]};
}
}
// Ethereum builtins:
//
// - The identity function
// - ec recovery
// - sha256
// - ripemd160
#[derive(Debug)]
struct Identity;
#[derive(Debug)]
struct EcRecover;
#[derive(Debug)]
struct Sha256;
#[derive(Debug)]
struct Ripemd160;
impl Impl for Identity {
fn execute(&self, input: &[u8], output: &mut [u8]) {
let len = min(input.len(), output.len());
output[..len].copy_from_slice(&input[..len]);
}
}
impl Impl for EcRecover {
fn execute(&self, i: &[u8], output: &mut [u8]) {
let len = min(i.len(), 128);
let mut input = [0; 128];
input[..len].copy_from_slice(&i[..len]);
let hash = H256::from_slice(&input[0..32]);
let v = H256::from_slice(&input[32..64]);
let r = H256::from_slice(&input[64..96]);
let s = H256::from_slice(&input[96..128]);
let bit = match v[31] {
27 | 28 if &v.as_slice()[..31] == &[0; 31] => v[31] - 27,
_ => return,
};
let s = Signature::from_rsv(&r, &s, bit);
if s.is_valid() {
if let Ok(p) = ec_recover(&s, &hash) {
let r = p.as_slice().sha3();
let out_len = min(output.len(), 32);
for x in &mut output[0.. min(12, out_len)] {
*x = 0;
}
if out_len > 12 {
output[12..out_len].copy_from_slice(&r[12..out_len]);
}
}
}),
"sha256" => Box::new(move|input: &[u8], output: &mut[u8]| {
let mut sha = Sha256::new();
sha.input(input);
if output.len() >= 32 {
sha.result(output);
} else {
let mut ret = H256::new();
sha.result(ret.as_slice_mut());
copy_to(&ret, output);
}
}),
"ripemd160" => Box::new(move|input: &[u8], output: &mut[u8]| {
let mut sha = Ripemd160::new();
sha.input(input);
let mut ret = H256::new();
sha.result(&mut ret.as_slice_mut()[12..32]);
copy_to(&ret, output);
}),
_ => {
panic!("invalid builtin name {}", name);
}
}
}
#[test]
fn identity() {
let f = new_builtin_exec("identity");
let i = [0u8, 1, 2, 3];
impl Impl for Sha256 {
fn execute(&self, input: &[u8], output: &mut [u8]) {
let out_len = min(output.len(), 32);
let mut o2 = [255u8; 2];
f(&i[..], &mut o2[..]);
assert_eq!(i[0..2], o2);
let mut sha = Sha256Digest::new();
sha.input(input);
let mut o4 = [255u8; 4];
f(&i[..], &mut o4[..]);
assert_eq!(i, o4);
if out_len == 32 {
sha.result(&mut output[0..32]);
} else {
let mut out = [0; 32];
sha.result(&mut out);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(i, o8[..4]);
assert_eq!([255u8; 4], o8[4..]);
output.copy_from_slice(&out[..out_len])
}
}
}
#[test]
fn sha256() {
use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("sha256");
let i = [0u8; 0];
impl Impl for Ripemd160 {
fn execute(&self, input: &[u8], output: &mut [u8]) {
let out_len = min(output.len(), 32);
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
let mut sha = Ripemd160Digest::new();
sha.input(input);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
for x in &mut output[0.. min(12, out_len)] {
*x = 0;
}
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
if out_len >= 32 {
sha.result(&mut output[12..32]);
} else if out_len > 12 {
let mut out = [0; 20];
sha.result(&mut out);
output.copy_from_slice(&out[12..out_len])
}
}
}
#[test]
fn ripemd160() {
use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("ripemd160");
let i = [0u8; 0];
#[cfg(test)]
mod tests {
use super::{Builtin, Linear, ethereum_builtin, Pricer};
use ethjson;
use util::U256;
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
#[test]
fn identity() {
let f = ethereum_builtin("identity");
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let i = [0u8, 1, 2, 3];
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
}
let mut o2 = [255u8; 2];
f.execute(&i[..], &mut o2[..]);
assert_eq!(i[0..2], o2);
#[test]
fn ecrecover() {
use rustc_serialize::hex::FromHex;
/*let k = KeyPair::from_secret(b"test".sha3()).unwrap();
let a: Address = From::from(k.public().sha3());
println!("Address: {}", a);
let m = b"hello world".sha3();
println!("Message: {}", m);
let s = k.sign(&m).unwrap();
println!("Signed: {}", s);*/
let mut o4 = [255u8; 4];
f.execute(&i[..], &mut o4[..]);
assert_eq!(i, o4);
let f = new_builtin_exec("ecrecover");
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
assert_eq!(i, o8[..4]);
assert_eq!([255u8; 4], o8[4..]);
}
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
#[test]
fn sha256() {
use rustc_serialize::hex::FromHex;
let f = ethereum_builtin("sha256");
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let i = [0u8; 0];
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
}
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
#[test]
fn ripemd160() {
use rustc_serialize::hex::FromHex;
let f = ethereum_builtin("ripemd160");
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i = [0u8; 0];
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
}
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
#[test]
#[should_panic]
fn from_unknown_linear() {
let _ = Builtin::from_named_linear("dw", 10, 20);
}
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
}
#[test]
fn from_named_linear() {
let b = Builtin::from_named_linear("identity", 10, 20);
assert_eq!((*b.cost)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30));
assert_eq!((*b.cost)(33), U256::from(50));
#[test]
fn ecrecover() {
use rustc_serialize::hex::FromHex;
/*let k = KeyPair::from_secret(b"test".sha3()).unwrap();
let a: Address = From::from(k.public().sha3());
println!("Address: {}", a);
let m = b"hello world".sha3();
println!("Message: {}", m);
let s = k.sign(&m).unwrap();
println!("Signed: {}", s);*/
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
(*b.execute)(&i[..], &mut o[..]);
assert_eq!(i, o);
}
let f = ethereum_builtin("ecrecover");
#[test]
fn from_json() {
let b = Builtin::from(ethjson::spec::Builtin {
name: "identity".to_owned(),
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
base: 10,
word: 20,
})
});
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
assert_eq!((*b.cost)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30));
assert_eq!((*b.cost)(33), U256::from(50));
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
(*b.execute)(&i[..], &mut o[..]);
assert_eq!(i, o);
}
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
}
#[test]
#[should_panic]
fn from_unknown_linear() {
let _ = ethereum_builtin("foo");
}
#[test]
fn from_named_linear() {
let pricer = Box::new(Linear { base: 10, word: 20 });
let b = Builtin {
pricer: pricer as Box<Pricer>,
native: ethereum_builtin("identity"),
};
assert_eq!(b.cost(0), U256::from(10));
assert_eq!(b.cost(1), U256::from(30));
assert_eq!(b.cost(32), U256::from(30));
assert_eq!(b.cost(33), U256::from(50));
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
b.execute(&i[..], &mut o[..]);
assert_eq!(i, o);
}
#[test]
fn from_json() {
let b = Builtin::from(ethjson::spec::Builtin {
name: "identity".to_owned(),
pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear {
base: 10,
word: 20,
})
});
assert_eq!(b.cost(0), U256::from(10));
assert_eq!(b.cost(1), U256::from(30));
assert_eq!(b.cost(32), U256::from(30));
assert_eq!(b.cost(33), U256::from(50));
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
b.execute(&i[..], &mut o[..]);
assert_eq!(i, o);
}
}

View File

@@ -100,7 +100,7 @@ impl ClientReport {
pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
self.blocks_imported += 1;
self.transactions_applied += block.transactions.len();
self.gas_processed = self.gas_processed + block.header.gas_used;
self.gas_processed = self.gas_processed + block.header.gas_used().clone();
}
}
@@ -285,15 +285,15 @@ impl Client {
};
// Check if Parent is in chain
let chain_has_parent = self.chain.block_header(&header.parent_hash);
let chain_has_parent = self.chain.block_header(header.parent_hash());
if let None = chain_has_parent {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
return Err(());
};
// Enact Verified Block
let parent = chain_has_parent.unwrap();
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
let db = self.state_db.lock().boxed_clone();
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
@@ -353,7 +353,7 @@ impl Client {
for block in blocks {
let header = &block.header;
if invalid_blocks.contains(&header.parent_hash) {
if invalid_blocks.contains(header.parent_hash()) {
invalid_blocks.insert(header.hash());
continue;
}

View File

@@ -169,19 +169,19 @@ impl TestBlockChainClient {
let len = self.numbers.read().len();
for n in len..(len + count) {
let mut header = BlockHeader::new();
header.difficulty = From::from(n);
header.parent_hash = self.last_hash.read().clone();
header.number = n as BlockNumber;
header.gas_limit = U256::from(1_000_000);
header.set_difficulty(From::from(n));
header.set_parent_hash(self.last_hash.read().clone());
header.set_number(n as BlockNumber);
header.set_gas_limit(U256::from(1_000_000));
let uncles = match with {
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
let mut uncles = RlpStream::new_list(1);
let mut uncle_header = BlockHeader::new();
uncle_header.difficulty = From::from(n);
uncle_header.parent_hash = self.last_hash.read().clone();
uncle_header.number = n as BlockNumber;
uncle_header.set_difficulty(From::from(n));
uncle_header.set_parent_hash(self.last_hash.read().clone());
uncle_header.set_number(n as BlockNumber);
uncles.append(&uncle_header);
header.uncles_hash = uncles.as_raw().sha3();
header.set_uncles_hash(uncles.as_raw().sha3());
uncles
},
_ => RlpStream::new_list(0)
@@ -219,7 +219,7 @@ impl TestBlockChainClient {
pub fn corrupt_block(&mut self, n: BlockNumber) {
let hash = self.block_hash(BlockID::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap());
header.extra_data = b"This extra data is way too long to be considered valid".to_vec();
header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
@@ -231,7 +231,7 @@ impl TestBlockChainClient {
pub fn corrupt_block_parent(&mut self, n: BlockNumber) {
let hash = self.block_hash(BlockID::Number(n)).unwrap();
let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap());
header.parent_hash = H256::from(42);
header.set_parent_hash(H256::from(42));
let mut rlp = RlpStream::new_list(3);
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
@@ -470,20 +470,20 @@ impl BlockChainClient for TestBlockChainClient {
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
let h = header.hash();
let number: usize = header.number as usize;
let number: usize = header.number() as usize;
if number > self.blocks.read().len() {
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number);
}
if number > 0 {
match self.blocks.read().get(&header.parent_hash) {
match self.blocks.read().get(header.parent_hash()) {
Some(parent) => {
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
if parent.number != (header.number - 1) {
if parent.number() != (header.number() - 1) {
panic!("Unexpected block parent");
}
},
None => {
panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
panic!("Unknown block parent {:?} for block {}", header.parent_hash(), number);
}
}
}
@@ -491,18 +491,18 @@ impl BlockChainClient for TestBlockChainClient {
if number == len {
{
let mut difficulty = self.difficulty.write();
*difficulty = *difficulty + header.difficulty;
*difficulty = *difficulty + header.difficulty().clone();
}
mem::replace(&mut *self.last_hash.write(), h.clone());
self.blocks.write().insert(h.clone(), b);
self.numbers.write().insert(number, h.clone());
let mut parent_hash = header.parent_hash;
let mut parent_hash = header.parent_hash().clone();
if number > 0 {
let mut n = number - 1;
while n > 0 && self.numbers.read()[&n] != parent_hash {
*self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone();
n -= 1;
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash().clone();
}
}
}

View File

@@ -82,17 +82,16 @@ impl Engine for BasicAuthority {
}
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
header.difficulty = parent.difficulty;
header.gas_limit = {
let gas_limit = parent.gas_limit;
header.set_difficulty(parent.difficulty().clone());
header.set_gas_limit({
let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.our_params.gas_limit_bound_divisor;
if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
} else {
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
}
};
header.note_dirty();
});
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
}
@@ -123,9 +122,9 @@ impl Engine for BasicAuthority {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the seal fields.
// TODO: pull this out into common code.
if header.seal.len() != self.seal_fields() {
if header.seal().len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
)));
}
Ok(())
@@ -133,7 +132,7 @@ impl Engine for BasicAuthority {
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the signature is legit.
let sig = try!(UntrustedRlp::new(&header.seal[0]).as_val::<H520>());
let sig = try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H520>());
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash())));
if !self.our_params.authorities.contains(&signer) {
return try!(Err(BlockError::InvalidSeal));
@@ -152,10 +151,10 @@ impl Engine for BasicAuthority {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
}
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
}
Ok(())
}

View File

@@ -131,9 +131,8 @@ pub trait Engine : Sync + Send {
/// Don't forget to call Super::populate_from_parent when subclassing & overriding.
// TODO: consider including State in the params.
fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) {
header.difficulty = parent.difficulty;
header.gas_limit = parent.gas_limit;
header.note_dirty();
header.set_difficulty(parent.difficulty().clone());
header.set_gas_limit(parent.gas_limit().clone());
}
/// Handle any potential consensus messages;

View File

@@ -114,9 +114,9 @@ impl Engine for Ethash {
}
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, gas_ceil_target: U256) {
header.difficulty = self.calculate_difficuty(header, parent);
header.gas_limit = {
let gas_limit = parent.gas_limit;
let difficulty = self.calculate_difficulty(header, parent);
let gas_limit = {
let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
@@ -126,21 +126,23 @@ impl Engine for Ethash {
min(gas_ceil_target,
max(gas_floor_target,
gas_limit - gas_limit / bound_divisor + 1.into() +
(header.gas_used * 6.into() / 5.into()) / bound_divisor))
(header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor))
}
};
if header.number >= self.ethash_params.dao_hardfork_transition &&
header.number <= self.ethash_params.dao_hardfork_transition + 9 {
header.extra_data = b"dao-hard-fork"[..].to_owned();
header.set_difficulty(difficulty);
header.set_gas_limit(gas_limit);
if header.number() >= self.ethash_params.dao_hardfork_transition &&
header.number() <= self.ethash_params.dao_hardfork_transition + 9 {
header.set_extra_data(b"dao-hard-fork"[..].to_owned());
}
header.note_dirty();
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number(), header.difficulty(), header.gas_limit());
}
fn on_new_block(&self, block: &mut ExecutedBlock) {
if block.fields().header.number == self.ethash_params.dao_hardfork_transition {
if block.fields().header.number() == self.ethash_params.dao_hardfork_transition {
// TODO: enable trigger function maybe?
// if block.fields().header.gas_limit <= 4_000_000.into() {
// if block.fields().header.gas_limit() <= 4_000_000.into() {
let mut state = block.fields_mut().state;
for child in &self.ethash_params.dao_hardfork_accounts {
let b = state.balance(child);
@@ -157,7 +159,7 @@ impl Engine for Ethash {
let fields = block.fields_mut();
// Bestow block reward
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
fields.state.add_balance(&fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
// Bestow uncle rewards
let current_number = fields.header.number();
@@ -171,18 +173,18 @@ impl Engine for Ethash {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the seal fields.
if header.seal.len() != self.seal_fields() {
if header.seal().len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
)));
}
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
try!(UntrustedRlp::new(&header.seal()[0]).as_val::<H256>());
try!(UntrustedRlp::new(&header.seal()[1]).as_val::<H64>());
// TODO: consider removing these lines.
let min_difficulty = self.ethash_params.minimum_difficulty;
if header.difficulty < min_difficulty {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty })))
if header.difficulty() < &min_difficulty {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() })))
}
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
@@ -190,37 +192,37 @@ impl Engine for Ethash {
header.nonce().low_u64(),
&Ethash::to_ethash(header.mix_hash())
)));
if difficulty < header.difficulty {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
if &difficulty < header.difficulty() {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
}
if header.number >= self.ethash_params.dao_hardfork_transition &&
header.number <= self.ethash_params.dao_hardfork_transition + 9 &&
header.extra_data[..] != b"dao-hard-fork"[..] {
if header.number() >= self.ethash_params.dao_hardfork_transition &&
header.number() <= self.ethash_params.dao_hardfork_transition + 9 &&
header.extra_data()[..] != b"dao-hard-fork"[..] {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 })));
}
if header.gas_limit > 0x7fffffffffffffffu64.into() {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit })));
if header.gas_limit() > &0x7fffffffffffffffu64.into() {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit().clone() })));
}
Ok(())
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
if header.seal.len() != self.seal_fields() {
if header.seal().len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
)));
}
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
let result = self.pow.compute_light(header.number() as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
let mix = Ethash::from_ethash(result.mix_hash);
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
if mix != header.mix_hash() {
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
}
if difficulty < header.difficulty {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
if &difficulty < header.difficulty() {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
}
Ok(())
}
@@ -232,15 +234,15 @@ impl Engine for Ethash {
}
// Check difficulty is correct given the two timestamps.
let expected_difficulty = self.calculate_difficuty(header, parent);
if header.difficulty != expected_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
let expected_difficulty = self.calculate_difficulty(header, parent);
if header.difficulty() != &expected_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() })))
}
let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor;
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
}
Ok(())
}
@@ -259,9 +261,9 @@ impl Engine for Ethash {
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
impl Ethash {
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
const EXP_DIFF_PERIOD: u64 = 100000;
if header.number == 0 {
if header.number() == 0 {
panic!("Can't calculate genesis block difficulty");
}
@@ -270,25 +272,25 @@ impl Ethash {
let duration_limit = self.ethash_params.duration_limit;
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
let mut target = if header.number < frontier_limit {
if header.timestamp >= parent.timestamp + duration_limit {
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
let mut target = if header.number() < frontier_limit {
if header.timestamp() >= parent.timestamp() + duration_limit {
parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor)
} else {
parent.difficulty + (parent.difficulty / difficulty_bound_divisor)
parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor)
}
}
else {
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty, header.timestamp, parent.timestamp);
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp());
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
let diff_inc = (header.timestamp - parent.timestamp) / 10;
let diff_inc = (header.timestamp() - parent.timestamp()) / 10;
if diff_inc <= 1 {
parent.difficulty + parent.difficulty / From::from(2048) * From::from(1 - diff_inc)
parent.difficulty().clone() + parent.difficulty().clone() / From::from(2048) * From::from(1 - diff_inc)
} else {
parent.difficulty - parent.difficulty / From::from(2048) * From::from(min(diff_inc - 1, 99))
parent.difficulty().clone() - parent.difficulty().clone() / From::from(2048) * From::from(min(diff_inc - 1, 99))
}
};
target = max(min_difficulty, target);
let period = ((parent.number + 1) / EXP_DIFF_PERIOD) as usize;
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
if period > 1 {
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
}
@@ -336,7 +338,7 @@ impl Header {
/// Set the nonce and mix hash fields of the header.
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
self.seal = vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()];
self.set_seal(vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()]);
}
}
@@ -374,7 +376,7 @@ mod tests {
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle = Header::new();
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
uncle.author = uncle_author.clone();
uncle.set_author(uncle_author);
b.push_uncle(uncle).unwrap();
let b = b.close();

View File

@@ -68,7 +68,7 @@ mod tests {
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(), Default::default()).unwrap();
assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()), 1u64.into());
assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()), 1u64.into());

View File

@@ -33,43 +33,42 @@ pub type BlockNumber = u64;
/// Doesn't do all that much on its own.
#[derive(Debug, Clone, Eq)]
pub struct Header {
// TODO: make all private.
/// Parent hash.
pub parent_hash: H256,
parent_hash: H256,
/// Block timestamp.
pub timestamp: u64,
timestamp: u64,
/// Block number.
pub number: BlockNumber,
number: BlockNumber,
/// Block author.
pub author: Address,
author: Address,
/// Transactions root.
pub transactions_root: H256,
transactions_root: H256,
/// Block uncles hash.
pub uncles_hash: H256,
uncles_hash: H256,
/// Block extra data.
pub extra_data: Bytes,
extra_data: Bytes,
/// State root.
pub state_root: H256,
state_root: H256,
/// Block receipts root.
pub receipts_root: H256,
receipts_root: H256,
/// Block bloom.
pub log_bloom: LogBloom,
log_bloom: LogBloom,
/// Gas used for contracts execution.
pub gas_used: U256,
gas_used: U256,
/// Block gas limit.
pub gas_limit: U256,
gas_limit: U256,
/// Block difficulty.
pub difficulty: U256,
difficulty: U256,
/// Vector of post-RLP-encoded fields.
pub seal: Vec<Bytes>,
seal: Vec<Bytes>,
/// The memoized hash of the RLP representation *including* the seal fields.
pub hash: RefCell<Option<H256>>,
hash: RefCell<Option<H256>>,
/// The memoized hash of the RLP representation *without* the seal fields.
pub bare_hash: RefCell<Option<H256>>,
bare_hash: RefCell<Option<H256>>,
}
impl PartialEq for Header {
@@ -134,15 +133,21 @@ impl Header {
/// Get the extra data field of the header.
pub fn extra_data(&self) -> &Bytes { &self.extra_data }
/// Get a mutable reference to extra_data
pub fn extra_data_mut(&mut self) -> &mut Bytes { self.note_dirty(); &mut self.extra_data }
/// Get the state root field of the header.
pub fn state_root(&self) -> &H256 { &self.state_root }
/// Get the receipts root field of the header.
pub fn receipts_root(&self) -> &H256 { &self.receipts_root }
/// Get the log bloom field of the header.
pub fn log_bloom(&self) -> &LogBloom { &self.log_bloom }
/// Get the transactions root field of the header.
pub fn transactions_root(&self) -> &H256 { &self.transactions_root }
/// Get the uncles hash field of the header.
pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash }
/// Get the gas used field of the header.
pub fn gas_used(&self) -> &U256 { &self.gas_used }
/// Get the gas limit field of the header.
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }

View File

@@ -22,6 +22,7 @@ use spec::Spec;
use error::*;
use client::{Client, ClientConfig, ChainNotify};
use miner::Miner;
use snapshot::ManifestData;
use snapshot::service::Service as SnapshotService;
use std::sync::atomic::AtomicBool;
@@ -39,6 +40,8 @@ pub enum ClientIoMessage {
BlockVerified,
/// New transaction RLPs are ready to be imported
NewTransactions(Vec<Bytes>),
/// Begin snapshot restoration
BeginRestoration(ManifestData),
/// Feed a state chunk to the snapshot service
FeedStateChunk(H256, Bytes),
/// Feed a block chunk to the snapshot service
@@ -164,6 +167,11 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
match *net_message {
ClientIoMessage::BlockVerified => { self.client.import_verified_blocks(); }
ClientIoMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(transactions); }
ClientIoMessage::BeginRestoration(ref manifest) => {
if let Err(e) = self.snapshot.init_restore(manifest.clone()) {
warn!("Failed to initialize snapshot restoration: {}", e);
}
}
ClientIoMessage::FeedStateChunk(ref hash, ref chunk) => self.snapshot.feed_state_chunk(*hash, chunk),
ClientIoMessage::FeedBlockChunk(ref hash, ref chunk) => self.snapshot.feed_block_chunk(*hash, chunk),
_ => {} // ignore other messages

View File

@@ -90,27 +90,26 @@ impl AbridgedBlock {
let rlp = UntrustedRlp::new(&self.rlp).decompress(RlpType::Blocks);
let rlp = UntrustedRlp::new(&rlp);
let mut header = Header {
parent_hash: parent_hash,
author: try!(rlp.val_at(0)),
state_root: try!(rlp.val_at(1)),
transactions_root: try!(rlp.val_at(2)),
receipts_root: try!(rlp.val_at(3)),
log_bloom: try!(rlp.val_at(4)),
difficulty: try!(rlp.val_at(5)),
number: number,
gas_limit: try!(rlp.val_at(6)),
gas_used: try!(rlp.val_at(7)),
timestamp: try!(rlp.val_at(8)),
extra_data: try!(rlp.val_at(9)),
..Default::default()
};
let mut header: Header = Default::default();
header.set_parent_hash(parent_hash);
header.set_author(try!(rlp.val_at(0)));
header.set_state_root(try!(rlp.val_at(1)));
header.set_transactions_root(try!(rlp.val_at(2)));
header.set_receipts_root(try!(rlp.val_at(3)));
header.set_log_bloom(try!(rlp.val_at(4)));
header.set_difficulty(try!(rlp.val_at(5)));
header.set_number(number);
header.set_gas_limit(try!(rlp.val_at(6)));
header.set_gas_used(try!(rlp.val_at(7)));
header.set_timestamp(try!(rlp.val_at(8)));
header.set_extra_data(try!(rlp.val_at(9)));
let transactions = try!(rlp.val_at(10));
let uncles: Vec<Header> = try!(rlp.val_at(11));
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append(&uncles);
header.uncles_hash = uncles_rlp.as_raw().sha3();
header.set_uncles_hash(uncles_rlp.as_raw().sha3());
let mut seal_fields = Vec::new();
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() {

View File

@@ -501,7 +501,7 @@ impl StateRebuilder {
/// Check for accounts missing code. Once all chunks have been fed, there should
/// be none.
pub fn check_missing(&self) -> Result<(), Error> {
pub fn check_missing(self) -> Result<(), Error> {
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
match missing.is_empty() {
true => Ok(()),
@@ -640,8 +640,8 @@ impl BlockRebuilder {
}
/// Glue together any disconnected chunks. To be called at the end.
pub fn glue_chunks(&mut self) {
for &(ref first_num, ref first_hash) in &self.disconnected {
pub fn glue_chunks(self) {
for (first_num, first_hash) in self.disconnected {
let parent_num = first_num - 1;
// check if the parent is even in the chain.
@@ -649,7 +649,7 @@ impl BlockRebuilder {
// the first block of the first chunks has nothing to connect to.
if let Some(parent_hash) = self.chain.block_hash(parent_num) {
// if so, add the child to it.
self.chain.add_child(parent_hash, *first_hash);
self.chain.add_child(parent_hash, first_hash);
}
}
}

View File

@@ -24,7 +24,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::{ManifestData, StateRebuilder, BlockRebuilder};
use super::io::{SnapshotReader, LooseReader};
use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter};
use blockchain::BlockChain;
use engines::Engine;
@@ -34,7 +34,7 @@ use spec::Spec;
use io::IoChannel;
use util::{Bytes, H256, Mutex, UtilError};
use util::{Bytes, H256, Mutex, RwLock, UtilError};
use util::journaldb::Algorithm;
use util::kvdb::{Database, DatabaseConfig};
use util::snappy;
@@ -50,8 +50,6 @@ pub enum RestorationStatus {
Failed,
}
/// Restoration info.
/// The interface for a snapshot network service.
/// This handles:
/// - restoration of snapshots to temporary databases.
@@ -74,8 +72,10 @@ pub trait SnapshotService {
/// Begin snapshot restoration.
/// If restoration in-progress, this will reset it.
/// From this point on, any previous snapshot may become unavailable.
/// Returns true if successful, false otherwise.
fn begin_restore(&self, manifest: ManifestData) -> bool;
fn begin_restore(&self, manifest: ManifestData);
/// Abort an in-progress restoration if there is one.
fn abort_restore(&self);
/// Feed a raw state chunk to the service to be processed asynchronously.
/// no-op if not currently restoring.
@@ -88,51 +88,59 @@ pub trait SnapshotService {
/// State restoration manager.
struct Restoration {
manifest: ManifestData,
state_chunks_left: HashSet<H256>,
block_chunks_left: HashSet<H256>,
state: StateRebuilder,
blocks: BlockRebuilder,
writer: LooseWriter,
snappy_buffer: Bytes,
final_state_root: H256,
}
struct RestorationParams<'a> {
manifest: ManifestData, // manifest to base restoration on.
pruning: Algorithm, // pruning algorithm for the database.
db_path: PathBuf, // database path
writer: LooseWriter, // writer for recovered snapshot.
genesis: &'a [u8], // genesis block of the chain.
}
impl Restoration {
// make a new restoration, building databases in the given path.
fn new(manifest: &ManifestData, pruning: Algorithm, path: &Path, gb: &[u8]) -> Result<Self, Error> {
// make a new restoration using the given parameters.
fn new(params: RestorationParams) -> Result<Self, Error> {
let manifest = params.manifest;
let state_chunks = manifest.state_hashes.iter().cloned().collect();
let block_chunks = manifest.block_hashes.iter().cloned().collect();
let cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let raw_db = Arc::new(try!(Database::open(&cfg, &*path.to_string_lossy())
let raw_db = Arc::new(try!(Database::open(&cfg, &*params.db_path.to_string_lossy())
.map_err(UtilError::SimpleString)));
let chain = BlockChain::new(Default::default(), gb, raw_db.clone());
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone());
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
let root = manifest.state_root.clone();
Ok(Restoration {
state_chunks_left: manifest.state_hashes.iter().cloned().collect(),
block_chunks_left: manifest.block_hashes.iter().cloned().collect(),
state: StateRebuilder::new(raw_db, pruning),
manifest: manifest,
state_chunks_left: state_chunks,
block_chunks_left: block_chunks,
state: StateRebuilder::new(raw_db, params.pruning),
blocks: blocks,
writer: params.writer,
snappy_buffer: Vec::new(),
final_state_root: manifest.state_root,
final_state_root: root,
})
}
// feeds a state chunk
fn feed_state(&mut self, hash: H256, chunk: &[u8]) -> Result<(), Error> {
use util::trie::TrieError;
if self.state_chunks_left.remove(&hash) {
let len = try!(snappy::decompress_into(&chunk, &mut self.snappy_buffer));
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
try!(self.state.feed(&self.snappy_buffer[..len]));
if self.state_chunks_left.is_empty() {
try!(self.state.check_missing());
let root = self.state.state_root();
if root != self.final_state_root {
warn!("Final restored state has wrong state root: expected {:?}, got {:?}", root, self.final_state_root);
return Err(TrieError::InvalidStateRoot(root).into());
}
}
try!(self.writer.write_state_chunk(hash, chunk));
}
Ok(())
@@ -141,18 +149,39 @@ impl Restoration {
// feeds a block chunk
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine) -> Result<(), Error> {
if self.block_chunks_left.remove(&hash) {
let len = try!(snappy::decompress_into(&chunk, &mut self.snappy_buffer));
try!(self.blocks.feed(&self.snappy_buffer[..len], engine));
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
if self.block_chunks_left.is_empty() {
// connect out-of-order chunks.
self.blocks.glue_chunks();
}
try!(self.blocks.feed(&self.snappy_buffer[..len], engine));
try!(self.writer.write_block_chunk(hash, chunk));
}
Ok(())
}
// finish up restoration.
fn finalize(self) -> Result<(), Error> {
use util::trie::TrieError;
if !self.is_done() { return Ok(()) }
// verify final state root.
let root = self.state.state_root();
if root != self.final_state_root {
warn!("Final restored state has wrong state root: expected {:?}, got {:?}", root, self.final_state_root);
return Err(TrieError::InvalidStateRoot(root).into());
}
// check for missing code.
try!(self.state.check_missing());
// connect out-of-order chunks.
self.blocks.glue_chunks();
try!(self.writer.finish(self.manifest));
Ok(())
}
// is everything done?
fn is_done(&self) -> bool {
self.block_chunks_left.is_empty() && self.state_chunks_left.is_empty()
@@ -174,7 +203,7 @@ pub struct Service {
io_channel: Channel,
pruning: Algorithm,
status: Mutex<RestorationStatus>,
reader: Option<LooseReader>,
reader: RwLock<Option<LooseReader>>,
engine: Arc<Engine>,
genesis_block: Bytes,
state_chunks: AtomicUsize,
@@ -190,6 +219,7 @@ impl Service {
let reader = {
let mut snapshot_path = db_path.clone();
snapshot_path.push("snapshot");
snapshot_path.push("current");
LooseReader::new(snapshot_path).ok()
};
@@ -201,15 +231,15 @@ impl Service {
io_channel: io_channel,
pruning: pruning,
status: Mutex::new(RestorationStatus::Inactive),
reader: reader,
reader: RwLock::new(reader),
engine: spec.engine.clone(),
genesis_block: spec.genesis_block(),
state_chunks: AtomicUsize::new(0),
block_chunks: AtomicUsize::new(0),
};
// create the snapshot dir if it doesn't exist.
if let Err(e) = fs::create_dir_all(service.snapshot_dir()) {
// create the root snapshot dir if it doesn't exist.
if let Err(e) = fs::create_dir_all(service.root_dir()) {
if e.kind() != ErrorKind::AlreadyExists {
return Err(e.into())
}
@@ -225,16 +255,23 @@ impl Service {
Ok(service)
}
// get the snapshot path.
fn snapshot_dir(&self) -> PathBuf {
// get the root path.
fn root_dir(&self) -> PathBuf {
let mut dir = self.db_path.clone();
dir.push("snapshot");
dir
}
// get the current snapshot dir.
fn snapshot_dir(&self) -> PathBuf {
let mut dir = self.root_dir();
dir.push("current");
dir
}
// get the restoration directory.
fn restoration_dir(&self) -> PathBuf {
let mut dir = self.snapshot_dir();
let mut dir = self.root_dir();
dir.push("restoration");
dir
}
@@ -246,6 +283,13 @@ impl Service {
dir
}
// temporary snapshot recovery path.
fn temp_recovery_dir(&self) -> PathBuf {
let mut dir = self.restoration_dir();
dir.push("temp");
dir
}
// replace one the client's database with our own.
fn replace_client_db(&self) -> Result<(), Error> {
let our_db = self.restoration_db();
@@ -284,6 +328,42 @@ impl Service {
}
}
/// Initialize the restoration synchronously.
pub fn init_restore(&self, manifest: ManifestData) -> Result<(), Error> {
let rest_dir = self.restoration_dir();
let mut res = self.restoration.lock();
// tear down existing restoration.
*res = None;
// delete and restore the restoration dir.
if let Err(e) = fs::remove_dir_all(&rest_dir) {
match e.kind() {
ErrorKind::NotFound => {},
_ => return Err(e.into()),
}
}
try!(fs::create_dir_all(&rest_dir));
// make new restoration.
let writer = try!(LooseWriter::new(self.temp_recovery_dir()));
let params = RestorationParams {
manifest: manifest,
pruning: self.pruning,
db_path: self.restoration_db(),
writer: writer,
genesis: &self.genesis_block,
};
*res = Some(try!(Restoration::new(params)));
*self.status.lock() = RestorationStatus::Ongoing;
Ok(())
}
// finalize the restoration. this accepts an already-locked
// restoration as an argument -- so acquiring it again _will_
// lead to deadlock.
@@ -293,27 +373,52 @@ impl Service {
self.state_chunks.store(0, Ordering::SeqCst);
self.block_chunks.store(0, Ordering::SeqCst);
// destroy the restoration before replacing databases.
*rest = None;
// destroy the restoration before replacing databases and snapshot.
try!(rest.take().map(Restoration::finalize).unwrap_or(Ok(())));
try!(self.replace_client_db());
*self.status.lock() = RestorationStatus::Inactive;
let mut reader = self.reader.write();
*reader = None; // destroy the old reader if it existed.
let snapshot_dir = self.snapshot_dir();
trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy());
if let Err(e) = fs::remove_dir_all(&snapshot_dir) {
match e.kind() {
ErrorKind::NotFound => {}
_ => return Err(e.into()),
}
}
try!(fs::create_dir(&snapshot_dir));
trace!(target: "snapshot", "copying restored snapshot files over");
for maybe_file in try!(fs::read_dir(self.temp_recovery_dir())) {
let path = try!(maybe_file).path();
if let Some(name) = path.file_name().map(|x| x.to_owned()) {
let mut new_path = snapshot_dir.clone();
new_path.push(name);
try!(fs::rename(path, new_path));
}
}
// TODO: take control of restored snapshot.
let _ = fs::remove_dir_all(self.restoration_dir());
*reader = Some(try!(LooseReader::new(snapshot_dir)));
*self.status.lock() = RestorationStatus::Inactive;
Ok(())
}
/// Feed a chunk of either kind. no-op if no restoration or status is wrong.
fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> {
// TODO: be able to process block chunks and state chunks at same time?
let mut restoration = self.restoration.lock();
match self.status() {
RestorationStatus::Inactive | RestorationStatus::Failed => Ok(()),
RestorationStatus::Ongoing => {
// TODO: be able to process block chunks and state chunks at same time?
let mut restoration = self.restoration.lock();
let res = {
let rest = match *restoration {
Some(ref mut r) => r,
@@ -373,11 +478,11 @@ impl Service {
impl SnapshotService for Service {
fn manifest(&self) -> Option<ManifestData> {
self.reader.as_ref().map(|r| r.manifest().clone())
self.reader.read().as_ref().map(|r| r.manifest().clone())
}
fn chunk(&self, hash: H256) -> Option<Bytes> {
self.reader.as_ref().and_then(|r| r.chunk(hash).ok())
self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok())
}
fn status(&self) -> RestorationStatus {
@@ -388,37 +493,20 @@ impl SnapshotService for Service {
(self.state_chunks.load(Ordering::Relaxed), self.block_chunks.load(Ordering::Relaxed))
}
fn begin_restore(&self, manifest: ManifestData) -> bool {
let rest_dir = self.restoration_dir();
fn begin_restore(&self, manifest: ManifestData) {
self.io_channel.send(ClientIoMessage::BeginRestoration(manifest))
.expect("snapshot service and io service are kept alive by client service; qed");
}
let mut res = self.restoration.lock();
// tear down existing restoration.
*res = None;
// delete and restore the restoration dir.
if let Err(e) = fs::remove_dir_all(&rest_dir).and_then(|_| fs::create_dir_all(&rest_dir)) {
fn abort_restore(&self) {
*self.restoration.lock() = None;
*self.status.lock() = RestorationStatus::Inactive;
if let Err(e) = fs::remove_dir_all(&self.restoration_dir()) {
match e.kind() {
ErrorKind::NotFound => {},
_ => {
warn!("encountered error {} while beginning snapshot restoration.", e);
return false;
}
_ => warn!("encountered error {} while deleting snapshot restoration dir.", e),
}
}
// make new restoration.
let db_path = self.restoration_db();
*res = match Restoration::new(&manifest, self.pruning, &db_path, &self.genesis_block) {
Ok(b) => Some(b),
Err(e) => {
warn!("encountered error {} while beginning snapshot restoration.", e);
return false;
}
};
*self.status.lock() = RestorationStatus::Ongoing;
true
}
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {

View File

@@ -79,7 +79,6 @@ fn chunk_and_restore(amount: u64) {
}
rebuilder.glue_chunks();
drop(rebuilder);
// and test it.
let new_chain = BlockChain::new(Default::default(), &genesis, new_db);

View File

@@ -72,8 +72,9 @@ fn snap_and_restore() {
rebuilder.feed(&chunk).unwrap();
}
rebuilder.check_missing().unwrap();
assert_eq!(rebuilder.state_root(), state_root);
rebuilder.check_missing().unwrap();
new_db
};

View File

@@ -25,8 +25,6 @@ use super::seal::Generic as GenericSeal;
use ethereum;
use ethjson;
use std::cell::RefCell;
/// Parameters common to all engines.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(test, derive(Default))]
@@ -162,32 +160,30 @@ impl Spec {
/// Get the header of the genesis block.
pub fn genesis_header(&self) -> Header {
Header {
parent_hash: self.parent_hash.clone(),
timestamp: self.timestamp,
number: 0,
author: self.author.clone(),
transactions_root: self.transactions_root.clone(),
uncles_hash: RlpStream::new_list(0).out().sha3(),
extra_data: self.extra_data.clone(),
state_root: self.state_root().clone(),
receipts_root: self.receipts_root.clone(),
log_bloom: H2048::new().clone(),
gas_used: self.gas_used.clone(),
gas_limit: self.gas_limit.clone(),
difficulty: self.difficulty.clone(),
seal: {
let seal = {
let mut s = RlpStream::new_list(self.seal_fields);
s.append_raw(&self.seal_rlp, self.seal_fields);
s.out()
};
let r = Rlp::new(&seal);
(0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect()
},
hash: RefCell::new(None),
bare_hash: RefCell::new(None),
}
let mut header: Header = Default::default();
header.set_parent_hash(self.parent_hash.clone());
header.set_timestamp(self.timestamp);
header.set_number(0);
header.set_author(self.author.clone());
header.set_transactions_root(self.transactions_root.clone());
header.set_uncles_hash(RlpStream::new_list(0).out().sha3());
header.set_extra_data(self.extra_data.clone());
header.set_state_root(self.state_root().clone());
header.set_receipts_root(self.receipts_root.clone());
header.set_log_bloom(H2048::new().clone());
header.set_gas_used(self.gas_used.clone());
header.set_gas_limit(self.gas_limit.clone());
header.set_difficulty(self.difficulty.clone());
header.set_seal({
let seal = {
let mut s = RlpStream::new_list(self.seal_fields);
s.append_raw(&self.seal_rlp, self.seal_fields);
s.out()
};
let r = Rlp::new(&seal);
(0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect()
});
return header;
}
/// Compose the genesis block for this chain.

View File

@@ -45,9 +45,9 @@ fn returns_state_root_basic() {
let client_result = generate_dummy_client(6);
let client = client_result.reference();
let test_spec = get_test_spec();
let state_root = test_spec.genesis_header().state_root;
let genesis_header = test_spec.genesis_header();
assert!(client.state_data(&state_root).is_some());
assert!(client.state_data(genesis_header.state_root()).is_some());
}
#[test]

View File

@@ -84,26 +84,26 @@ pub fn create_test_block(header: &Header) -> Bytes {
fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header {
let mut header = Header::new();
header.gas_limit = 0.into();
header.difficulty = (order * 100).into();
header.timestamp = (order * 10) as u64;
header.number = order as u64;
header.parent_hash = parent_hash;
header.state_root = H256::zero();
header.set_gas_limit(0.into());
header.set_difficulty((order * 100).into());
header.set_timestamp((order * 10) as u64);
header.set_number(order as u64);
header.set_parent_hash(parent_hash);
header.set_state_root(H256::zero());
header
}
fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option<Bytes>) -> Bytes {
let mut header = create_unverifiable_block_header(order, parent_hash);
header.extra_data = match extra {
header.set_extra_data(match extra {
Some(extra_data) => extra_data,
None => {
let base = (order & 0x000000ff) as u8;
let generated: Vec<u8> = vec![base + 1, base + 2, base + 3];
generated
}
};
});
create_test_block(&header)
}
@@ -204,7 +204,7 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
let test_spec = get_test_spec();
let test_engine = &test_spec.engine;
//let test_engine = test_spec.to_engine().unwrap();
let state_root = test_spec.genesis_header().state_root;
let state_root = test_spec.genesis_header().state_root().clone();
let mut rolling_hash = client.chain_info().best_block_hash;
let mut rolling_block_number = starting_number as u64;
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
@@ -212,12 +212,12 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
for _ in 0..block_number {
let mut header = Header::new();
header.gas_limit = test_engine.params().min_gas_limit;
header.difficulty = U256::from(0x20000);
header.timestamp = rolling_timestamp;
header.number = rolling_block_number;
header.parent_hash = rolling_hash;
header.state_root = state_root.clone();
header.set_gas_limit(test_engine.params().min_gas_limit);
header.set_difficulty(U256::from(0x20000));
header.set_timestamp(rolling_timestamp);
header.set_number(rolling_block_number);
header.set_parent_hash(rolling_hash);
header.set_state_root(state_root);
rolling_hash = header.hash();
rolling_block_number = rolling_block_number + 1;
@@ -345,12 +345,12 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h
let mut r = Vec::new();
for i in start_number .. start_number + count + 1 {
let mut block_header = Header::new();
block_header.gas_limit = test_engine.params().min_gas_limit;
block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
block_header.timestamp = rolling_timestamp;
block_header.number = i as u64;
block_header.parent_hash = parent;
block_header.state_root = test_spec.genesis_header().state_root;
block_header.set_gas_limit(test_engine.params().min_gas_limit);
block_header.set_difficulty(U256::from(i).mul(U256([0, 1, 0, 0])));
block_header.set_timestamp(rolling_timestamp);
block_header.set_number(i as u64);
block_header.set_parent_hash(parent);
block_header.set_state_root(test_spec.genesis_header().state_root().clone());
parent = block_header.hash();
rolling_timestamp = rolling_timestamp + 10;
@@ -365,12 +365,12 @@ pub fn get_good_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = &test_spec.engine;
block_header.gas_limit = test_engine.params().min_gas_limit;
block_header.difficulty = U256::from(0x20000);
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_spec.genesis_header().hash();
block_header.state_root = test_spec.genesis_header().state_root;
block_header.set_gas_limit(test_engine.params().min_gas_limit);
block_header.set_difficulty(U256::from(0x20000));
block_header.set_timestamp(40);
block_header.set_number(1);
block_header.set_parent_hash(test_spec.genesis_header().hash());
block_header.set_state_root(test_spec.genesis_header().state_root().clone());
create_test_block(&block_header)
}
@@ -379,12 +379,12 @@ pub fn get_bad_state_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = &test_spec.engine;
block_header.gas_limit = test_engine.params().min_gas_limit;
block_header.difficulty = U256::from(0x20000);
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_spec.genesis_header().hash();
block_header.state_root = 0xbad.into();
block_header.set_gas_limit(test_engine.params().min_gas_limit);
block_header.set_difficulty(U256::from(0x20000));
block_header.set_timestamp(40);
block_header.set_number(1);
block_header.set_parent_hash(test_spec.genesis_header().hash());
block_header.set_state_root(0xbad.into());
create_test_block(&block_header)
}

View File

@@ -38,7 +38,7 @@ pub struct PreverifiedBlock {
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
try!(verify_header(&header, engine));
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
try!(verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash()));
try!(engine.verify_block_basic(&header, Some(bytes)));
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
let u = try!(u);
@@ -81,7 +81,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) ->
/// Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
// TODO: verify timestamp
let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
let parent = try!(bc.block_header(&header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone()))));
try!(verify_parent(&header, &parent));
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
@@ -93,7 +93,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
let mut excluded = HashSet::new();
excluded.insert(header.hash());
let mut hash = header.parent_hash.clone();
let mut hash = header.parent_hash().clone();
excluded.insert(hash.clone());
for _ in 0..engine.maximum_uncle_age() {
match bc.block_details(&hash) {
@@ -122,12 +122,12 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
// 6 7
// (8 Invalid)
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
let depth = if header.number() > uncle.number() { header.number() - uncle.number() } else { 0 };
if depth > engine.maximum_uncle_age() as u64 {
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() })));
}
else if depth < 1 {
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() })));
}
// cB
@@ -139,8 +139,8 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
// cB.p^6 -----------/ 6
// cB.p^7 -------------/
// cB.p^8
let mut expected_uncle_parent = header.parent_hash.clone();
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
let mut expected_uncle_parent = header.parent_hash().clone();
let uncle_parent = try!(bc.block_header(&uncle.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash().clone()))));
for _ in 0..depth {
match bc.block_details(&expected_uncle_parent) {
Some(details) => {
@@ -162,50 +162,50 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
/// Phase 4 verification. Check block information against transaction enactment results,
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
if expected.gas_used != got.gas_used {
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used, found: got.gas_used })))
if expected.gas_used() != got.gas_used() {
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used().clone(), found: got.gas_used().clone() })))
}
if expected.log_bloom != got.log_bloom {
return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom.clone(), found: got.log_bloom.clone() })))
if expected.log_bloom() != got.log_bloom() {
return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom().clone(), found: got.log_bloom().clone() })))
}
if expected.state_root != got.state_root {
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root.clone(), found: got.state_root.clone() })))
if expected.state_root() != got.state_root() {
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root().clone(), found: got.state_root().clone() })))
}
if expected.receipts_root != got.receipts_root {
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: expected.receipts_root.clone(), found: got.receipts_root.clone() })))
if expected.receipts_root() != got.receipts_root() {
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: expected.receipts_root().clone(), found: got.receipts_root().clone() })))
}
Ok(())
}
/// Check basic header parameters.
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
if header.number >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
if header.number() >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() })))
}
if header.gas_used > header.gas_limit {
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
if header.gas_used() > header.gas_limit() {
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit().clone()), min: None, found: header.gas_used().clone() })));
}
let min_gas_limit = engine.params().min_gas_limit;
if header.gas_limit < min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })));
if header.gas_limit() < &min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit().clone() })));
}
let maximum_extra_data_size = engine.maximum_extra_data_size();
if header.number != 0 && header.extra_data.len() > maximum_extra_data_size {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() })));
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() })));
}
Ok(())
}
/// Check header parameters agains parent header.
fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash {
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() })))
if !header.parent_hash().is_zero() && &parent.hash() != header.parent_hash() {
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash().clone() })))
}
if header.timestamp <= parent.timestamp {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
if header.timestamp() <= parent.timestamp() {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() })))
}
if header.number != parent.number + 1 {
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })));
if header.number() != parent.number() + 1 {
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() })));
}
Ok(())
}
@@ -307,9 +307,9 @@ mod tests {
self.blocks.get(hash).map(|bytes| {
let header = BlockView::new(bytes).header();
BlockDetails {
number: header.number,
total_difficulty: header.difficulty,
parent: header.parent_hash,
number: header.number(),
total_difficulty: header.difficulty().clone(),
parent: header.parent_hash().clone(),
children: Vec::new(),
}
})
@@ -352,9 +352,9 @@ mod tests {
let engine = &*spec.engine;
let min_gas_limit = engine.params().min_gas_limit;
good.gas_limit = min_gas_limit;
good.timestamp = 40;
good.number = 10;
good.set_gas_limit(min_gas_limit);
good.set_timestamp(40);
good.set_number(10);
let keypair = Random.generate().unwrap();
@@ -381,31 +381,31 @@ mod tests {
let diff_inc = U256::from(0x40);
let mut parent6 = good.clone();
parent6.number = 6;
parent6.set_number(6);
let mut parent7 = good.clone();
parent7.number = 7;
parent7.parent_hash = parent6.hash();
parent7.difficulty = parent6.difficulty + diff_inc;
parent7.timestamp = parent6.timestamp + 10;
parent7.set_number(7);
parent7.set_parent_hash(parent6.hash());
parent7.set_difficulty(parent6.difficulty().clone() + diff_inc);
parent7.set_timestamp(parent6.timestamp() + 10);
let mut parent8 = good.clone();
parent8.number = 8;
parent8.parent_hash = parent7.hash();
parent8.difficulty = parent7.difficulty + diff_inc;
parent8.timestamp = parent7.timestamp + 10;
parent8.set_number(8);
parent8.set_parent_hash(parent7.hash());
parent8.set_difficulty(parent7.difficulty().clone() + diff_inc);
parent8.set_timestamp(parent7.timestamp() + 10);
let mut good_uncle1 = good.clone();
good_uncle1.number = 9;
good_uncle1.parent_hash = parent8.hash();
good_uncle1.difficulty = parent8.difficulty + diff_inc;
good_uncle1.timestamp = parent8.timestamp + 10;
good_uncle1.extra_data.push(1u8);
good_uncle1.set_number(9);
good_uncle1.set_parent_hash(parent8.hash());
good_uncle1.set_difficulty(parent8.difficulty().clone() + diff_inc);
good_uncle1.set_timestamp(parent8.timestamp() + 10);
good_uncle1.extra_data_mut().push(1u8);
let mut good_uncle2 = good.clone();
good_uncle2.number = 8;
good_uncle2.parent_hash = parent7.hash();
good_uncle2.difficulty = parent7.difficulty + diff_inc;
good_uncle2.timestamp = parent7.timestamp + 10;
good_uncle2.extra_data.push(2u8);
good_uncle2.set_number(8);
good_uncle2.set_parent_hash(parent7.hash());
good_uncle2.set_difficulty(parent7.difficulty().clone() + diff_inc);
good_uncle2.set_timestamp(parent7.timestamp() + 10);
good_uncle2.extra_data_mut().push(2u8);
let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ];
let mut uncles_rlp = RlpStream::new();
@@ -414,14 +414,14 @@ mod tests {
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| encode::<SignedTransaction>(t).to_vec()).collect());
let mut parent = good.clone();
parent.number = 9;
parent.timestamp = parent8.timestamp + 10;
parent.parent_hash = parent8.hash();
parent.difficulty = parent8.difficulty + diff_inc;
parent.set_number(9);
parent.set_timestamp(parent8.timestamp() + 10);
parent.set_parent_hash(parent8.hash());
parent.set_difficulty(parent8.difficulty().clone() + diff_inc);
good.parent_hash = parent.hash();
good.difficulty = parent.difficulty + diff_inc;
good.timestamp = parent.timestamp + 10;
good.set_parent_hash(parent.hash());
good.set_difficulty(parent.difficulty().clone() + diff_inc);
good.set_timestamp(parent.timestamp() + 10);
let mut bc = TestBlockChain::new();
bc.insert(create_test_block(&good));
@@ -433,61 +433,62 @@ mod tests {
check_ok(basic_test(&create_test_block(&good), engine));
let mut header = good.clone();
header.transactions_root = good_transactions_root.clone();
header.uncles_hash = good_uncles_hash.clone();
header.set_transactions_root(good_transactions_root.clone());
header.set_uncles_hash(good_uncles_hash.clone());
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
header.gas_limit = min_gas_limit - From::from(1);
header.set_gas_limit(min_gas_limit - From::from(1));
check_fail(basic_test(&create_test_block(&header), engine),
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit().clone() }));
header = good.clone();
header.number = BlockNumber::max_value();
header.set_number(BlockNumber::max_value());
check_fail(basic_test(&create_test_block(&header), engine),
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number() }));
header = good.clone();
header.gas_used = header.gas_limit + From::from(1);
let gas_used = header.gas_limit().clone() + 1.into();
header.set_gas_used(gas_used);
check_fail(basic_test(&create_test_block(&header), engine),
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit().clone()), min: None, found: header.gas_used().clone() }));
header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
header.extra_data_mut().resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(basic_test(&create_test_block(&header), engine),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() }));
header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
header.extra_data_mut().resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(basic_test(&create_test_block(&header), engine),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data().len() }));
header = good.clone();
header.uncles_hash = good_uncles_hash.clone();
header.set_uncles_hash(good_uncles_hash.clone());
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root().clone() }));
header = good.clone();
header.transactions_root = good_transactions_root.clone();
header.set_transactions_root(good_transactions_root.clone());
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash().clone() }));
check_ok(family_test(&create_test_block(&good), engine, &bc));
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine, &bc));
header = good.clone();
header.parent_hash = H256::random();
header.set_parent_hash(H256::random());
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
UnknownParent(header.parent_hash));
UnknownParent(header.parent_hash().clone()));
header = good.clone();
header.timestamp = 10;
header.set_timestamp(10);
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp() + 1), found: header.timestamp() }));
header = good.clone();
header.number = 9;
header.set_number(9);
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }));
header = good.clone();
let mut bad_uncles = good_uncles.clone();