Merge remote-tracking branch 'parity/master' into auth-round

Conflicts:
	devtools/src/random_path.rs
This commit is contained in:
keorn
2016-09-27 12:39:25 +02:00
108 changed files with 2475 additions and 1206 deletions

View File

@@ -322,6 +322,26 @@ impl AccountProvider {
Ok(signature)
}
/// Decrypts a message. Account must be unlocked.
pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let data = {
let mut unlocked = self.unlocked.lock();
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
if let Unlock::Temp = data.unlock {
unlocked.remove(&account).expect("data exists: so key must exist: qed");
}
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
if start.elapsed() > Duration::from_millis(*duration as u64) {
unlocked.remove(&account).expect("data exists: so key must exist: qed");
return Err(Error::NotUnlocked);
}
}
data
};
Ok(try!(self.sstore.decrypt(&account, &data.password, shared_mac, message)))
}
/// Unlocks an account, signs the message, and locks it again.
pub fn sign_with_password(&self, account: Address, password: String, message: Message) -> Result<Signature, Error> {
let signature = try!(self.sstore.sign(&account, &password, &message));

View File

@@ -205,7 +205,6 @@ pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: Arc<LastHashes>,
unclosed_state: State,
}
/// Just like `ClosedBlock` except that we can't reopen it and it's faster.
@@ -343,18 +342,19 @@ impl<'x> OpenBlock<'x> {
}
}
/// Turn this into a `ClosedBlock`. A `BlockChain` must be provided in order to figure out the uncles.
/// Turn this into a `ClosedBlock`.
pub fn close(self) -> ClosedBlock {
let mut s = self;
let unclosed_state = s.block.state.clone();
// take a snapshot so the engine's changes can be rolled back.
s.block.state.snapshot();
s.engine.on_close_block(&mut s.block);
s.block.base.header.set_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())));
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.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_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
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));
@@ -362,33 +362,37 @@ impl<'x> OpenBlock<'x> {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
unclosed_state: unclosed_state,
}
}
/// Turn this into a `LockedBlock`. A BlockChain must be provided in order to figure out the uncles.
/// Turn this into a `LockedBlock`.
pub fn close_and_lock(self) -> LockedBlock {
let mut s = self;
// take a snapshot so the engine's changes can be rolled back.
s.block.state.snapshot();
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.set_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())));
}
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.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.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec()).collect()));
s.block.base.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
}
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 {
ClosedBlock {
block: s.block,
uncle_bytes: uncle_bytes,
}
last_hashes: s.last_hashes,
}.lock()
}
}
@@ -409,7 +413,17 @@ impl ClosedBlock {
pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) }
/// Turn this into a `LockedBlock`, unable to be reopened again.
pub fn lock(self) -> LockedBlock {
pub fn lock(mut self) -> LockedBlock {
// finalize the changes made by the engine.
self.block.state.clear_snapshot();
if let Err(e) = self.block.state.commit() {
warn!("Error committing closed block's state: {:?}", e);
}
// set the state root here, after commit recalculates with the block
// rewards.
self.block.base.header.set_state_root(self.block.state.root().clone());
LockedBlock {
block: self.block,
uncle_bytes: self.uncle_bytes,
@@ -417,12 +431,12 @@ impl ClosedBlock {
}
/// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`.
pub fn reopen(self, engine: &Engine) -> OpenBlock {
pub fn reopen(mut self, engine: &Engine) -> OpenBlock {
// revert rewards (i.e. set state back at last transaction's state).
let mut block = self.block;
block.state = self.unclosed_state;
self.block.state.revert_snapshot();
OpenBlock {
block: block,
block: self.block,
engine: engine,
last_hashes: self.last_hashes,
}

View File

@@ -17,14 +17,15 @@
use crypto::sha2::Sha256 as Sha256Digest;
use crypto::ripemd160::Ripemd160 as Ripemd160Digest;
use crypto::digest::Digest;
use util::*;
use std::cmp::min;
use util::{U256, H256, Hashable, FixedHash, BytesRef};
use ethkey::{Signature, recover as ec_recover};
use ethjson;
/// 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]);
fn execute(&self, input: &[u8], output: &mut BytesRef);
}
/// A gas pricing scheme for built-in contracts.
@@ -56,7 +57,7 @@ impl Builtin {
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.native.execute(input, output) }
pub fn execute(&self, input: &[u8], output: &mut BytesRef) { self.native.execute(input, output) }
}
impl From<ethjson::spec::Builtin> for Builtin {
@@ -108,14 +109,13 @@ struct Sha256;
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]);
fn execute(&self, input: &[u8], output: &mut BytesRef) {
output.write(0, input);
}
}
impl Impl for EcRecover {
fn execute(&self, i: &[u8], output: &mut [u8]) {
fn execute(&self, i: &[u8], output: &mut BytesRef) {
let len = min(i.len(), 128);
let mut input = [0; 128];
@@ -135,58 +135,34 @@ impl Impl for EcRecover {
if s.is_valid() {
if let Ok(p) = ec_recover(&s, &hash) {
let r = p.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]);
}
output.write(0, &[0; 12]);
output.write(12, &r[12..r.len()]);
}
}
}
}
impl Impl for Sha256 {
fn execute(&self, input: &[u8], output: &mut [u8]) {
let out_len = min(output.len(), 32);
fn execute(&self, input: &[u8], output: &mut BytesRef) {
let mut sha = Sha256Digest::new();
sha.input(input);
if out_len == 32 {
sha.result(&mut output[0..32]);
} else {
let mut out = [0; 32];
sha.result(&mut out);
let mut out = [0; 32];
sha.result(&mut out);
output.copy_from_slice(&out[..out_len])
}
output.write(0, &out);
}
}
impl Impl for Ripemd160 {
fn execute(&self, input: &[u8], output: &mut [u8]) {
let out_len = min(output.len(), 32);
fn execute(&self, input: &[u8], output: &mut BytesRef) {
let mut sha = Ripemd160Digest::new();
sha.input(input);
for x in &mut output[0.. min(12, out_len)] {
*x = 0;
}
let mut out = [0; 32];
sha.result(&mut out[12..32]);
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])
}
output.write(0, &out);
}
}
@@ -194,7 +170,7 @@ impl Impl for Ripemd160 {
mod tests {
use super::{Builtin, Linear, ethereum_builtin, Pricer};
use ethjson;
use util::U256;
use util::{U256, BytesRef};
#[test]
fn identity() {
@@ -203,15 +179,15 @@ mod tests {
let i = [0u8, 1, 2, 3];
let mut o2 = [255u8; 2];
f.execute(&i[..], &mut o2[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..]));
assert_eq!(i[0..2], o2);
let mut o4 = [255u8; 4];
f.execute(&i[..], &mut o4[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..]));
assert_eq!(i, o4);
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
assert_eq!(i, o8[..4]);
assert_eq!([255u8; 4], o8[4..]);
}
@@ -224,16 +200,20 @@ mod tests {
let i = [0u8; 0];
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]));
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
let mut ov = vec![];
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov));
assert_eq!(&ov[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
}
#[test]
@@ -244,15 +224,15 @@ mod tests {
let i = [0u8; 0];
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]));
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
}
@@ -272,46 +252,46 @@ mod tests {
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f.execute(&i[..], &mut o[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
let mut o8 = [255u8; 8];
f.execute(&i[..], &mut o8[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut o34[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&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[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
}
@@ -336,7 +316,7 @@ mod tests {
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
b.execute(&i[..], &mut o[..]);
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(i, o);
}
@@ -357,7 +337,7 @@ mod tests {
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
b.execute(&i[..], &mut o[..]);
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
assert_eq!(i, o);
}
}
}

View File

@@ -144,7 +144,9 @@ pub struct Client {
factories: Factories,
}
const HISTORY: u64 = 1200;
/// The pruning constant -- how old blocks must be before we
/// assume finality of a given candidate.
pub const HISTORY: u64 = 1200;
/// Append a path element to the given path and return the string.
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
@@ -168,7 +170,7 @@ impl Client {
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
let tracedb = RwLock::new(try!(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())));
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
let mut state_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
if state_db.is_empty() && try!(spec.ensure_db_good(state_db.as_hashdb_mut())) {
@@ -701,7 +703,7 @@ impl snapshot::DatabaseRestore for Client {
*state_db = journaldb::new(db.clone(), self.pruning, ::db::COL_STATE);
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
*tracedb = try!(TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()).map_err(ClientError::from));
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
Ok(())
}
}
@@ -973,7 +975,7 @@ impl BlockChainClient for Client {
}
}
fn logs(&self, filter: Filter, limit: Option<usize>) -> Vec<LocalizedLogEntry> {
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
let blocks = filter.bloom_possibilities().iter()
.filter_map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone()))
.flat_map(|m| m)
@@ -982,7 +984,7 @@ impl BlockChainClient for Client {
.into_iter()
.collect::<Vec<u64>>();
self.chain.read().logs(blocks, |entry| filter.matches(entry), limit)
self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)
}
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {

View File

@@ -18,7 +18,7 @@ use std::str::FromStr;
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch};
pub use trace::Config as TraceConfig;
pub use evm::VMType;
pub use verification::VerifierType;
use util::{journaldb, CompactionProfile};
@@ -102,7 +102,7 @@ pub struct ClientConfig {
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Should db have WAL enabled?
pub db_wal: bool,
pub db_wal: bool,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client.

View File

@@ -23,7 +23,7 @@ mod trace;
mod client;
pub use self::client::*;
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};

View File

@@ -67,6 +67,8 @@ pub struct TestBlockChainClient {
pub execution_result: RwLock<Option<Result<Executed, CallError>>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
/// Logs
pub logs: RwLock<Vec<LocalizedLogEntry>>,
/// Block queue size.
pub queue_size: AtomicUsize,
/// Miner
@@ -119,6 +121,7 @@ impl TestBlockChainClient {
code: RwLock::new(HashMap::new()),
execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
logs: RwLock::new(Vec::new()),
queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::with_spec(&spec)),
spec: spec,
@@ -170,6 +173,11 @@ impl TestBlockChainClient {
*self.latest_block_timestamp.write() = ts;
}
/// Set logs to return for each logs call.
pub fn set_logs(&self, logs: Vec<LocalizedLogEntry>) {
*self.logs.write() = logs;
}
/// Add blocks to test client.
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
let len = self.numbers.read().len();
@@ -395,8 +403,13 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn logs(&self, _filter: Filter, _limit: Option<usize>) -> Vec<LocalizedLogEntry> {
Vec::new()
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
let mut logs = self.logs.read().clone();
let len = logs.len();
match filter.limit {
Some(limit) if limit <= len => logs.split_off(len - limit),
_ => logs,
}
}
fn last_hashes(&self) -> LastHashes {

View File

@@ -156,7 +156,7 @@ pub trait BlockChainClient : Sync + Send {
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
/// Returns logs matching given filter.
fn logs(&self, filter: Filter, limit: Option<usize>) -> Vec<LocalizedLogEntry>;
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
/// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError>;
@@ -215,8 +215,11 @@ pub trait BlockChainClient : Sync + Send {
/// Extended client interface used for mining
pub trait MiningBlockChainClient : BlockChainClient {
/// Returns OpenBlock prepared for closing.
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
-> OpenBlock;
fn prepare_open_block(&self,
author: Address,
gas_range_target: (U256, U256),
extra_data: Bytes
) -> OpenBlock;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;

View File

@@ -49,7 +49,7 @@ pub trait Engine : Sync + Send {
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
/// Additional information.
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
/// Get the general parameters of the chain.
fn params(&self) -> &CommonParams;
@@ -130,7 +130,7 @@ pub trait Engine : Sync + Send {
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
/// Execution the builtin contract `a` on `input` and return `output`.
/// Panics if `is_builtin(a)` is not true.
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.builtins().get(a).unwrap().execute(input, output); }
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) { self.builtins().get(a).unwrap().execute(input, output); }
/// Add a channel for communication with Client.
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {}

View File

@@ -167,9 +167,7 @@ impl Engine for Ethash {
for u in fields.uncles.iter() {
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
}
if let Err(e) = fields.state.commit() {
warn!("Encountered error on state commit: {}", e);
}
}
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {

View File

@@ -113,7 +113,10 @@ impl<'a> Finalize for Result<GasLeft<'a>> {
}
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self> + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + cmp::Ord + Sized + From<usize> + Copy {
pub trait CostType: Sized + From<usize> + Copy
+ ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> +ops::Sub<Output=Self>
+ ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
+ cmp::Ord + fmt::Debug {
/// Converts this cost into `U256`
fn as_u256(&self) -> U256;
/// Tries to fit `U256` into this `Cost` type

View File

@@ -83,6 +83,9 @@ pub trait Ext {
/// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes;
/// Returns code size at given address
fn extcodesize(&self, address: &Address) -> usize;
/// Creates log entry with given topics and data
fn log(&mut self, topics: Vec<H256>, data: &[u8]);

View File

@@ -53,6 +53,17 @@ fn color(instruction: Instruction, name: &'static str) -> String {
type CodePosition = usize;
type ProgramCounter = usize;
const ONE: U256 = U256([1, 0, 0, 0]);
const TWO: U256 = U256([2, 0, 0, 0]);
const TWO_POW_5: U256 = U256([0x20, 0, 0, 0]);
const TWO_POW_8: U256 = U256([0x100, 0, 0, 0]);
const TWO_POW_16: U256 = U256([0x10000, 0, 0, 0]);
const TWO_POW_24: U256 = U256([0x1000000, 0, 0, 0]);
const TWO_POW_64: U256 = U256([0, 0x1, 0, 0]); // 0x1 00000000 00000000
const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 00000000
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
/// Abstraction over raw vector of Bytes. Easier state management of PC.
struct CodeReader<'a> {
position: ProgramCounter,
@@ -126,7 +137,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
gasometer.current_gas = gasometer.current_gas - gas_cost;
evm_debug!({
println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}",
println!("[0x{:x}][{}(0x{:x}) Gas: {:?}\n Gas Before: {:?}",
reader.position,
color(instruction, info.name),
instruction,
@@ -471,7 +482,7 @@ impl<Cost: CostType> Interpreter<Cost> {
},
instructions::EXTCODESIZE => {
let address = u256_to_address(&stack.pop_back());
let len = ext.extcode(&address).len();
let len = ext.extcodesize(&address);
stack.push(U256::from(len));
},
instructions::CALLDATACOPY => {
@@ -599,7 +610,19 @@ impl<Cost: CostType> Interpreter<Cost> {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(if !self.is_zero(&b) {
a.overflowing_div(b).0
match b {
ONE => a,
TWO => a >> 1,
TWO_POW_5 => a >> 5,
TWO_POW_8 => a >> 8,
TWO_POW_16 => a >> 16,
TWO_POW_24 => a >> 24,
TWO_POW_64 => a >> 64,
TWO_POW_96 => a >> 96,
TWO_POW_224 => a >> 224,
TWO_POW_248 => a >> 248,
_ => a.overflowing_div(b).0,
}
} else {
U256::zero()
});

View File

@@ -18,6 +18,7 @@
use common::*;
use evmjit;
use evm::{self, GasLeft};
use types::executed::CallType;
/// Should be used to convert jit types to ethcore
trait FromJit<T>: Sized {
@@ -77,10 +78,11 @@ impl IntoJit<evmjit::I256> for U256 {
impl IntoJit<evmjit::I256> for H256 {
fn into_jit(self) -> evmjit::I256 {
let mut ret = [0; 4];
for i in 0..self.bytes().len() {
let rev = self.bytes().len() - 1 - i;
let len = self.len();
for i in 0..len {
let rev = len - 1 - i;
let pos = rev / 8;
ret[pos] += (self.bytes()[i] as u64) << ((rev % 8) * 8);
ret[pos] += (self[i] as u64) << ((rev % 8) * 8);
}
evmjit::I256 { words: ret }
}
@@ -206,6 +208,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
let sender_address = unsafe { Address::from_jit(&*sender_address) };
let receive_address = unsafe { Address::from_jit(&*receive_address) };
let code_address = unsafe { Address::from_jit(&*code_address) };
// TODO Is it always safe in case of DELEGATE_CALL?
let transfer_value = unsafe { U256::from_jit(&*transfer_value) };
let value = Some(transfer_value);
@@ -239,6 +242,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
}
}
// TODO [ToDr] Any way to detect DelegateCall?
let call_type = match is_callcode {
true => CallType::CallCode,
false => CallType::Call,
};
match self.ext.call(
&call_gas,
&sender_address,
@@ -246,7 +255,9 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
value,
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
&code_address,
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) },
call_type,
) {
evm::MessageCallResult::Success(gas_left) => unsafe {
*io_gas = (gas + gas_left).low_u64();
true

View File

@@ -140,6 +140,10 @@ impl Ext for FakeExt {
self.codes.get(address).unwrap_or(&Bytes::new()).clone()
}
fn extcodesize(&self, address: &Address) -> usize {
self.codes.get(address).map(|v| v.len()).unwrap_or(0)
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
self.logs.push(FakeLogEntry {
topics: topics,

View File

@@ -193,7 +193,6 @@ impl<'a> Executive<'a> {
data: Some(t.data.clone()),
call_type: CallType::Call,
};
// TODO: move output upstream
let mut out = vec![];
(self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out)
}

View File

@@ -205,6 +205,11 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
self.state.code(address).unwrap_or_else(|| vec![])
}
fn extcodesize(&self, address: &Address) -> usize {
self.state.code_size(address).unwrap_or(0)
}
#[cfg_attr(feature="dev", allow(match_ref_pats))]
fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result<U256>
where Self: Sized {

View File

@@ -131,6 +131,10 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
self.ext.extcode(address)
}
fn extcodesize(&self, address: &Address) -> usize {
self.ext.extcodesize(address)
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
self.ext.log(topics, data)
}

View File

@@ -297,6 +297,7 @@ impl Miner {
};
let mut invalid_transactions = HashSet::new();
let mut transactions_to_penalize = HashSet::new();
let block_number = open_block.block().fields().header.number();
// TODO: push new uncles, too.
for tx in transactions {
@@ -304,6 +305,12 @@ impl Miner {
match open_block.push_transaction(tx, None) {
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) => {
debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas);
// Penalize transaction if it's above current gas limit
if gas > gas_limit {
transactions_to_penalize.insert(hash);
}
// Exit early if gas left is smaller then min_tx_gas
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas {
@@ -339,6 +346,9 @@ impl Miner {
for hash in invalid_transactions.into_iter() {
queue.remove_invalid(&hash, &fetch_account);
}
for hash in transactions_to_penalize {
queue.penalize(&hash);
}
}
(block, original_work_hash)
}

View File

@@ -134,6 +134,8 @@ struct TransactionOrder {
hash: H256,
/// Origin of the transaction
origin: TransactionOrigin,
/// Penalties
penalties: usize,
}
@@ -144,6 +146,7 @@ impl TransactionOrder {
gas_price: tx.transaction.gas_price,
hash: tx.hash(),
origin: tx.origin,
penalties: 0,
}
}
@@ -151,6 +154,11 @@ impl TransactionOrder {
self.nonce_height = nonce - base_nonce;
self
}
fn penalize(mut self) -> Self {
self.penalties = self.penalties.saturating_add(1);
self
}
}
impl Eq for TransactionOrder {}
@@ -167,6 +175,11 @@ impl PartialOrd for TransactionOrder {
impl Ord for TransactionOrder {
fn cmp(&self, b: &TransactionOrder) -> Ordering {
// First check number of penalties
if self.penalties != b.penalties {
return self.penalties.cmp(&b.penalties);
}
// First check nonce_height
if self.nonce_height != b.nonce_height {
return self.nonce_height.cmp(&b.nonce_height);
@@ -387,7 +400,7 @@ pub struct AccountDetails {
}
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
const GAS_LIMIT_HYSTERESIS: usize = 10; // %
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
/// `TransactionQueue` implementation
pub struct TransactionQueue {
@@ -506,8 +519,6 @@ impl TransactionQueue {
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails {
trace!(target: "txqueue", "Importing: {:?}", tx.hash());
if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local {
trace!(target: "txqueue",
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
@@ -593,6 +604,39 @@ impl TransactionQueue {
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
}
/// Penalize transactions from sender of transaction with given hash.
/// I.e. it should change the priority of the transaction in the queue.
///
/// NOTE: We need to penalize all transactions from particular sender
/// to avoid breaking invariants in queue (ordered by nonces).
/// Consecutive transactions from this sender would fail otherwise (because of invalid nonce).
pub fn penalize(&mut self, transaction_hash: &H256) {
let transaction = match self.by_hash.get(transaction_hash) {
None => return,
Some(t) => t,
};
let sender = transaction.sender();
// Penalize all transactions from this sender
let nonces_from_sender = match self.current.by_address.row(&sender) {
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
None => vec![],
};
for k in nonces_from_sender {
let order = self.current.drop(&sender, &k).unwrap();
self.current.insert(sender, k, order.penalize());
}
// Same thing for future
let nonces_from_sender = match self.future.by_address.row(&sender) {
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
None => vec![],
};
for k in nonces_from_sender {
let order = self.future.drop(&sender, &k).unwrap();
self.current.insert(sender, k, order.penalize());
}
}
/// Removes invalid transaction identified by hash from queue.
/// Assumption is that this transaction nonce is not related to client nonce,
/// so transactions left in queue are processed according to client nonce.
@@ -764,6 +808,7 @@ impl TransactionQueue {
let address = tx.sender();
let nonce = tx.nonce();
let hash = tx.hash();
let next_nonce = self.last_nonces
.get(&address)
@@ -785,6 +830,9 @@ impl TransactionQueue {
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash)));
// Return an error if this transaction is not imported because of limit.
try!(check_if_removed(&address, &nonce, self.future.enforce_limit(&mut self.by_hash)));
debug!(target: "txqueue", "Importing transaction to future: {:?}", hash);
debug!(target: "txqueue", "status: {:?}", self.status());
return Ok(TransactionImportResult::Future);
}
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
@@ -811,7 +859,8 @@ impl TransactionQueue {
// Trigger error if the transaction we are importing was removed.
try!(check_if_removed(&address, &nonce, removed));
trace!(target: "txqueue", "status: {:?}", self.status());
debug!(target: "txqueue", "Imported transaction to current: {:?}", hash);
debug!(target: "txqueue", "status: {:?}", self.status());
Ok(TransactionImportResult::Current)
}
@@ -945,6 +994,17 @@ mod test {
(tx1.sign(secret), tx2.sign(secret))
}
/// Returns two consecutive transactions, both with increased gas price
fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
let gas = default_gas_price() + gas_price_increment;
let tx1 = new_unsigned_tx(default_nonce(), gas);
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), gas);
let keypair = Random.generate().unwrap();
let secret = &keypair.secret();
(tx1.sign(secret), tx2.sign(secret))
}
fn new_tx_pair_default(nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
new_tx_pair(default_nonce(), default_gas_price(), nonce_increment, gas_price_increment)
}
@@ -1332,6 +1392,39 @@ mod test {
assert_eq!(top.len(), 2);
}
#[test]
fn should_penalize_transactions_from_sender() {
// given
let mut txq = TransactionQueue::new();
// txa, txb - slightly bigger gas price to have consistent ordering
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
// insert everything
txq.add(txa.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(txb.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
let top = txq.top_transactions();
assert_eq!(top[0], tx1);
assert_eq!(top[1], txa);
assert_eq!(top[2], tx2);
assert_eq!(top[3], txb);
assert_eq!(top.len(), 4);
// when
txq.penalize(&tx1.hash());
// then
let top = txq.top_transactions();
assert_eq!(top[0], txa);
assert_eq!(top[1], txb);
assert_eq!(top[2], tx1);
assert_eq!(top[3], tx2);
assert_eq!(top.len(), 4);
}
#[test]
fn should_return_pending_hashes() {
// given

View File

@@ -92,7 +92,8 @@ impl Account {
let mut pairs = Vec::new();
for (k, v) in db.iter() {
for item in try!(db.iter()) {
let (k, v) = try!(item);
pairs.push((k, v));
}

View File

@@ -21,10 +21,10 @@ use header::Header;
use views::BlockView;
use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View};
use rlp::{Compressible, RlpType};
use util::{Bytes, Hashable, H256};
use util::triehash::ordered_trie_root;
const HEADER_FIELDS: usize = 10;
const HEADER_FIELDS: usize = 8;
const BLOCK_FIELDS: usize = 2;
pub struct AbridgedBlock {
@@ -61,8 +61,6 @@ impl AbridgedBlock {
stream
.append(&header.author())
.append(&header.state_root())
.append(&header.transactions_root())
.append(&header.receipts_root())
.append(&header.log_bloom())
.append(&header.difficulty())
.append(&header.gas_limit())
@@ -79,33 +77,35 @@ impl AbridgedBlock {
}
AbridgedBlock {
rlp: UntrustedRlp::new(stream.as_raw()).compress(RlpType::Blocks).to_vec(),
rlp: stream.out(),
}
}
/// Flesh out an abridged block view with the provided parent hash and block number.
///
/// Will fail if contains invalid rlp.
pub fn to_block(&self, parent_hash: H256, number: u64) -> Result<Block, DecoderError> {
let rlp = UntrustedRlp::new(&self.rlp).decompress(RlpType::Blocks);
let rlp = UntrustedRlp::new(&rlp);
pub fn to_block(&self, parent_hash: H256, number: u64, receipts_root: H256) -> Result<Block, DecoderError> {
let rlp = UntrustedRlp::new(&self.rlp);
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_log_bloom(try!(rlp.val_at(2)));
header.set_difficulty(try!(rlp.val_at(3)));
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)));
header.set_gas_limit(try!(rlp.val_at(4)));
header.set_gas_used(try!(rlp.val_at(5)));
header.set_timestamp(try!(rlp.val_at(6)));
header.set_extra_data(try!(rlp.val_at(7)));
let transactions = try!(rlp.val_at(10));
let uncles: Vec<Header> = try!(rlp.val_at(11));
let transactions = try!(rlp.val_at(8));
let uncles: Vec<Header> = try!(rlp.val_at(9));
header.set_transactions_root(ordered_trie_root(
try!(rlp.at(8)).iter().map(|r| r.as_raw().to_owned())
));
header.set_receipts_root(receipts_root);
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append(&uncles);
@@ -143,20 +143,22 @@ mod tests {
#[test]
fn empty_block_abridging() {
let b = Block::default();
let receipts_root = b.header.receipts_root().clone();
let encoded = encode_block(&b);
let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded));
assert_eq!(abridged.to_block(H256::new(), 0).unwrap(), b);
assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b);
}
#[test]
#[should_panic]
fn wrong_number() {
let b = Block::default();
let receipts_root = b.header.receipts_root().clone();
let encoded = encode_block(&b);
let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded));
assert_eq!(abridged.to_block(H256::new(), 2).unwrap(), b);
assert_eq!(abridged.to_block(H256::new(), 2, receipts_root).unwrap(), b);
}
#[test]
@@ -184,9 +186,14 @@ mod tests {
b.transactions.push(t1);
b.transactions.push(t2);
let receipts_root = b.header.receipts_root().clone();
b.header.set_transactions_root(::util::triehash::ordered_trie_root(
b.transactions.iter().map(::rlp::encode).map(|out| out.to_vec())
));
let encoded = encode_block(&b);
let abridged = AbridgedBlock::from_block_view(&BlockView::new(&encoded[..]));
assert_eq!(abridged.to_block(H256::new(), 0).unwrap(), b);
assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b);
}
}

View File

@@ -15,6 +15,9 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Snapshot creation, restoration, and network service.
//!
//! Documentation of the format can be found at
//! https://github.com/ethcore/parity/wiki/%22PV64%22-Snapshot-Format
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::Arc;
@@ -34,7 +37,7 @@ use util::journaldb::{self, Algorithm, JournalDB};
use util::kvdb::Database;
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
use util::sha3::SHA3_NULL_RLP;
use rlp::{RlpStream, Stream, UntrustedRlp, View, Compressible, RlpType};
use rlp::{RlpStream, Stream, UntrustedRlp, View};
use self::account::Account;
use self::block::AbridgedBlock;
@@ -358,15 +361,15 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
let mut used_code = HashSet::new();
// account_key here is the address' hash.
for (account_key, account_data) in account_trie.iter() {
for item in try!(account_trie.iter()) {
let (account_key, account_data) = try!(item);
let account = Account::from_thin_rlp(account_data);
let account_key_hash = H256::from_slice(&account_key);
let account_db = AccountDB::from_hash(db, account_key_hash);
let fat_rlp = try!(account.to_fat_rlp(&account_db, &mut used_code));
let compressed_rlp = UntrustedRlp::new(&fat_rlp).compress(RlpType::Snapshot).to_vec();
try!(chunker.push(account_key, compressed_rlp));
try!(chunker.push(account_key, fat_rlp));
}
if chunker.cur_size != 0 {
@@ -507,8 +510,7 @@ fn rebuild_accounts(
let account_rlp = UntrustedRlp::new(account_pair);
let hash: H256 = try!(account_rlp.val_at(0));
let decompressed = try!(account_rlp.at(1)).decompress(RlpType::Snapshot);
let fat_rlp = UntrustedRlp::new(&decompressed[..]);
let fat_rlp = try!(account_rlp.at(1));
let thin_rlp = {
let mut acct_db = AccountDBMut::from_hash(db, hash);
@@ -569,6 +571,7 @@ impl BlockRebuilder {
pub fn feed(&mut self, chunk: &[u8], engine: &Engine) -> Result<u64, ::error::Error> {
use basic_types::Seal::With;
use util::U256;
use util::triehash::ordered_trie_root;
let rlp = UntrustedRlp::new(chunk);
let item_count = rlp.item_count();
@@ -585,7 +588,11 @@ impl BlockRebuilder {
let abridged_rlp = try!(pair.at(0)).as_raw().to_owned();
let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
let receipts: Vec<::receipt::Receipt> = try!(pair.val_at(1));
let block = try!(abridged_block.to_block(parent_hash, cur_number));
let receipts_root = ordered_trie_root(
try!(pair.at(1)).iter().map(|r| r.as_raw().to_owned())
);
let block = try!(abridged_block.to_block(parent_hash, cur_number, receipts_root));
let block_bytes = block.rlp_bytes(With);
if self.rng.gen::<f32>() <= POW_VERIFY_RATE {

View File

@@ -27,7 +27,7 @@ use super::{ManifestData, StateRebuilder, BlockRebuilder, RestorationStatus, Sna
use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter};
use blockchain::BlockChain;
use client::Client;
use client::{BlockChainClient, Client};
use engines::Engine;
use error::Error;
use ids::BlockID;
@@ -345,7 +345,17 @@ impl Service {
let res = client.take_snapshot(writer, BlockID::Number(num), &self.progress);
self.taking_snapshot.store(false, Ordering::SeqCst);
try!(res);
if let Err(e) = res {
if client.chain_info().best_block_number >= num + ::client::HISTORY {
// "Cancelled" is mincing words a bit -- what really happened
// is that the state we were snapshotting got pruned out
// before we could finish.
info!("Cancelled prematurely-started periodic snapshot.");
return Ok(())
} else {
return Err(e);
}
}
info!("Finished taking snapshot at #{}", num);

View File

@@ -52,8 +52,9 @@ impl StateProducer {
// modify existing accounts.
let mut accounts_to_modify: Vec<_> = {
let trie = TrieDB::new(&*db, &self.state_root).unwrap();
let temp = trie.iter() // binding required due to complicated lifetime stuff
let temp = trie.iter().unwrap() // binding required due to complicated lifetime stuff
.filter(|_| rng.gen::<f32>() < ACCOUNT_CHURN)
.map(Result::unwrap)
.map(|(k, v)| (H256::from_slice(&k), v.to_owned()))
.collect();

View File

@@ -191,10 +191,16 @@ impl State {
}))
}
/// Mutate storage of account `a` so that it is `value` for `key`.
/// Get the code of account `a`.
pub fn code(&self, a: &Address) -> Option<Bytes> {
self.ensure_cached(a, true,
|a| a.as_ref().map_or(None, |a|a.code().map(|x|x.to_vec())))
|a| a.as_ref().map_or(None, |a| a.code().map(|x| x.to_vec())))
}
/// Get the code size of account `a`.
pub fn code_size(&self, a: &Address) -> Option<usize> {
self.ensure_cached(a, true,
|a| a.as_ref().map_or(None, |a| a.code().map(|x| x.len())))
}
/// Add `incr` to the balance of account `a`.
@@ -420,10 +426,27 @@ impl fmt::Debug for State {
impl Clone for State {
fn clone(&self) -> State {
let cache = {
let mut cache = HashMap::new();
for (key, val) in self.cache.borrow().iter() {
let key = key.clone();
match *val {
Some(ref acc) if acc.is_dirty() => {
cache.insert(key, Some(acc.clone()));
},
None => {
cache.insert(key, None);
},
_ => {},
}
}
cache
};
State {
db: self.db.boxed_clone(),
root: self.root.clone(),
cache: RefCell::new(self.cache.borrow().clone()),
cache: RefCell::new(cache),
snapshots: RefCell::new(self.snapshots.borrow().clone()),
account_start_nonce: self.account_start_nonce.clone(),
factories: self.factories.clone(),

View File

@@ -142,7 +142,8 @@ fn returns_logs() {
to_block: BlockID::Latest,
address: None,
topics: vec![],
}, None);
limit: None,
});
assert_eq!(logs.len(), 0);
}
@@ -156,7 +157,8 @@ fn returns_logs_with_limit() {
to_block: BlockID::Latest,
address: None,
topics: vec![],
}, Some(2));
limit: Some(2),
});
assert_eq!(logs.len(), 0);
}

View File

@@ -15,57 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Traces config.
use std::str::FromStr;
use bloomchain::Config as BloomConfig;
use trace::Error;
/// 3-value enum.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Switch {
/// True.
On,
/// False.
Off,
/// Auto.
Auto,
}
impl Default for Switch {
fn default() -> Self {
Switch::Auto
}
}
impl FromStr for Switch {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"on" => Ok(Switch::On),
"off" => Ok(Switch::Off),
"auto" => Ok(Switch::Auto),
other => Err(format!("Invalid switch value: {}", other))
}
}
}
impl Switch {
/// Tries to turn old switch to new value.
pub fn turn_to(&self, to: Switch) -> Result<bool, Error> {
match (*self, to) {
(Switch::On, Switch::On) | (Switch::On, Switch::Auto) | (Switch::Auto, Switch::On) => Ok(true),
(Switch::Off, Switch::On) => Err(Error::ResyncRequired),
_ => Ok(false),
}
}
}
/// Traces config.
#[derive(Debug, PartialEq, Clone)]
pub struct Config {
/// Indicates if tracing should be enabled or not.
/// If it's None, it will be automatically configured.
pub enabled: Switch,
pub enabled: bool,
/// Traces blooms configuration.
pub blooms: BloomConfig,
/// Preferef cache-size.
@@ -77,7 +34,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Config {
enabled: Switch::default(),
enabled: false,
blooms: BloomConfig {
levels: 3,
elements_per_index: 16,
@@ -87,20 +44,3 @@ impl Default for Config {
}
}
}
#[cfg(test)]
mod tests {
use super::Switch;
#[test]
fn test_switch_parsing() {
assert_eq!(Switch::On, "on".parse().unwrap());
assert_eq!(Switch::Off, "off".parse().unwrap());
assert_eq!(Switch::Auto, "auto".parse().unwrap());
}
#[test]
fn test_switch_default() {
assert_eq!(Switch::default(), Switch::Auto);
}
}

View File

@@ -22,7 +22,7 @@ use bloomchain::{Number, Config as BloomConfig};
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
use util::{H256, H264, Database, DBTransaction, RwLock, HeapSizeOf};
use header::BlockNumber;
use trace::{LocalizedTrace, Config, Switch, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras, Error};
use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras};
use db::{self, Key, Writable, Readable, CacheUpdatePolicy};
use blooms;
use super::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
@@ -126,38 +126,20 @@ impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
impl<T> TraceDB<T> where T: DatabaseExtras {
/// Creates new instance of `TraceDB`.
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Result<Self, Error> {
// check if in previously tracing was enabled
let old_tracing = match tracesdb.get(db::COL_TRACE, b"enabled").unwrap() {
Some(ref value) if value as &[u8] == &[0x1] => Switch::On,
Some(ref value) if value as &[u8] == &[0x0] => Switch::Off,
Some(_) => { panic!("tracesdb is corrupted") },
None => Switch::Auto,
};
let enabled = try!(old_tracing.turn_to(config.enabled));
let encoded_tracing = match enabled {
true => [0x1],
false => [0x0]
};
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Self {
let mut batch = DBTransaction::new(&tracesdb);
batch.put(db::COL_TRACE, b"enabled", &encoded_tracing);
batch.put(db::COL_TRACE, b"version", TRACE_DB_VER);
tracesdb.write(batch).unwrap();
let db = TraceDB {
TraceDB {
traces: RwLock::new(HashMap::new()),
blooms: RwLock::new(HashMap::new()),
cache_manager: RwLock::new(CacheManager::new(config.pref_cache_size, config.max_cache_size, 10 * 1024)),
tracesdb: tracesdb,
bloom_config: config.blooms,
enabled: enabled,
enabled: config.enabled,
extras: extras,
};
Ok(db)
}
}
fn cache_size(&self) -> usize {
@@ -419,7 +401,7 @@ mod tests {
use util::{Address, U256, H256, Database, DatabaseConfig, DBTransaction};
use devtools::RandomTempPath;
use header::BlockNumber;
use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
use trace::{Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError};
use trace::trace::{Call, Action, Res};
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
@@ -474,22 +456,10 @@ mod tests {
let mut config = Config::default();
// set autotracing
config.enabled = Switch::Auto;
config.enabled = false;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras));
assert_eq!(tracedb.tracing_enabled(), false);
}
}
@@ -501,50 +471,12 @@ mod tests {
let mut config = Config::default();
// set tracing on
config.enabled = Switch::On;
config.enabled = true;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras));
assert_eq!(tracedb.tracing_enabled(), true);
}
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::Auto;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), false);
}
}
#[test]
#[should_panic]
fn test_invalid_reopening_db() {
let temp = RandomTempPath::new();
let db = new_db(temp.as_str());
let mut config = Config::default();
// set tracing on
config.enabled = Switch::Off;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap();
assert_eq!(tracedb.tracing_enabled(), true);
}
config.enabled = Switch::On;
TraceDB::new(config.clone(), db.clone(), Arc::new(NoopExtras)).unwrap(); // should panic!
}
fn create_simple_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
@@ -595,7 +527,7 @@ mod tests {
let temp = RandomTempPath::new();
let db = Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), temp.as_str()).unwrap());
let mut config = Config::default();
config.enabled = Switch::On;
config.enabled = true;
let block_0 = H256::from(0xa1);
let block_1 = H256::from(0xa2);
let tx_0 = H256::from(0xff);
@@ -607,7 +539,7 @@ mod tests {
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras)).unwrap();
let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras));
// import block 0
let request = create_simple_import_request(0, block_0.clone());
@@ -679,10 +611,10 @@ mod tests {
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
// set tracing on
config.enabled = Switch::On;
config.enabled = true;
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone())).unwrap();
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone()));
// import block 0
let request = create_simple_import_request(0, block_0.clone());
@@ -692,7 +624,7 @@ mod tests {
}
{
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras)).unwrap();
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras));
let traces = tracedb.transaction_traces(0, 0);
assert_eq!(traces.unwrap(), vec![create_simple_localized_trace(0, block_0, tx_0)]);
}

View File

@@ -26,7 +26,7 @@ mod noop_tracer;
pub use types::trace_types::{filter, flat, localized, trace};
pub use types::trace_types::error::Error as TraceError;
pub use self::config::{Config, Switch};
pub use self::config::Config;
pub use self::db::TraceDB;
pub use self::error::Error;
pub use types::trace_types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};

View File

@@ -41,6 +41,12 @@ pub struct Filter {
/// If None, match all.
/// If specified, log must contain one of these topics.
pub topics: Vec<Option<Vec<H256>>>,
/// Logs limit
///
/// If None, return all logs
/// If specified, should only return *last* `n` logs.
pub limit: Option<usize>,
}
impl Clone for Filter {
@@ -59,7 +65,8 @@ impl Clone for Filter {
from_block: self.from_block.clone(),
to_block: self.to_block.clone(),
address: self.address.clone(),
topics: topics[..].to_vec()
topics: topics[..].to_vec(),
limit: self.limit,
}
}
}
@@ -117,6 +124,7 @@ mod tests {
to_block: BlockID::Latest,
address: None,
topics: vec![None, None, None, None],
limit: None,
};
let possibilities = none_filter.bloom_possibilities();
@@ -136,7 +144,8 @@ mod tests {
None,
None,
None,
]
],
limit: None,
};
let possibilities = filter.bloom_possibilities();
@@ -154,7 +163,8 @@ mod tests {
Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]),
None,
None,
]
],
limit: None,
};
let possibilities = filter.bloom_possibilities();
@@ -181,7 +191,8 @@ mod tests {
]),
Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]),
None
]
],
limit: None,
};
// number of possibilites should be equal 2 * 2 * 2 * 1 = 8
@@ -201,7 +212,8 @@ mod tests {
Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into()]),
None,
None,
]
],
limit: None,
};
let entry0 = LogEntry {

View File

@@ -21,7 +21,7 @@ use std::cell::*;
use rlp::*;
use util::sha3::Hashable;
use util::{H256, Address, U256, Bytes};
use ethkey::{Signature, sign, Secret, recover, public_to_address, Error as EthkeyError};
use ethkey::{Signature, sign, Secret, Public, recover, public_to_address, Error as EthkeyError};
use error::*;
use evm::Schedule;
use header::BlockNumber;
@@ -305,13 +305,18 @@ impl SignedTransaction {
match sender {
Some(s) => Ok(s),
None => {
let s = public_to_address(&try!(recover(&self.signature(), &self.unsigned.hash())));
let s = public_to_address(&try!(self.public_key()));
self.sender.set(Some(s));
Ok(s)
}
}
}
/// Returns the public key of the sender.
pub fn public_key(&self) -> Result<Public, Error> {
Ok(try!(recover(&self.signature(), &self.unsigned.hash())))
}
/// Do basic validation, checking for valid signature and minimum gas,
// TODO: consider use in block validation.
#[cfg(test)]

View File

@@ -215,7 +215,7 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> {
let block = UntrustedRlp::new(block);
let tx = try!(block.at(1));
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec())); //TODO: get rid of vectors here
if expected_root != transactions_root {
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
}
@@ -422,7 +422,7 @@ mod tests {
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append(&good_uncles);
let good_uncles_hash = uncles_rlp.as_raw().sha3();
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::<SignedTransaction>(t).to_vec()).collect());
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::<SignedTransaction>(t).to_vec()));
let mut parent = good.clone();
parent.set_number(9);