Merge remote-tracking branch 'parity/master' into bft
Conflicts: ethcore/src/error.rs
This commit is contained in:
@@ -35,6 +35,38 @@ fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
|
||||
dst
|
||||
}
|
||||
|
||||
/// A factory for different kinds of account dbs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Factory {
|
||||
/// Mangle hashes based on address.
|
||||
Mangled,
|
||||
/// Don't mangle hashes.
|
||||
Plain,
|
||||
}
|
||||
|
||||
impl Default for Factory {
|
||||
fn default() -> Self { Factory::Mangled }
|
||||
}
|
||||
|
||||
impl Factory {
|
||||
/// Create a read-only accountdb.
|
||||
/// This will panic when write operations are called.
|
||||
pub fn readonly<'db>(&self, db: &'db HashDB, address_hash: H256) -> Box<HashDB + 'db> {
|
||||
match *self {
|
||||
Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)),
|
||||
Factory::Plain => Box::new(Wrapping(db)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new mutable hashdb.
|
||||
pub fn create<'db>(&self, db: &'db mut HashDB, address_hash: H256) -> Box<HashDB + 'db> {
|
||||
match *self {
|
||||
Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)),
|
||||
Factory::Plain => Box::new(WrappingMut(db)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: introduce HashDBMut?
|
||||
/// DB backend wrapper for Account trie
|
||||
/// Transforms trie node keys for the database
|
||||
@@ -162,4 +194,79 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
}
|
||||
}
|
||||
|
||||
struct Wrapping<'db>(&'db HashDB);
|
||||
|
||||
impl<'db> HashDB for Wrapping<'db> {
|
||||
fn keys(&self) -> HashMap<H256, i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
}
|
||||
self.0.get(key)
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H256) -> bool {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return true;
|
||||
}
|
||||
self.0.contains(key)
|
||||
}
|
||||
|
||||
fn insert(&mut self, _value: &[u8]) -> H256 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn emplace(&mut self, _key: H256, _value: Bytes) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remove(&mut self, _key: &H256) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappingMut<'db>(&'db mut HashDB);
|
||||
|
||||
impl<'db> HashDB for WrappingMut<'db>{
|
||||
fn keys(&self) -> HashMap<H256, i32> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
}
|
||||
self.0.get(key)
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H256) -> bool {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return true;
|
||||
}
|
||||
self.0.contains(key)
|
||||
}
|
||||
|
||||
fn insert(&mut self, value: &[u8]) -> H256 {
|
||||
if value == &NULL_RLP {
|
||||
return SHA3_NULL_RLP.clone();
|
||||
}
|
||||
self.0.insert(value)
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
||||
if key == SHA3_NULL_RLP {
|
||||
return;
|
||||
}
|
||||
self.0.emplace(key, value)
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &H256) {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return;
|
||||
}
|
||||
self.0.remove(key)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ use engines::Engine;
|
||||
use state::*;
|
||||
use verification::PreverifiedBlock;
|
||||
use trace::FlatTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
use factory::Factories;
|
||||
|
||||
/// A block, encoded as it is on the block chain.
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
@@ -192,7 +192,6 @@ impl IsBlock for ExecutedBlock {
|
||||
pub struct OpenBlock<'x> {
|
||||
block: ExecutedBlock,
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
}
|
||||
|
||||
@@ -230,8 +229,7 @@ impl<'x> OpenBlock<'x> {
|
||||
/// Create a new `OpenBlock` ready for transaction pushing.
|
||||
pub fn new(
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
@@ -240,11 +238,10 @@ impl<'x> OpenBlock<'x> {
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
) -> Result<Self, Error> {
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), factories));
|
||||
let mut r = OpenBlock {
|
||||
block: ExecutedBlock::new(state, tracing),
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
last_hashes: last_hashes,
|
||||
};
|
||||
|
||||
@@ -332,7 +329,7 @@ impl<'x> OpenBlock<'x> {
|
||||
|
||||
let env_info = self.env_info();
|
||||
// info!("env_info says gas_used={}", env_info.gas_used);
|
||||
match self.block.state.apply(&env_info, self.engine, self.vm_factory, &t, self.block.traces.is_some()) {
|
||||
match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) {
|
||||
Ok(outcome) => {
|
||||
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
|
||||
self.block.base.transactions.push(t);
|
||||
@@ -421,14 +418,13 @@ impl ClosedBlock {
|
||||
}
|
||||
|
||||
/// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`.
|
||||
pub fn reopen<'a>(self, engine: &'a Engine, vm_factory: &'a EvmFactory) -> OpenBlock<'a> {
|
||||
pub fn reopen<'a>(self, engine: &'a Engine) -> OpenBlock<'a> {
|
||||
// revert rewards (i.e. set state back at last transaction's state).
|
||||
let mut block = self.block;
|
||||
block.state = self.unclosed_state;
|
||||
OpenBlock {
|
||||
block: block,
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
last_hashes: self.last_hashes,
|
||||
}
|
||||
}
|
||||
@@ -499,17 +495,16 @@ pub fn enact(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), factories.clone()));
|
||||
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
|
||||
let mut b = try!(OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
|
||||
b.set_difficulty(*header.difficulty());
|
||||
b.set_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
@@ -532,12 +527,11 @@ pub fn enact_bytes(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header();
|
||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
|
||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||
}
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||
@@ -549,11 +543,10 @@ pub fn enact_verified(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let view = BlockView::new(&block.bytes);
|
||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
|
||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||
}
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||
@@ -565,11 +558,10 @@ pub fn enact_and_seal(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<SealedBlock, Error> {
|
||||
let header = BlockView::new(block_bytes).header_view();
|
||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
|
||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -587,8 +579,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(&*spec.engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let _ = b.seal(&*spec.engine, vec![]);
|
||||
}
|
||||
@@ -603,9 +594,8 @@ 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 vm_factory = Default::default();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
@@ -613,7 +603,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 e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap();
|
||||
|
||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||
|
||||
@@ -632,9 +622,8 @@ 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 vm_factory = Default::default();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut open_block = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
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();
|
||||
let mut uncle2_header = Header::new();
|
||||
@@ -649,7 +638,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 e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap();
|
||||
|
||||
let bytes = e.rlp_bytes();
|
||||
assert_eq!(bytes, orig_bytes);
|
||||
|
||||
@@ -380,7 +380,7 @@ impl BlockChain {
|
||||
children: vec![]
|
||||
};
|
||||
|
||||
let batch = DBTransaction::new(&db);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
batch.put(db::COL_HEADERS, &hash, block.header_rlp().as_raw());
|
||||
batch.put(db::COL_BODIES, &hash, &Self::block_to_body(genesis));
|
||||
|
||||
@@ -419,7 +419,7 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
let batch = db.transaction();
|
||||
let mut batch = db.transaction();
|
||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||
db.write(batch).expect("Low level database error.");
|
||||
|
||||
@@ -451,7 +451,7 @@ impl BlockChain {
|
||||
#[cfg(test)]
|
||||
fn rewind(&self) -> Option<H256> {
|
||||
use db::Key;
|
||||
let batch = self.db.transaction();
|
||||
let mut batch =self.db.transaction();
|
||||
// track back to the best block we have in the blocks database
|
||||
if let Some(best_block_hash) = self.db.get(db::COL_EXTRA, b"best").unwrap() {
|
||||
let best_block_hash = H256::from_slice(&best_block_hash);
|
||||
@@ -604,7 +604,7 @@ impl BlockChain {
|
||||
|
||||
assert!(self.pending_best_block.read().is_none());
|
||||
|
||||
let batch = self.db.transaction();
|
||||
let mut batch = self.db.transaction();
|
||||
|
||||
let block_rlp = UntrustedRlp::new(bytes);
|
||||
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
||||
@@ -625,7 +625,7 @@ impl BlockChain {
|
||||
location: BlockLocation::CanonChain,
|
||||
};
|
||||
|
||||
self.prepare_update(&batch, ExtrasUpdate {
|
||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
@@ -659,7 +659,7 @@ impl BlockChain {
|
||||
let mut update = HashMap::new();
|
||||
update.insert(hash, block_details);
|
||||
|
||||
self.prepare_update(&batch, ExtrasUpdate {
|
||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: update,
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
@@ -682,7 +682,7 @@ impl BlockChain {
|
||||
let mut parent_details = self.block_details(&block_hash)
|
||||
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash));
|
||||
|
||||
let batch = self.db.transaction();
|
||||
let mut batch = self.db.transaction();
|
||||
parent_details.children.push(child_hash);
|
||||
|
||||
let mut update = HashMap::new();
|
||||
@@ -701,7 +701,7 @@ impl BlockChain {
|
||||
/// Inserts the block into backing cache database.
|
||||
/// Expects the block to be valid and already verified.
|
||||
/// If the block is already known, does nothing.
|
||||
pub fn insert_block(&self, batch: &DBTransaction, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
pub fn insert_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
// create views onto rlp
|
||||
let block = BlockView::new(bytes);
|
||||
let header = block.header_view();
|
||||
@@ -782,7 +782,7 @@ impl BlockChain {
|
||||
}
|
||||
|
||||
/// Prepares extras update.
|
||||
fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
||||
fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
||||
{
|
||||
let block_hashes: Vec<_> = update.block_details.keys().cloned().collect();
|
||||
|
||||
@@ -995,8 +995,13 @@ impl BlockChain {
|
||||
let log_blooms = match info.location {
|
||||
BlockLocation::Branch => HashMap::new(),
|
||||
BlockLocation::CanonChain => {
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
chain.insert(info.number as bc::Number, Bloom::from(header.log_bloom()).into())
|
||||
let log_bloom = header.log_bloom();
|
||||
if log_bloom.is_zero() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
chain.insert(info.number as bc::Number, Bloom::from(log_bloom).into())
|
||||
}
|
||||
},
|
||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||
let ancestor_number = self.block_number(&data.ancestor).unwrap();
|
||||
@@ -1142,8 +1147,8 @@ mod tests {
|
||||
assert_eq!(bc.best_block_number(), 0);
|
||||
|
||||
// when
|
||||
let batch = db.transaction();
|
||||
bc.insert_block(&batch, &first, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &first, vec![]);
|
||||
assert_eq!(bc.best_block_number(), 0);
|
||||
bc.commit();
|
||||
// NOTE no db.write here (we want to check if best block is cached)
|
||||
@@ -1172,8 +1177,8 @@ mod tests {
|
||||
assert_eq!(bc.block_hash(1), None);
|
||||
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]);
|
||||
|
||||
let batch = db.transaction();
|
||||
bc.insert_block(&batch, &first, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &first, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
|
||||
@@ -1198,11 +1203,11 @@ mod tests {
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
|
||||
let mut block_hashes = vec![genesis_hash.clone()];
|
||||
let batch = db.transaction();
|
||||
let mut batch =db.transaction();
|
||||
for _ in 0..10 {
|
||||
let block = canon_chain.generate(&mut finalizer).unwrap();
|
||||
block_hashes.push(BlockView::new(&block).header_view().sha3());
|
||||
bc.insert_block(&batch, &block, vec![]);
|
||||
bc.insert_block(&mut batch, &block, vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
@@ -1233,20 +1238,20 @@ mod tests {
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
|
||||
let batch = db.transaction();
|
||||
let mut batch =db.transaction();
|
||||
for b in &[&b1a, &b1b, &b2a, &b2b, &b3a, &b3b, &b4a, &b4b, &b5a, &b5b] {
|
||||
bc.insert_block(&batch, b, vec![]);
|
||||
bc.insert_block(&mut batch, b, vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
bc.insert_block(&batch, &b1b, vec![]);
|
||||
bc.insert_block(&batch, &b2a, vec![]);
|
||||
bc.insert_block(&batch, &b2b, vec![]);
|
||||
bc.insert_block(&batch, &b3a, vec![]);
|
||||
bc.insert_block(&batch, &b3b, vec![]);
|
||||
bc.insert_block(&batch, &b4a, vec![]);
|
||||
bc.insert_block(&batch, &b4b, vec![]);
|
||||
bc.insert_block(&batch, &b5a, vec![]);
|
||||
bc.insert_block(&batch, &b5b, vec![]);
|
||||
bc.insert_block(&mut batch, &b1b, vec![]);
|
||||
bc.insert_block(&mut batch, &b2a, vec![]);
|
||||
bc.insert_block(&mut batch, &b2b, vec![]);
|
||||
bc.insert_block(&mut batch, &b3a, vec![]);
|
||||
bc.insert_block(&mut batch, &b3b, vec![]);
|
||||
bc.insert_block(&mut batch, &b4a, vec![]);
|
||||
bc.insert_block(&mut batch, &b4b, vec![]);
|
||||
bc.insert_block(&mut batch, &b5a, vec![]);
|
||||
bc.insert_block(&mut batch, &b5b, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@@ -1281,17 +1286,17 @@ mod tests {
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
|
||||
let batch = db.transaction();
|
||||
let ir1 = bc.insert_block(&batch, &b1, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
let ir1 = bc.insert_block(&mut batch, &b1, vec![]);
|
||||
bc.commit();
|
||||
let ir2 = bc.insert_block(&batch, &b2, vec![]);
|
||||
let ir2 = bc.insert_block(&mut batch, &b2, vec![]);
|
||||
bc.commit();
|
||||
let ir3b = bc.insert_block(&batch, &b3b, vec![]);
|
||||
let ir3b = bc.insert_block(&mut batch, &b3b, vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
assert_eq!(bc.block_hash(3).unwrap(), b3b_hash);
|
||||
let batch = db.transaction();
|
||||
let ir3a = bc.insert_block(&batch, &b3a, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
let ir3a = bc.insert_block(&mut batch, &b3a, vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@@ -1397,8 +1402,8 @@ mod tests {
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
assert_eq!(bc.best_block_hash(), genesis_hash);
|
||||
let batch = db.transaction();
|
||||
bc.insert_block(&batch, &first, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &first, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
assert_eq!(bc.best_block_hash(), first_hash);
|
||||
@@ -1462,8 +1467,8 @@ mod tests {
|
||||
let temp = RandomTempPath::new();
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
let batch = db.transaction();
|
||||
bc.insert_block(&batch, &b1, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &b1, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
|
||||
@@ -1475,8 +1480,8 @@ mod tests {
|
||||
}
|
||||
|
||||
fn insert_block(db: &Arc<Database>, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||
let batch = db.transaction();
|
||||
let res = bc.insert_block(&batch, bytes, receipts);
|
||||
let mut batch =db.transaction();
|
||||
let res = bc.insert_block(&mut batch, bytes, receipts);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
res
|
||||
@@ -1564,16 +1569,16 @@ mod tests {
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
let uncle = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||
|
||||
let batch = db.transaction();
|
||||
let mut batch =db.transaction();
|
||||
// create a longer fork
|
||||
for _ in 0..5 {
|
||||
let canon_block = canon_chain.generate(&mut finalizer).unwrap();
|
||||
bc.insert_block(&batch, &canon_block, vec![]);
|
||||
bc.insert_block(&mut batch, &canon_block, vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 5);
|
||||
bc.insert_block(&batch, &uncle, vec![]);
|
||||
bc.insert_block(&mut batch, &uncle, vec![]);
|
||||
db.write(batch).unwrap();
|
||||
bc.commit();
|
||||
}
|
||||
@@ -1599,10 +1604,10 @@ mod tests {
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||
|
||||
let batch = db.transaction();
|
||||
bc.insert_block(&batch, &first, vec![]);
|
||||
let mut batch =db.transaction();
|
||||
bc.insert_block(&mut batch, &first, vec![]);
|
||||
bc.commit();
|
||||
bc.insert_block(&batch, &second, vec![]);
|
||||
bc.insert_block(&mut batch, &second, vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::*;
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::ripemd160::Ripemd160;
|
||||
use crypto::digest::Digest;
|
||||
use util::*;
|
||||
use ethkey::{Signature, recover};
|
||||
use ethjson;
|
||||
|
||||
/// Definition of a contract whose implementation is built-in.
|
||||
@@ -92,19 +93,19 @@ pub fn new_builtin_exec(name: &str) -> Box<Fn(&[u8], &mut [u8])> {
|
||||
}),
|
||||
"ecrecover" => Box::new(move|input: &[u8], output: &mut[u8]| {
|
||||
#[repr(packed)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
struct InType {
|
||||
hash: H256,
|
||||
v: H256,
|
||||
r: H256,
|
||||
s: H256,
|
||||
}
|
||||
let mut it: InType = InType { hash: H256::new(), v: H256::new(), r: H256::new(), s: H256::new() };
|
||||
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 ec::is_valid(&s) {
|
||||
if let Ok(p) = ec::recover(&s, &it.hash) {
|
||||
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()) {
|
||||
|
||||
@@ -66,6 +66,7 @@ use evm::Factory as EvmFactory;
|
||||
use miner::{Miner, MinerService};
|
||||
use util::TrieFactory;
|
||||
use snapshot::{self, io as snapshot_io};
|
||||
use factory::Factories;
|
||||
|
||||
// re-export
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
@@ -131,8 +132,6 @@ pub struct Client {
|
||||
import_lock: Mutex<()>,
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
verifier: Box<Verifier>,
|
||||
vm_factory: Arc<EvmFactory>,
|
||||
trie_factory: TrieFactory,
|
||||
miner: Arc<Miner>,
|
||||
sleep_state: Mutex<SleepState>,
|
||||
liveness: AtomicBool,
|
||||
@@ -140,6 +139,7 @@ pub struct Client {
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
factories: Factories,
|
||||
}
|
||||
|
||||
const HISTORY: u64 = 1200;
|
||||
@@ -173,8 +173,8 @@ impl Client {
|
||||
|
||||
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())) {
|
||||
let batch = DBTransaction::new(&db);
|
||||
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.commit(&mut batch, 0, &spec.genesis_header().hash(), None));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
|
||||
@@ -189,6 +189,13 @@ impl Client {
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
||||
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
|
||||
|
||||
let factories = Factories {
|
||||
vm: EvmFactory::new(config.vm_type),
|
||||
trie: TrieFactory::new(config.trie_spec),
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
|
||||
let client = Client {
|
||||
sleep_state: Mutex::new(SleepState::new(awake)),
|
||||
liveness: AtomicBool::new(awake),
|
||||
@@ -203,13 +210,12 @@ impl Client {
|
||||
import_lock: Mutex::new(()),
|
||||
panic_handler: panic_handler,
|
||||
verifier: verification::new(config.verifier_type),
|
||||
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
|
||||
trie_factory: TrieFactory::new(config.trie_spec),
|
||||
miner: miner,
|
||||
io_channel: message_channel,
|
||||
notify: RwLock::new(Vec::new()),
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
factories: factories,
|
||||
};
|
||||
Ok(Arc::new(client))
|
||||
}
|
||||
@@ -290,7 +296,7 @@ impl Client {
|
||||
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.vm_factory, self.trie_factory.clone());
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
if let Err(e) = enact_result {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
@@ -426,14 +432,14 @@ impl Client {
|
||||
|
||||
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
||||
|
||||
let batch = DBTransaction::new(&self.db);
|
||||
let mut batch = DBTransaction::new(&self.db);
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
// TODO: Prove it with a test.
|
||||
block.drain().commit(&batch, number, hash, ancient).expect("DB commit failed.");
|
||||
block.drain().commit(&mut batch, number, hash, ancient).expect("DB commit failed.");
|
||||
|
||||
let route = self.chain.insert_block(&batch, block_data, receipts);
|
||||
self.tracedb.import(&batch, TraceImportRequest {
|
||||
let route = self.chain.insert_block(&mut batch, block_data, receipts);
|
||||
self.tracedb.import(&mut batch, TraceImportRequest {
|
||||
traces: traces.into(),
|
||||
block_hash: hash.clone(),
|
||||
block_number: number,
|
||||
@@ -494,7 +500,7 @@ impl Client {
|
||||
|
||||
let root = HeaderView::new(&header).state_root();
|
||||
|
||||
State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok()
|
||||
State::from_existing(db, root, self.engine.account_start_nonce(), self.factories.clone()).ok()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -519,7 +525,7 @@ impl Client {
|
||||
self.state_db.lock().boxed_clone(),
|
||||
HeaderView::new(&self.best_block_header()).state_root(),
|
||||
self.engine.account_start_nonce(),
|
||||
self.trie_factory.clone())
|
||||
self.factories.clone())
|
||||
.expect("State root of best block header always valid.")
|
||||
}
|
||||
|
||||
@@ -689,7 +695,7 @@ impl BlockChainClient for Client {
|
||||
state.add_balance(&sender, &(needed_balance - balance));
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options));
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
@@ -721,7 +727,7 @@ impl BlockChainClient for Client {
|
||||
gas_limit: view.gas_limit(),
|
||||
};
|
||||
for t in txs.iter().take(address.index) {
|
||||
match Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, Default::default()) {
|
||||
match Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, Default::default()) {
|
||||
Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; }
|
||||
Err(ee) => { return Err(CallError::Execution(ee)) }
|
||||
}
|
||||
@@ -729,7 +735,7 @@ impl BlockChainClient for Client {
|
||||
let t = &txs[address.index];
|
||||
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options));
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
|
||||
Ok(ret)
|
||||
@@ -1045,8 +1051,7 @@ impl MiningBlockChainClient for Client {
|
||||
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine,
|
||||
&self.vm_factory,
|
||||
self.trie_factory.clone(),
|
||||
self.factories.clone(),
|
||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||
self.state_db.lock().boxed_clone(),
|
||||
&self.chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
||||
@@ -1070,7 +1075,7 @@ impl MiningBlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn vm_factory(&self) -> &EvmFactory {
|
||||
&self.vm_factory
|
||||
&self.factories.vm
|
||||
}
|
||||
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||
use util::*;
|
||||
use ethkey::{Generator, Random};
|
||||
use devtools::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::TreeRoute;
|
||||
@@ -73,6 +74,8 @@ pub struct TestBlockChainClient {
|
||||
pub spec: Spec,
|
||||
/// VM Factory
|
||||
pub vm_factory: EvmFactory,
|
||||
/// Timestamp assigned to latest sealed block
|
||||
pub latest_block_timestamp: RwLock<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -114,6 +117,7 @@ impl TestBlockChainClient {
|
||||
miner: Arc::new(Miner::with_spec(&spec)),
|
||||
spec: spec,
|
||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
||||
latest_block_timestamp: RwLock::new(10_000_000),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
@@ -155,6 +159,11 @@ impl TestBlockChainClient {
|
||||
self.queue_size.store(size, AtomicOrder::Relaxed);
|
||||
}
|
||||
|
||||
/// Set timestamp assigned to latest sealed block
|
||||
pub fn set_latest_block_timestamp(&self, ts: u64) {
|
||||
*self.latest_block_timestamp.write() = ts;
|
||||
}
|
||||
|
||||
/// Add blocks to test client.
|
||||
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
|
||||
let len = self.numbers.read().len();
|
||||
@@ -180,7 +189,7 @@ impl TestBlockChainClient {
|
||||
let txs = match with {
|
||||
EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
|
||||
let mut txs = RlpStream::new_list(1);
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
// Update nonces value
|
||||
self.nonces.write().insert(keypair.address(), U256::one());
|
||||
let tx = Transaction {
|
||||
@@ -268,7 +277,6 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine,
|
||||
self.vm_factory(),
|
||||
Default::default(),
|
||||
false,
|
||||
db,
|
||||
@@ -279,7 +287,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
extra_data
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
||||
open_block.set_timestamp(10_000_000);
|
||||
open_block.set_timestamp(*self.latest_block_timestamp.read());
|
||||
open_block
|
||||
}
|
||||
|
||||
|
||||
@@ -83,10 +83,10 @@ pub trait Key<T> {
|
||||
/// Should be used to write value into database.
|
||||
pub trait Writable {
|
||||
/// Writes the value into the database.
|
||||
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Writes the value into the database and updates the cache.
|
||||
fn write_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
fn write_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
@@ -102,7 +102,7 @@ pub trait Writable {
|
||||
}
|
||||
|
||||
/// Writes the values into the database and updates the cache.
|
||||
fn extend_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
fn extend_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
@@ -169,7 +169,7 @@ pub trait Readable {
|
||||
}
|
||||
|
||||
impl Writable for DBTransaction {
|
||||
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
self.put(col, &key.key(), &encode(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||
|
||||
use common::*;
|
||||
use ethkey::{recover, public_to_address};
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use spec::CommonParams;
|
||||
@@ -133,7 +134,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 signer = Address::from(try!(ec::recover(&sig, &header.bare_hash())).sha3());
|
||||
let signer = public_to_address(&try!(recover(&sig.into(), &header.bare_hash())));
|
||||
if !self.our_params.authorities.contains(&signer) {
|
||||
return try!(Err(BlockError::InvalidSeal));
|
||||
}
|
||||
@@ -228,15 +229,10 @@ mod tests {
|
||||
fn can_do_signature_verification_fail() {
|
||||
let engine = new_test_authority().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H520::default()).to_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
|
||||
match verify_result {
|
||||
Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => {},
|
||||
Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); },
|
||||
_ => { panic!("Should be error, got Ok"); },
|
||||
}
|
||||
assert!(verify_result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -252,8 +248,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
|
||||
@@ -86,8 +86,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
// Seal with empty AccountProvider.
|
||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
@@ -101,7 +100,7 @@ mod tests {
|
||||
|
||||
assert!(engine.verify_block_basic(&header, None).is_ok());
|
||||
|
||||
header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H520::default()).to_vec()]);
|
||||
|
||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||
use types::block_import_error::BlockImportError;
|
||||
use snapshot::Error as SnapshotError;
|
||||
use engines::EngineError;
|
||||
use ethkey::Error as EthkeyError;
|
||||
|
||||
pub use types::executed::{ExecutionError, CallError};
|
||||
|
||||
@@ -241,6 +242,8 @@ pub enum Error {
|
||||
Snapshot(SnapshotError),
|
||||
/// Consensus vote error.
|
||||
Engine(EngineError),
|
||||
/// Ethkey error.
|
||||
Ethkey(EthkeyError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -263,6 +266,7 @@ impl fmt::Display for Error {
|
||||
Error::Snapshot(ref err) => err.fmt(f),
|
||||
Error::Engine(ref err) =>
|
||||
f.write_fmt(format_args!("Bad vote: {:?}", err)),
|
||||
Error::Ethkey(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,12 +307,6 @@ impl From<ExecutionError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CryptoError> for Error {
|
||||
fn from(err: CryptoError) -> Error {
|
||||
Error::Util(UtilError::Crypto(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecoderError> for Error {
|
||||
fn from(err: DecoderError) -> Error {
|
||||
Error::Util(UtilError::Decoder(err))
|
||||
@@ -371,6 +369,9 @@ impl From<EngineError> for Error {
|
||||
match err {
|
||||
other => Error::Engine(other),
|
||||
}
|
||||
impl From<EthkeyError> for Error {
|
||||
fn from(err: EthkeyError) -> Error {
|
||||
Error::Ethkey(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -357,8 +357,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
||||
}
|
||||
@@ -372,8 +371,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let mut b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
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();
|
||||
|
||||
@@ -80,6 +80,7 @@ impl VMType {
|
||||
}
|
||||
|
||||
/// Evm factory. Creates appropriate Evm.
|
||||
#[derive(Clone)]
|
||||
pub struct Factory {
|
||||
evm: VMType
|
||||
}
|
||||
@@ -128,7 +129,7 @@ impl Factory {
|
||||
|
||||
impl Default for Factory {
|
||||
/// Returns jitvm factory
|
||||
#[cfg(feature = "jit")]
|
||||
#[cfg(all(feature = "jit", not(test)))]
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Jit
|
||||
@@ -136,7 +137,7 @@ impl Default for Factory {
|
||||
}
|
||||
|
||||
/// Returns native rust evm factory
|
||||
#[cfg(not(feature = "jit"))]
|
||||
#[cfg(any(not(feature = "jit"), test))]
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Interpreter
|
||||
|
||||
@@ -483,6 +483,7 @@ impl<'a> Executive<'a> {
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use ethkey::{Generator, Random};
|
||||
use super::*;
|
||||
use common::*;
|
||||
use evm::{Factory, VMType};
|
||||
@@ -1002,7 +1003,7 @@ mod tests {
|
||||
// TODO: fix (preferred) or remove
|
||||
evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int}
|
||||
fn test_transact_simple(factory: Factory) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(17),
|
||||
@@ -1069,7 +1070,7 @@ mod tests {
|
||||
|
||||
evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int}
|
||||
fn test_transact_invalid_nonce(factory: Factory) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(17),
|
||||
@@ -1102,7 +1103,7 @@ mod tests {
|
||||
|
||||
evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int}
|
||||
fn test_transact_gas_limit_reached(factory: Factory) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(17),
|
||||
@@ -1137,7 +1138,7 @@ mod tests {
|
||||
evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int}
|
||||
fn test_not_enough_cash(factory: Factory) {
|
||||
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(18),
|
||||
|
||||
30
ethcore/src/factory.rs
Normal file
30
ethcore/src/factory.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::trie::TrieFactory;
|
||||
use evm::Factory as EvmFactory;
|
||||
use account_db::Factory as AccountFactory;
|
||||
|
||||
/// Collection of factories.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Factories {
|
||||
/// factory for evm.
|
||||
pub vm: EvmFactory,
|
||||
/// factory for tries.
|
||||
pub trie: TrieFactory,
|
||||
/// factory for account databases.
|
||||
pub accountdb: AccountFactory,
|
||||
}
|
||||
@@ -64,8 +64,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
state.populate_from(pre);
|
||||
state.commit()
|
||||
.expect(&format!("State test {} failed due to internal error.", name));
|
||||
let vm_factory = Default::default();
|
||||
let res = state.apply(&env, &*engine, &vm_factory, &transaction, false);
|
||||
let res = state.apply(&env, &*engine, &transaction, false);
|
||||
|
||||
if fail_unless(state.root() == &post_state_root) {
|
||||
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
|
||||
|
||||
@@ -96,6 +96,7 @@ extern crate bloomchain;
|
||||
extern crate rayon;
|
||||
extern crate hyper;
|
||||
extern crate ethash;
|
||||
extern crate ethkey;
|
||||
pub extern crate ethstore;
|
||||
extern crate semver;
|
||||
extern crate ethcore_ipc_nano as nanoipc;
|
||||
@@ -139,6 +140,7 @@ mod externalities;
|
||||
mod verification;
|
||||
mod blockchain;
|
||||
mod types;
|
||||
mod factory;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -270,7 +270,7 @@ impl Miner {
|
||||
Some(old_block) => {
|
||||
trace!(target: "miner", "Already have previous work; updating and returning");
|
||||
// add transactions to old_block
|
||||
old_block.reopen(&*self.engine, chain.vm_factory())
|
||||
old_block.reopen(&*self.engine)
|
||||
}
|
||||
None => {
|
||||
// block not found - create it.
|
||||
@@ -723,8 +723,8 @@ impl MinerService for Miner {
|
||||
.position(|t| t == *hash)
|
||||
.map(|index| {
|
||||
let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used };
|
||||
let ref tx = txs[index];
|
||||
let ref receipt = pending.receipts()[index];
|
||||
let tx = &txs[index];
|
||||
let receipt = &pending.receipts()[index];
|
||||
RichReceipt {
|
||||
transaction_hash: hash.clone(),
|
||||
transaction_index: index,
|
||||
@@ -911,6 +911,7 @@ mod tests {
|
||||
use super::super::MinerService;
|
||||
use super::*;
|
||||
use util::*;
|
||||
use ethkey::{Generator, Random};
|
||||
use client::{TestBlockChainClient, EachBlockWith};
|
||||
use client::{TransactionImportResult};
|
||||
use types::transaction::{Transaction, Action};
|
||||
@@ -975,7 +976,7 @@ mod tests {
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::zero(),
|
||||
@@ -1005,7 +1006,7 @@ mod tests {
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::zero(),
|
||||
|
||||
@@ -26,16 +26,17 @@
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//! extern crate ethcore;
|
||||
//! extern crate ethkey;
|
||||
//! extern crate rustc_serialize;
|
||||
//!
|
||||
//! use util::crypto::KeyPair;
|
||||
//! use util::{Uint, U256, Address};
|
||||
//! use ethkey::{Random, Generator};
|
||||
//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::transaction::*;
|
||||
//! use rustc_serialize::hex::FromHex;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let key = KeyPair::create().unwrap();
|
||||
//! let key = Random.generate().unwrap();
|
||||
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) };
|
||||
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
@@ -43,14 +44,14 @@
|
||||
//!
|
||||
//! let st1 = t1.sign(&key.secret());
|
||||
//! let st2 = t2.sign(&key.secret());
|
||||
//! let default_nonce = |_a: &Address| AccountDetails {
|
||||
//! let default_account_details = |_a: &Address| AccountDetails {
|
||||
//! nonce: U256::from(10),
|
||||
//! balance: U256::from(1_000_000),
|
||||
//! };
|
||||
//!
|
||||
//! let mut txq = TransactionQueue::new();
|
||||
//! txq.add(st2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
//!
|
||||
//! // Check status
|
||||
//! assert_eq!(txq.status().pending, 2);
|
||||
@@ -62,7 +63,7 @@
|
||||
//!
|
||||
//! // And when transaction is removed (but nonce haven't changed)
|
||||
//! // it will move subsequent transactions to future
|
||||
//! txq.remove_invalid(&st1.hash(), &default_nonce);
|
||||
//! txq.remove_invalid(&st1.hash(), &default_account_details);
|
||||
//! assert_eq!(txq.status().pending, 0);
|
||||
//! assert_eq!(txq.status().future, 1);
|
||||
//! assert_eq!(txq.top_transactions().len(), 0);
|
||||
@@ -82,7 +83,7 @@
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, BTreeSet};
|
||||
use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap};
|
||||
use util::{Address, H256, Uint, U256};
|
||||
use util::table::Table;
|
||||
use transaction::*;
|
||||
@@ -226,23 +227,34 @@ impl VerifiedTransaction {
|
||||
struct TransactionSet {
|
||||
by_priority: BTreeSet<TransactionOrder>,
|
||||
by_address: Table<Address, U256, TransactionOrder>,
|
||||
by_gas_price: BTreeMap<U256, HashSet<H256>>,
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
impl TransactionSet {
|
||||
/// Inserts `TransactionOrder` to this set
|
||||
/// Inserts `TransactionOrder` to this set. Transaction does not need to be unique -
|
||||
/// the same transaction may be validly inserted twice. Any previous transaction that
|
||||
/// it replaces (i.e. with the same `sender` and `nonce`) should be returned.
|
||||
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
|
||||
self.by_priority.insert(order.clone());
|
||||
let r = self.by_address.insert(sender, nonce, order);
|
||||
// If transaction was replaced remove it from priority queue
|
||||
if let Some(ref old_order) = r {
|
||||
self.by_priority.remove(old_order);
|
||||
if !self.by_priority.insert(order.clone()) {
|
||||
return Some(order.clone());
|
||||
}
|
||||
assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||
r
|
||||
let order_hash = order.hash.clone();
|
||||
let order_gas_price = order.gas_price.clone();
|
||||
let by_address_replaced = self.by_address.insert(sender, nonce, order);
|
||||
// If transaction was replaced remove it from priority queue
|
||||
if let Some(ref old_order) = by_address_replaced {
|
||||
assert!(self.by_priority.remove(old_order), "hash is in `by_address`; all transactions in `by_address` must be in `by_priority`; qed");
|
||||
assert!(Self::remove_item(&mut self.by_gas_price, &old_order.gas_price, &old_order.hash),
|
||||
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
||||
}
|
||||
Self::insert_item(&mut self.by_gas_price, order_gas_price, order_hash);
|
||||
debug_assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||
debug_assert_eq!(self.by_gas_price.iter().map(|(_, v)| v.len()).fold(0, |a, b| a + b), self.by_address.len());
|
||||
by_address_replaced
|
||||
}
|
||||
|
||||
/// Remove low priority transactions if there is more then specified by given `limit`.
|
||||
/// Remove low priority transactions if there is more than specified by given `limit`.
|
||||
///
|
||||
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
||||
@@ -267,7 +279,7 @@ impl TransactionSet {
|
||||
.expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
||||
|
||||
by_hash.remove(&order.hash)
|
||||
.expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
|
||||
.expect("hash is in `by_priorty`; all hashes in `by_priority` must be in `by_hash`; qed");
|
||||
|
||||
let min = removed.get(&sender).map_or(nonce, |val| cmp::min(*val, nonce));
|
||||
removed.insert(sender, min);
|
||||
@@ -278,6 +290,8 @@ impl TransactionSet {
|
||||
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
||||
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
||||
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
||||
assert!(Self::remove_item(&mut self.by_gas_price, &tx_order.gas_price, &tx_order.hash),
|
||||
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
||||
self.by_priority.remove(&tx_order);
|
||||
assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||
return Some(tx_order);
|
||||
@@ -290,6 +304,7 @@ impl TransactionSet {
|
||||
fn clear(&mut self) {
|
||||
self.by_priority.clear();
|
||||
self.by_address.clear();
|
||||
self.by_gas_price.clear();
|
||||
}
|
||||
|
||||
/// Sets new limit for number of transactions in this `TransactionSet`.
|
||||
@@ -297,6 +312,41 @@ impl TransactionSet {
|
||||
fn set_limit(&mut self, limit: usize) {
|
||||
self.limit = limit;
|
||||
}
|
||||
|
||||
/// Get the minimum gas price that we can accept into this queue that wouldn't cause the transaction to
|
||||
/// immediately be dropped. 0 if the queue isn't at capacity; 1 plus the lowest if it is.
|
||||
fn gas_price_entry_limit(&self) -> U256 {
|
||||
match self.by_gas_price.keys().next() {
|
||||
Some(k) if self.by_priority.len() >= self.limit => *k + 1.into(),
|
||||
_ => U256::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert an item into a BTreeMap/HashSet "multimap".
|
||||
fn insert_item(into: &mut BTreeMap<U256, HashSet<H256>>, gas_price: U256, hash: H256) -> bool {
|
||||
into.entry(gas_price).or_insert_with(Default::default).insert(hash)
|
||||
}
|
||||
|
||||
/// Remove an item from a BTreeMap/HashSet "multimap".
|
||||
/// Returns true if the item was removed successfully.
|
||||
fn remove_item(from: &mut BTreeMap<U256, HashSet<H256>>, gas_price: &U256, hash: &H256) -> bool {
|
||||
if let Some(mut hashes) = from.get_mut(gas_price) {
|
||||
let only_one_left = hashes.len() == 1;
|
||||
if !only_one_left {
|
||||
// Operation may be ok: only if hash is in gas-price's Set.
|
||||
return hashes.remove(hash);
|
||||
}
|
||||
if hashes.iter().next().unwrap() != hash {
|
||||
// Operation failed: hash not the single item in gas-price's Set.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Operation failed: gas-price not found in Map.
|
||||
return false;
|
||||
}
|
||||
// Operation maybe ok: only if hash not found in gas-price Set.
|
||||
from.remove(gas_price).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -316,7 +366,6 @@ pub struct AccountDetails {
|
||||
pub balance: U256,
|
||||
}
|
||||
|
||||
|
||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||
const GAS_LIMIT_HYSTERESIS: usize = 10; // %
|
||||
|
||||
@@ -355,12 +404,14 @@ impl TransactionQueue {
|
||||
let current = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: limit,
|
||||
};
|
||||
|
||||
let future = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: limit,
|
||||
};
|
||||
|
||||
@@ -400,6 +451,12 @@ impl TransactionQueue {
|
||||
self.minimal_gas_price = min_gas_price;
|
||||
}
|
||||
|
||||
/// Get one more than the lowest gas price in the queue iff the pool is
|
||||
/// full, otherwise 0.
|
||||
pub fn effective_minimum_gas_price(&self) -> U256 {
|
||||
self.current.gas_price_entry_limit()
|
||||
}
|
||||
|
||||
/// Sets new gas limit. Transactions with gas slightly (`GAS_LIMIT_HYSTERESIS`) above the limit won't be imported.
|
||||
/// Any transaction already imported to the queue is not affected.
|
||||
pub fn set_gas_limit(&mut self, gas_limit: U256) {
|
||||
@@ -445,6 +502,21 @@ impl TransactionQueue {
|
||||
}));
|
||||
}
|
||||
|
||||
let full_queues_lowest = self.effective_minimum_gas_price();
|
||||
if tx.gas_price < full_queues_lowest && origin != TransactionOrigin::Local {
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction below lowest gas price in a full queue: {:?} (gp: {} < {})",
|
||||
tx.hash(),
|
||||
tx.gas_price,
|
||||
full_queues_lowest
|
||||
);
|
||||
|
||||
return Err(Error::Transaction(TransactionError::InsufficientGasPrice {
|
||||
minimal: full_queues_lowest,
|
||||
got: tx.gas_price,
|
||||
}));
|
||||
}
|
||||
|
||||
try!(tx.check_low_s());
|
||||
|
||||
if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit {
|
||||
@@ -798,6 +870,7 @@ mod test {
|
||||
extern crate rustc_serialize;
|
||||
use util::table::*;
|
||||
use util::*;
|
||||
use ethkey::{Random, Generator};
|
||||
use transaction::*;
|
||||
use error::{Error, TransactionError};
|
||||
use super::*;
|
||||
@@ -811,59 +884,86 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
||||
fn default_nonce() -> U256 { 123.into() }
|
||||
fn default_gas_price() -> U256 { 1.into() }
|
||||
|
||||
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction {
|
||||
Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(100),
|
||||
data: "3331600055".from_hex().unwrap(),
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::one(),
|
||||
gas_price: gas_price,
|
||||
nonce: nonce
|
||||
}
|
||||
}
|
||||
|
||||
fn new_tx() -> SignedTransaction {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
new_unsigned_tx(U256::from(123)).sign(keypair.secret())
|
||||
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
|
||||
let keypair = Random.generate().unwrap();
|
||||
new_unsigned_tx(nonce, gas_price).sign(keypair.secret())
|
||||
}
|
||||
|
||||
|
||||
fn default_nonce_val() -> U256 {
|
||||
U256::from(123)
|
||||
fn new_tx_default() -> SignedTransaction {
|
||||
new_tx(default_nonce(), default_gas_price())
|
||||
}
|
||||
|
||||
fn default_nonce(_address: &Address) -> AccountDetails {
|
||||
fn default_account_details(_address: &Address) -> AccountDetails {
|
||||
AccountDetails {
|
||||
nonce: default_nonce_val(),
|
||||
nonce: default_nonce(),
|
||||
balance: !U256::zero()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns two transactions with identical (sender, nonce) but different hashes
|
||||
fn new_similar_txs() -> (SignedTransaction, SignedTransaction) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
let nonce = U256::from(123);
|
||||
let tx = new_unsigned_tx(nonce);
|
||||
let mut tx2 = new_unsigned_tx(nonce);
|
||||
tx2.gas_price = U256::from(2);
|
||||
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
let tx1 = new_unsigned_tx(nonce, gas_price);
|
||||
let tx2 = new_unsigned_tx(nonce + nonce_increment, gas_price + gas_price_increment);
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
let keypair = Random.generate().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
(tx1.sign(secret), tx2.sign(secret))
|
||||
}
|
||||
|
||||
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
new_txs_with_gas_price_diff(second_nonce, U256::zero())
|
||||
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)
|
||||
}
|
||||
|
||||
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
let nonce = U256::from(123);
|
||||
let tx = new_unsigned_tx(nonce);
|
||||
let mut tx2 = new_unsigned_tx(nonce + second_nonce);
|
||||
tx2.gas_price = tx2.gas_price + gas_price;
|
||||
/// Returns two transactions with identical (sender, nonce) but different gas_price/hash.
|
||||
fn new_similar_tx_pair() -> (SignedTransaction, SignedTransaction) {
|
||||
new_tx_pair_default(0.into(), 1.into())
|
||||
}
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
#[test]
|
||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero());
|
||||
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
||||
let sender = tx1.sender().unwrap();
|
||||
let nonce = tx1.nonce;
|
||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce + U256::one()));
|
||||
|
||||
// when
|
||||
let tx = new_tx(123.into(), 1.into());
|
||||
let res = txq.add(tx.clone(), &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
// No longer the case as we don't even consider a transaction that isn't above a full
|
||||
// queue's minimum gas price.
|
||||
// We may want to reconsider this in the near future so leaving this code in as a
|
||||
// possible alternative.
|
||||
/*
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce));
|
||||
*/
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
||||
minimal: 2.into(),
|
||||
got: 1.into(),
|
||||
});
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(tx2.nonce));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -872,9 +972,10 @@ mod test {
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 1
|
||||
};
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External).unwrap();
|
||||
let mut by_hash = {
|
||||
@@ -911,11 +1012,12 @@ mod test {
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 1
|
||||
};
|
||||
// Create two transactions with same nonce
|
||||
// (same hash)
|
||||
let (tx1, tx2) = new_txs(U256::from(0));
|
||||
let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into());
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External).unwrap();
|
||||
let by_hash = {
|
||||
@@ -931,25 +1033,68 @@ mod test {
|
||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
assert_eq!(set.by_gas_price.len(), 1);
|
||||
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
|
||||
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
|
||||
// Two different orders (imagine nonce changed in the meantime)
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::one());
|
||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
assert_eq!(set.by_gas_price.len(), 1);
|
||||
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
|
||||
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
|
||||
|
||||
// then
|
||||
assert_eq!(by_hash.len(), 1);
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
assert_eq!(set.by_gas_price.len(), 1);
|
||||
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
|
||||
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
|
||||
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_insert_same_transaction_twice_into_set() {
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 2
|
||||
};
|
||||
let tx = new_tx_default();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none());
|
||||
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External).unwrap();
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
||||
assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_give_correct_gas_price_entry_limit() {
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
by_gas_price: Default::default(),
|
||||
limit: 1
|
||||
};
|
||||
|
||||
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
||||
let tx = new_tx_default();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none());
|
||||
assert_eq!(set.gas_price_entry_limit(), 2.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx, tx2) = new_similar_txs();
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
let (tx, tx2) = new_similar_tx_pair();
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
|
||||
// First insert one transaction to future
|
||||
@@ -958,7 +1103,7 @@ mod test {
|
||||
assert_eq!(txq.status().future, 1);
|
||||
|
||||
// now import second transaction to current
|
||||
let res = txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// and then there should be only one transaction in current (the one with higher gas_price)
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@@ -974,10 +1119,10 @@ mod test {
|
||||
fn should_import_tx() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(tx, &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@@ -1003,13 +1148,13 @@ mod test {
|
||||
fn should_not_import_transaction_above_gas_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
let gas = tx.gas;
|
||||
let limit = gas / U256::from(2);
|
||||
txq.set_gas_limit(limit);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(tx, &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
||||
@@ -1026,9 +1171,9 @@ mod test {
|
||||
fn should_drop_transactions_from_senders_without_balance() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
let account = |a: &Address| AccountDetails {
|
||||
nonce: default_nonce(a).nonce,
|
||||
nonce: default_account_details(a).nonce,
|
||||
balance: U256::one()
|
||||
};
|
||||
|
||||
@@ -1049,11 +1194,11 @@ mod test {
|
||||
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(tx, &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
||||
@@ -1069,11 +1214,11 @@ mod test {
|
||||
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, &default_nonce, TransactionOrigin::Local);
|
||||
let res = txq.add(tx, &default_account_details, TransactionOrigin::Local);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@@ -1086,7 +1231,7 @@ mod test {
|
||||
fn should_reject_incorectly_signed_transaction() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_unsigned_tx(U256::from(123));
|
||||
let tx = new_unsigned_tx(123.into(), 1.into());
|
||||
let stx = {
|
||||
let mut s = RlpStream::new_list(9);
|
||||
s.append(&tx.nonce);
|
||||
@@ -1101,7 +1246,7 @@ mod test {
|
||||
decode(s.as_raw())
|
||||
};
|
||||
// when
|
||||
let res = txq.add(stx, &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(stx, &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert!(res.is_err());
|
||||
@@ -1112,11 +1257,11 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@@ -1129,15 +1274,15 @@ mod test {
|
||||
fn should_prioritize_local_transactions_within_same_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx2) = new_similar_txs();
|
||||
let (_, tx2) = new_similar_tx_pair();
|
||||
|
||||
// when
|
||||
// first insert the one with higher gas price
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
// then the one with lower gas price, but local
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@@ -1150,11 +1295,11 @@ mod test {
|
||||
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@@ -1168,11 +1313,11 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.pending_hashes();
|
||||
@@ -1186,11 +1331,11 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(2));
|
||||
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let res1 = txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionImportResult::Current);
|
||||
@@ -1206,13 +1351,13 @@ mod test {
|
||||
#[test]
|
||||
fn should_correctly_update_futures_when_removing() {
|
||||
// given
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let next2_nonce = default_nonce_val() + U256::from(3);
|
||||
let next2_nonce = default_nonce() + U256::from(3);
|
||||
|
||||
let mut txq = TransactionQueue::new();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 2);
|
||||
@@ -1230,19 +1375,19 @@ mod test {
|
||||
fn should_move_transactions_if_gap_filled() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let kp = KeyPair::create().unwrap();
|
||||
let kp = Random.generate().unwrap();
|
||||
let secret = kp.secret();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret);
|
||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(secret);
|
||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(secret);
|
||||
let tx = new_unsigned_tx(123.into(), 1.into()).sign(secret);
|
||||
let tx1 = new_unsigned_tx(124.into(), 1.into()).sign(secret);
|
||||
let tx2 = new_unsigned_tx(125.into(), 1.into()).sign(secret);
|
||||
|
||||
txq.add(tx, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
txq.add(tx2, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
|
||||
// when
|
||||
txq.add(tx1, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1254,9 +1399,9 @@ mod test {
|
||||
fn should_remove_transaction() {
|
||||
// given
|
||||
let mut txq2 = TransactionQueue::new();
|
||||
let (tx, tx2) = new_txs(U256::from(3));
|
||||
txq2.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq2.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
||||
txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq2.status().pending, 1);
|
||||
assert_eq!(txq2.status().future, 1);
|
||||
|
||||
@@ -1275,16 +1420,16 @@ mod test {
|
||||
fn should_move_transactions_to_future_if_gap_introduced() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx3 = new_tx_default();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 3);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx.hash(), &default_nonce);
|
||||
txq.remove_invalid(&tx.hash(), &default_account_details);
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1296,11 +1441,11 @@ mod test {
|
||||
fn should_clear_queue() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// add
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
let stats = txq.status();
|
||||
assert_eq!(stats.pending, 2);
|
||||
|
||||
@@ -1316,60 +1461,38 @@ mod test {
|
||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let sender = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External);
|
||||
let res = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
let t = txq.top_transactions();
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::LimitReached);
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { minimal: 2.into(), got: 1.into() });
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
assert_eq!(t.len(), 1);
|
||||
assert_eq!(t[0], tx);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero());
|
||||
let tx = new_tx();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let sender = tx1.sender().unwrap();
|
||||
let nonce = tx1.nonce;
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce + U256::one()));
|
||||
|
||||
// when
|
||||
let res = txq.add(tx.clone(), &default_nonce, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_limit_future_transactions() {
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
txq.current.set_limit(10);
|
||||
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1));
|
||||
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
|
||||
// when
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx4.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(txq.status().future, 1);
|
||||
@@ -1378,7 +1501,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_transactions_with_old_nonces() {
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
let last_nonce = tx.nonce + U256::one();
|
||||
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
||||
|
||||
@@ -1395,11 +1518,11 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_insert_same_transaction_twice() {
|
||||
// given
|
||||
let nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce + U256::one(),
|
||||
let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(),
|
||||
balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (_tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
|
||||
@@ -1417,16 +1540,16 @@ mod test {
|
||||
fn should_accept_same_transaction_twice_if_removed() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx1.hash(), &default_nonce);
|
||||
txq.remove_invalid(&tx1.hash(), &default_account_details);
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1438,17 +1561,17 @@ mod test {
|
||||
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx3 = new_tx_default();
|
||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 3);
|
||||
|
||||
// when
|
||||
let sender = tx.sender().unwrap();
|
||||
txq.remove_all(sender, default_nonce_val() + U256::one());
|
||||
txq.remove_all(sender, default_nonce() + U256::one());
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1461,8 +1584,8 @@ mod test {
|
||||
init_log();
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let keypair = Random.generate().unwrap();
|
||||
let tx = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret());
|
||||
let tx2 = {
|
||||
let mut tx2 = (*tx).clone();
|
||||
tx2.gas_price = U256::from(200);
|
||||
@@ -1470,8 +1593,8 @@ mod test {
|
||||
};
|
||||
|
||||
// when
|
||||
txq.add(tx, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1484,8 +1607,8 @@ mod test {
|
||||
fn should_replace_same_transaction_when_importing_to_futures() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let keypair = Random.generate().unwrap();
|
||||
let tx0 = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret());
|
||||
let tx1 = {
|
||||
let mut tx1 = (*tx0).clone();
|
||||
tx1.nonce = U256::from(124);
|
||||
@@ -1498,10 +1621,10 @@ mod test {
|
||||
};
|
||||
|
||||
// when
|
||||
txq.add(tx1, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx0, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx0, &default_account_details, TransactionOrigin::External).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@@ -1513,12 +1636,12 @@ mod test {
|
||||
#[test]
|
||||
fn should_recalculate_height_when_removing_from_future() {
|
||||
// given
|
||||
let previous_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
let previous_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::one(), balance:
|
||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 2);
|
||||
@@ -1545,7 +1668,7 @@ mod test {
|
||||
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let tx = new_tx();
|
||||
let tx = new_tx_default();
|
||||
let from = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() };
|
||||
@@ -1561,7 +1684,7 @@ mod test {
|
||||
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||
|
||||
@@ -1579,7 +1702,7 @@ mod test {
|
||||
fn should_return_valid_last_nonce_after_remove_all() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2) = new_txs(U256::from(4));
|
||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
||||
let sender = tx1.sender().unwrap();
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||
@@ -1603,13 +1726,13 @@ mod test {
|
||||
fn should_return_true_if_there_is_local_transaction_pending() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
|
||||
// when
|
||||
assert_eq!(txq.add(tx1, &default_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
assert_eq!(txq.add(tx2, &default_nonce, TransactionOrigin::Local).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.add(tx2, &default_account_details, TransactionOrigin::Local).unwrap(), TransactionImportResult::Current);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.has_local_pending_transactions(), true);
|
||||
@@ -1619,9 +1742,9 @@ mod test {
|
||||
fn should_keep_right_order_in_future() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
default_nonce(a).balance };
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
default_account_details(a).balance };
|
||||
|
||||
// when
|
||||
assert_eq!(txq.add(tx2, &prev_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Future);
|
||||
@@ -1637,27 +1760,26 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let (tx1, tx2, tx2_2, tx3) = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
let nonce = U256::from(123);
|
||||
let tx = new_unsigned_tx(nonce);
|
||||
let tx2 = new_unsigned_tx(nonce + 1.into());
|
||||
let mut tx2_2 = new_unsigned_tx(nonce + 1.into());
|
||||
tx2_2.gas_price = U256::from(5);
|
||||
let tx3 = new_unsigned_tx(nonce + 2.into());
|
||||
let nonce = 123.into();
|
||||
let tx = new_unsigned_tx(nonce, 1.into());
|
||||
let tx2 = new_unsigned_tx(nonce + 1.into(), 1.into());
|
||||
let tx2_2 = new_unsigned_tx(nonce + 1.into(), 5.into());
|
||||
let tx3 = new_unsigned_tx(nonce + 2.into(), 1.into());
|
||||
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
|
||||
};
|
||||
let sender = tx1.sender().unwrap();
|
||||
txq.add(tx1, &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx2, &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx3, &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx1, &default_account_details, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx2, &default_account_details, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx3, &default_account_details, TransactionOrigin::Local).unwrap();
|
||||
assert_eq!(txq.future.by_priority.len(), 0);
|
||||
assert_eq!(txq.current.by_priority.len(), 3);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx2_2, &default_nonce, TransactionOrigin::Local);
|
||||
let res = txq.add(tx2_2, &default_account_details, TransactionOrigin::Local);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into());
|
||||
|
||||
@@ -60,6 +60,7 @@ impl ClientService {
|
||||
config: ClientConfig,
|
||||
spec: &Spec,
|
||||
db_path: &Path,
|
||||
ipc_path: &Path,
|
||||
miner: Arc<Miner>,
|
||||
) -> Result<ClientService, Error>
|
||||
{
|
||||
@@ -86,7 +87,7 @@ impl ClientService {
|
||||
try!(io_service.register_handler(client_io));
|
||||
|
||||
let stop_guard = ::devtools::StopGuard::new();
|
||||
run_ipc(client.clone(), stop_guard.share());
|
||||
run_ipc(ipc_path, client.clone(), stop_guard.share());
|
||||
|
||||
Ok(ClientService {
|
||||
io_service: Arc::new(io_service),
|
||||
@@ -167,10 +168,13 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
|
||||
}
|
||||
|
||||
#[cfg(feature="ipc")]
|
||||
fn run_ipc(client: Arc<Client>, stop: Arc<AtomicBool>) {
|
||||
fn run_ipc(base_path: &Path, client: Arc<Client>, stop: Arc<AtomicBool>) {
|
||||
let mut path = base_path.to_owned();
|
||||
path.push("parity-chain.ipc");
|
||||
let socket_addr = format!("ipc://{}", path.to_string_lossy());
|
||||
::std::thread::spawn(move || {
|
||||
let mut worker = nanoipc::Worker::new(&(client as Arc<BlockChainClient>));
|
||||
worker.add_reqrep("ipc:///tmp/parity-chain.ipc").expect("Ipc expected to initialize with no issues");
|
||||
worker.add_reqrep(&socket_addr).expect("Ipc expected to initialize with no issues");
|
||||
|
||||
while !stop.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
worker.poll();
|
||||
@@ -179,7 +183,7 @@ fn run_ipc(client: Arc<Client>, stop: Arc<AtomicBool>) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature="ipc"))]
|
||||
fn run_ipc(_client: Arc<Client>, _stop: Arc<AtomicBool>) {
|
||||
fn run_ipc(_base_path: &Path, _client: Arc<Client>, _stop: Arc<AtomicBool>) {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -203,6 +207,7 @@ mod tests {
|
||||
ClientConfig::default(),
|
||||
&spec,
|
||||
&path,
|
||||
&path,
|
||||
Arc::new(Miner::with_spec(&spec)),
|
||||
);
|
||||
assert!(service.is_ok());
|
||||
|
||||
@@ -17,10 +17,39 @@
|
||||
//! Account state encoding and decoding
|
||||
|
||||
use account_db::{AccountDB, AccountDBMut};
|
||||
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, TrieDB};
|
||||
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY};
|
||||
use util::rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
|
||||
use util::trie::{TrieDB, Trie};
|
||||
use snapshot::Error;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
// whether an encoded account has code and how it is referred to.
|
||||
#[repr(u8)]
|
||||
enum CodeState {
|
||||
// the account has no code.
|
||||
Empty = 0,
|
||||
// raw code is encoded.
|
||||
Inline = 1,
|
||||
// the code is referred to by hash.
|
||||
Hash = 2,
|
||||
}
|
||||
|
||||
impl CodeState {
|
||||
fn from(x: u8) -> Result<Self, Error> {
|
||||
match x {
|
||||
0 => Ok(CodeState::Empty),
|
||||
1 => Ok(CodeState::Inline),
|
||||
2 => Ok(CodeState::Hash),
|
||||
_ => Err(Error::UnrecognizedCodeState(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn raw(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
// An alternate account structure from ::account::Account.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Account {
|
||||
@@ -57,7 +86,7 @@ impl Account {
|
||||
|
||||
// walk the account's storage trie, returning an RLP item containing the
|
||||
// account properties and the storage.
|
||||
pub fn to_fat_rlp(&self, acct_db: &AccountDB) -> Result<Bytes, Error> {
|
||||
pub fn to_fat_rlp(&self, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
||||
let db = try!(TrieDB::new(acct_db, &self.storage_root));
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
@@ -80,11 +109,14 @@ impl Account {
|
||||
|
||||
// [has_code, code_hash].
|
||||
if self.code_hash == SHA3_EMPTY {
|
||||
account_stream.append(&false).append_empty_data();
|
||||
account_stream.append(&CodeState::Empty.raw()).append_empty_data();
|
||||
} else if used_code.contains(&self.code_hash) {
|
||||
account_stream.append(&CodeState::Hash.raw()).append(&self.code_hash);
|
||||
} else {
|
||||
match acct_db.get(&self.code_hash) {
|
||||
Some(c) => {
|
||||
account_stream.append(&true).append(&c);
|
||||
used_code.insert(self.code_hash.clone());
|
||||
account_stream.append(&CodeState::Inline.raw()).append(&c);
|
||||
}
|
||||
None => {
|
||||
warn!("code lookup failed during snapshot");
|
||||
@@ -99,16 +131,39 @@ impl Account {
|
||||
}
|
||||
|
||||
// decode a fat rlp, and rebuild the storage trie as we go.
|
||||
pub fn from_fat_rlp(acct_db: &mut AccountDBMut, rlp: UntrustedRlp) -> Result<Self, Error> {
|
||||
// returns the account structure along with its newly recovered code,
|
||||
// if it exists.
|
||||
pub fn from_fat_rlp(
|
||||
acct_db: &mut AccountDBMut,
|
||||
rlp: UntrustedRlp,
|
||||
code_map: &HashMap<H256, Bytes>,
|
||||
) -> Result<(Self, Option<Bytes>), Error> {
|
||||
use util::{TrieDBMut, TrieMut};
|
||||
|
||||
let nonce = try!(rlp.val_at(0));
|
||||
let balance = try!(rlp.val_at(1));
|
||||
let code_hash = if try!(rlp.val_at(2)) {
|
||||
let code: Bytes = try!(rlp.val_at(3));
|
||||
acct_db.insert(&code)
|
||||
} else {
|
||||
SHA3_EMPTY
|
||||
let code_state: CodeState = {
|
||||
let raw: u8 = try!(rlp.val_at(2));
|
||||
try!(CodeState::from(raw))
|
||||
};
|
||||
|
||||
// load the code if it exists.
|
||||
let (code_hash, new_code) = match code_state {
|
||||
CodeState::Empty => (SHA3_EMPTY, None),
|
||||
CodeState::Inline => {
|
||||
let code: Bytes = try!(rlp.val_at(3));
|
||||
let code_hash = acct_db.insert(&code);
|
||||
|
||||
(code_hash, Some(code))
|
||||
}
|
||||
CodeState::Hash => {
|
||||
let code_hash = try!(rlp.val_at(3));
|
||||
if let Some(code) = code_map.get(&code_hash) {
|
||||
acct_db.emplace(code_hash.clone(), code.clone());
|
||||
}
|
||||
|
||||
(code_hash, None)
|
||||
}
|
||||
};
|
||||
|
||||
let mut storage_root = H256::zero();
|
||||
@@ -123,12 +178,20 @@ impl Account {
|
||||
try!(storage_trie.insert(&k, &v));
|
||||
}
|
||||
}
|
||||
Ok(Account {
|
||||
|
||||
let acc = Account {
|
||||
nonce: nonce,
|
||||
balance: balance,
|
||||
storage_root: storage_root,
|
||||
code_hash: code_hash,
|
||||
})
|
||||
};
|
||||
|
||||
Ok((acc, new_code))
|
||||
}
|
||||
|
||||
/// Get the account's code hash.
|
||||
pub fn code_hash(&self) -> &H256 {
|
||||
&self.code_hash
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -144,9 +207,11 @@ mod tests {
|
||||
use snapshot::tests::helpers::fill_storage;
|
||||
|
||||
use util::{SHA3_NULL_RLP, SHA3_EMPTY};
|
||||
use util::{Address, FixedHash, H256};
|
||||
use util::{Address, FixedHash, H256, HashDB};
|
||||
use util::rlp::{UntrustedRlp, View};
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
|
||||
use super::Account;
|
||||
|
||||
#[test]
|
||||
@@ -165,9 +230,9 @@ mod tests {
|
||||
let thin_rlp = account.to_thin_rlp();
|
||||
assert_eq!(Account::from_thin_rlp(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr)).unwrap();
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap(), account);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, &Default::default()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -191,8 +256,59 @@ mod tests {
|
||||
let thin_rlp = account.to_thin_rlp();
|
||||
assert_eq!(Account::from_thin_rlp(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr)).unwrap();
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap(), account);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, &Default::default()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_code() {
|
||||
let mut db = get_temp_journal_db();
|
||||
let mut db = &mut **db;
|
||||
|
||||
let addr1 = Address::random();
|
||||
let addr2 = Address::random();
|
||||
|
||||
let code_hash = {
|
||||
let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr1);
|
||||
acct_db.insert(b"this is definitely code")
|
||||
};
|
||||
|
||||
{
|
||||
let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr2);
|
||||
acct_db.emplace(code_hash.clone(), b"this is definitely code".to_vec());
|
||||
}
|
||||
|
||||
let account1 = Account {
|
||||
nonce: 50.into(),
|
||||
balance: 123456789.into(),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
code_hash: code_hash,
|
||||
};
|
||||
|
||||
let account2 = Account {
|
||||
nonce: 400.into(),
|
||||
balance: 98765432123456789usize.into(),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
code_hash: code_hash,
|
||||
};
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
let fat_rlp1 = account1.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
|
||||
let fat_rlp2 = account2.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
|
||||
assert_eq!(used_code.len(), 1);
|
||||
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
|
||||
|
||||
let code_map = HashMap::new();
|
||||
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, &code_map).unwrap();
|
||||
assert!(maybe_code.is_none());
|
||||
assert_eq!(acc, account2);
|
||||
|
||||
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, &code_map).unwrap();
|
||||
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
||||
assert_eq!(acc, account1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ use header::Header;
|
||||
|
||||
use views::BlockView;
|
||||
use util::rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View};
|
||||
use util::rlp::{Compressible, RlpType};
|
||||
use util::{Bytes, Hashable, H256};
|
||||
|
||||
const HEADER_FIELDS: usize = 10;
|
||||
@@ -31,10 +32,10 @@ pub struct AbridgedBlock {
|
||||
}
|
||||
|
||||
impl AbridgedBlock {
|
||||
/// Create from a vector of bytes. Does no verification.
|
||||
pub fn from_raw(rlp: Bytes) -> Self {
|
||||
/// Create from rlp-compressed bytes. Does no verification.
|
||||
pub fn from_raw(compressed: Bytes) -> Self {
|
||||
AbridgedBlock {
|
||||
rlp: rlp,
|
||||
rlp: compressed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +79,7 @@ impl AbridgedBlock {
|
||||
}
|
||||
|
||||
AbridgedBlock {
|
||||
rlp: stream.out(),
|
||||
rlp: UntrustedRlp::new(stream.as_raw()).compress(RlpType::Blocks).to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +87,8 @@ impl AbridgedBlock {
|
||||
///
|
||||
/// 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);
|
||||
let rlp = UntrustedRlp::new(&self.rlp).decompress(RlpType::Blocks);
|
||||
let rlp = UntrustedRlp::new(&rlp);
|
||||
|
||||
let mut header = Header {
|
||||
parent_hash: parent_hash,
|
||||
|
||||
@@ -35,6 +35,10 @@ pub enum Error {
|
||||
IncompleteChain,
|
||||
/// Old starting block in a pruned database.
|
||||
OldBlockPrunedDB,
|
||||
/// Missing code.
|
||||
MissingCode(Vec<H256>),
|
||||
/// Unrecognized code encoding.
|
||||
UnrecognizedCodeState(u8),
|
||||
/// Trie error.
|
||||
Trie(TrieError),
|
||||
/// Decoder error.
|
||||
@@ -51,6 +55,8 @@ impl fmt::Display for Error {
|
||||
Error::IncompleteChain => write!(f, "Cannot create snapshot due to incomplete chain."),
|
||||
Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \
|
||||
a pruned database. Please re-run with the --pruning archive flag."),
|
||||
Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()),
|
||||
Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state),
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Decoder(ref err) => err.fmt(f),
|
||||
Error::Trie(ref err) => err.fmt(f),
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Snapshot creation, restoration, and network service.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
@@ -26,13 +26,15 @@ use engines::Engine;
|
||||
use ids::BlockID;
|
||||
use views::BlockView;
|
||||
|
||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut};
|
||||
use util::{Bytes, Hashable, HashDB, snappy};
|
||||
use util::memorydb::MemoryDB;
|
||||
use util::Mutex;
|
||||
use util::hash::{FixedHash, H256};
|
||||
use util::journaldb::{self, Algorithm, JournalDB};
|
||||
use util::kvdb::Database;
|
||||
use util::rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View, Compressible, RlpType};
|
||||
use util::rlp::SHA3_NULL_RLP;
|
||||
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
|
||||
|
||||
use self::account::Account;
|
||||
use self::block::AbridgedBlock;
|
||||
@@ -331,6 +333,8 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
progress: progress,
|
||||
};
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
// account_key here is the address' hash.
|
||||
for (account_key, account_data) in account_trie.iter() {
|
||||
let account = Account::from_thin_rlp(account_data);
|
||||
@@ -338,7 +342,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
|
||||
let account_db = AccountDB::from_hash(db, account_key_hash);
|
||||
|
||||
let fat_rlp = try!(account.to_fat_rlp(&account_db));
|
||||
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));
|
||||
}
|
||||
@@ -402,6 +406,8 @@ impl ManifestData {
|
||||
pub struct StateRebuilder {
|
||||
db: Box<JournalDB>,
|
||||
state_root: H256,
|
||||
code_map: HashMap<H256, Bytes>, // maps code hashes to code itself.
|
||||
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
|
||||
}
|
||||
|
||||
impl StateRebuilder {
|
||||
@@ -410,6 +416,8 @@ impl StateRebuilder {
|
||||
StateRebuilder {
|
||||
db: journaldb::new(db.clone(), pruning, ::db::COL_STATE),
|
||||
state_root: SHA3_NULL_RLP,
|
||||
code_map: HashMap::new(),
|
||||
missing_code: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,41 +426,57 @@ impl StateRebuilder {
|
||||
let rlp = UntrustedRlp::new(chunk);
|
||||
let account_fat_rlps: Vec<_> = rlp.iter().map(|r| r.as_raw()).collect();
|
||||
let mut pairs = Vec::with_capacity(rlp.item_count());
|
||||
let backing = self.db.backing().clone();
|
||||
|
||||
// initialize the pairs vector with empty values so we have slots to write into.
|
||||
pairs.resize(rlp.item_count(), (H256::new(), Vec::new()));
|
||||
|
||||
let chunk_size = account_fat_rlps.len() / ::num_cpus::get() + 1;
|
||||
|
||||
// new code contained within this chunk.
|
||||
let mut chunk_code = HashMap::new();
|
||||
|
||||
// build account tries in parallel.
|
||||
// Todo [rob] keep a thread pool around so we don't do this per-chunk.
|
||||
try!(scope(|scope| {
|
||||
let mut handles = Vec::new();
|
||||
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
|
||||
let mut db = self.db.boxed_clone();
|
||||
let handle: ScopedJoinHandle<Result<Box<JournalDB>, ::error::Error>> = scope.spawn(move || {
|
||||
try!(rebuild_account_trie(db.as_hashdb_mut(), account_chunk, out_pairs_chunk));
|
||||
let code_map = &self.code_map;
|
||||
let handle: ScopedJoinHandle<Result<_, ::error::Error>> = scope.spawn(move || {
|
||||
let mut db = MemoryDB::new();
|
||||
let status = try!(rebuild_accounts(&mut db, account_chunk, out_pairs_chunk, code_map));
|
||||
|
||||
trace!(target: "snapshot", "thread rebuilt {} account tries", account_chunk.len());
|
||||
Ok(db)
|
||||
Ok((db, status))
|
||||
});
|
||||
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// commit all account tries to the db, but only in this thread.
|
||||
let batch = backing.transaction();
|
||||
// consolidate all edits into the main overlay.
|
||||
for handle in handles {
|
||||
let mut thread_db = try!(handle.join());
|
||||
try!(thread_db.inject(&batch));
|
||||
}
|
||||
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
|
||||
let (thread_db, status): (MemoryDB, _) = try!(handle.join());
|
||||
self.db.consolidate(thread_db);
|
||||
|
||||
chunk_code.extend(status.new_code);
|
||||
|
||||
for (addr_hash, code_hash) in status.missing_code {
|
||||
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, ::error::Error>(())
|
||||
}));
|
||||
|
||||
// patch up all missing code. must be done after collecting all new missing code entries.
|
||||
for (code_hash, code) in chunk_code {
|
||||
for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) {
|
||||
let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash);
|
||||
db.emplace(code_hash, code.clone());
|
||||
}
|
||||
|
||||
self.code_map.insert(code_hash, code);
|
||||
}
|
||||
|
||||
|
||||
// batch trie writes
|
||||
{
|
||||
@@ -467,18 +491,44 @@ impl StateRebuilder {
|
||||
}
|
||||
}
|
||||
|
||||
let batch = backing.transaction();
|
||||
try!(self.db.inject(&batch));
|
||||
let backing = self.db.backing().clone();
|
||||
let mut batch = backing.transaction();
|
||||
try!(self.db.inject(&mut batch));
|
||||
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
|
||||
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check for accounts missing code. Once all chunks have been fed, there should
|
||||
/// be none.
|
||||
pub fn check_missing(&self) -> Result<(), Error> {
|
||||
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
|
||||
match missing.is_empty() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::MissingCode(missing)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the state root of the rebuilder.
|
||||
pub fn state_root(&self) -> H256 { self.state_root }
|
||||
}
|
||||
|
||||
fn rebuild_account_trie(db: &mut HashDB, account_chunk: &[&[u8]], out_chunk: &mut [(H256, Bytes)]) -> Result<(), ::error::Error> {
|
||||
#[derive(Default)]
|
||||
struct RebuiltStatus {
|
||||
new_code: Vec<(H256, Bytes)>, // new code that's become available.
|
||||
missing_code: Vec<(H256, H256)>, // accounts that are missing code.
|
||||
}
|
||||
|
||||
// rebuild a set of accounts and their storage.
|
||||
// returns
|
||||
fn rebuild_accounts(
|
||||
db: &mut HashDB,
|
||||
account_chunk: &[&[u8]],
|
||||
out_chunk: &mut [(H256, Bytes)],
|
||||
code_map: &HashMap<H256, Bytes>
|
||||
) -> Result<RebuiltStatus, ::error::Error>
|
||||
{
|
||||
let mut status = RebuiltStatus::default();
|
||||
for (account_pair, out) in account_chunk.into_iter().zip(out_chunk) {
|
||||
let account_rlp = UntrustedRlp::new(account_pair);
|
||||
|
||||
@@ -490,14 +540,24 @@ fn rebuild_account_trie(db: &mut HashDB, account_chunk: &[&[u8]], out_chunk: &mu
|
||||
let mut acct_db = AccountDBMut::from_hash(db, hash);
|
||||
|
||||
// fill out the storage trie and code while decoding.
|
||||
let acc = try!(Account::from_fat_rlp(&mut acct_db, fat_rlp));
|
||||
let (acc, maybe_code) = try!(Account::from_fat_rlp(&mut acct_db, fat_rlp, code_map));
|
||||
|
||||
let code_hash = acc.code_hash().clone();
|
||||
match maybe_code {
|
||||
Some(code) => status.new_code.push((code_hash, code)),
|
||||
None => {
|
||||
if code_hash != ::util::SHA3_EMPTY && !code_map.contains_key(&code_hash) {
|
||||
status.missing_code.push((hash, code_hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
acc.to_thin_rlp()
|
||||
};
|
||||
|
||||
*out = (hash, thin_rlp);
|
||||
}
|
||||
Ok(())
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
/// Proportion of blocks which we will verify `PoW` for.
|
||||
|
||||
@@ -125,6 +125,8 @@ impl Restoration {
|
||||
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);
|
||||
|
||||
@@ -43,14 +43,16 @@ fn chunk_and_restore(amount: u64) {
|
||||
let bc = BlockChain::new(Default::default(), &genesis, old_db.clone());
|
||||
|
||||
// build the blockchain.
|
||||
let mut batch = old_db.transaction();
|
||||
for _ in 0..amount {
|
||||
let block = canon_chain.generate(&mut finalizer).unwrap();
|
||||
let batch = old_db.transaction();
|
||||
bc.insert_block(&batch, &block, vec![]);
|
||||
bc.insert_block(&mut batch, &block, vec![]);
|
||||
bc.commit();
|
||||
old_db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
old_db.write(batch).unwrap();
|
||||
|
||||
|
||||
let best_hash = bc.best_block_hash();
|
||||
|
||||
// snapshot it.
|
||||
|
||||
@@ -24,7 +24,7 @@ use snapshot::account::Account;
|
||||
use util::hash::{FixedHash, H256};
|
||||
use util::hashdb::HashDB;
|
||||
use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode};
|
||||
use util::trie::{TrieDB, TrieDBMut};
|
||||
use util::trie::{TrieDB, TrieDBMut, Trie};
|
||||
use util::rlp::SHA3_NULL_RLP;
|
||||
|
||||
// the proportion of accounts we will alter each tick.
|
||||
@@ -51,10 +51,12 @@ impl StateProducer {
|
||||
// modify existing accounts.
|
||||
let mut accounts_to_modify: Vec<_> = {
|
||||
let trie = TrieDB::new(&*db, &self.state_root).unwrap();
|
||||
trie.iter()
|
||||
let temp = trie.iter() // binding required due to complicated lifetime stuff
|
||||
.filter(|_| rng.gen::<f32>() < ACCOUNT_CHURN)
|
||||
.map(|(k, v)| (H256::from_slice(&k), v.to_owned()))
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
// sweep once to alter storage tries.
|
||||
|
||||
@@ -72,6 +72,7 @@ fn snap_and_restore() {
|
||||
rebuilder.feed(&chunk).unwrap();
|
||||
}
|
||||
|
||||
rebuilder.check_missing().unwrap();
|
||||
assert_eq!(rebuilder.state_root(), state_root);
|
||||
new_db
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use util::*;
|
||||
use pod_account::*;
|
||||
use account_db::*;
|
||||
|
||||
use std::cell::{Ref, RefCell, Cell};
|
||||
|
||||
@@ -148,7 +147,7 @@ impl Account {
|
||||
}
|
||||
|
||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||
pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 {
|
||||
pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 {
|
||||
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
|
||||
let db = SecTrieDB::new(db, &self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
@@ -225,7 +224,7 @@ impl Account {
|
||||
}
|
||||
|
||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
||||
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
|
||||
pub fn cache_code(&mut self, db: &HashDB) -> bool {
|
||||
// TODO: fill out self.code_cache;
|
||||
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||
self.is_cached() ||
|
||||
@@ -277,7 +276,7 @@ impl Account {
|
||||
}
|
||||
|
||||
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
|
||||
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
|
||||
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB) {
|
||||
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
@@ -300,7 +299,7 @@ impl Account {
|
||||
}
|
||||
|
||||
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
|
||||
pub fn commit_code(&mut self, db: &mut AccountDBMut) {
|
||||
pub fn commit_code(&mut self, db: &mut HashDB) {
|
||||
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
|
||||
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
|
||||
(true, true) => self.code_hash = Some(SHA3_EMPTY),
|
||||
|
||||
@@ -18,8 +18,7 @@ use std::cell::{RefCell, RefMut};
|
||||
use common::*;
|
||||
use engines::Engine;
|
||||
use executive::{Executive, TransactOptions};
|
||||
use evm::Factory as EvmFactory;
|
||||
use account_db::*;
|
||||
use factory::Factories;
|
||||
use trace::FlatTrace;
|
||||
use pod_account::*;
|
||||
use pod_state::{self, PodState};
|
||||
@@ -49,7 +48,7 @@ pub struct State {
|
||||
cache: RefCell<HashMap<Address, Option<Account>>>,
|
||||
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
||||
account_start_nonce: U256,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
}
|
||||
|
||||
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
||||
@@ -58,11 +57,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
|
||||
impl State {
|
||||
/// Creates new state with empty state root
|
||||
#[cfg(test)]
|
||||
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, trie_factory: TrieFactory) -> State {
|
||||
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, factories: Factories) -> State {
|
||||
let mut root = H256::new();
|
||||
{
|
||||
// init trie and reset root too null
|
||||
let _ = trie_factory.create(db.as_hashdb_mut(), &mut root);
|
||||
let _ = factories.trie.create(db.as_hashdb_mut(), &mut root);
|
||||
}
|
||||
|
||||
State {
|
||||
@@ -71,12 +70,12 @@ impl State {
|
||||
cache: RefCell::new(HashMap::new()),
|
||||
snapshots: RefCell::new(Vec::new()),
|
||||
account_start_nonce: account_start_nonce,
|
||||
trie_factory: trie_factory,
|
||||
factories: factories,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new state with existing state root
|
||||
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> {
|
||||
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, factories: Factories) -> Result<State, TrieError> {
|
||||
if !db.as_hashdb().contains(&root) {
|
||||
return Err(TrieError::InvalidStateRoot(root));
|
||||
}
|
||||
@@ -87,7 +86,7 @@ impl State {
|
||||
cache: RefCell::new(HashMap::new()),
|
||||
snapshots: RefCell::new(Vec::new()),
|
||||
account_start_nonce: account_start_nonce,
|
||||
trie_factory: trie_factory,
|
||||
factories: factories
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
@@ -185,8 +184,11 @@ impl State {
|
||||
|
||||
/// Mutate storage of account `address` so that it is `value` for `key`.
|
||||
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
||||
self.ensure_cached(address, false,
|
||||
|a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key)))
|
||||
self.ensure_cached(address, false, |a| a.as_ref().map_or(H256::new(), |a| {
|
||||
let addr_hash = a.address_hash(address);
|
||||
let db = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash);
|
||||
a.storage_at(db.as_hashdb(), key)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Mutate storage of account `a` so that it is `value` for `key`.
|
||||
@@ -236,11 +238,12 @@ impl State {
|
||||
|
||||
/// Execute a given transaction.
|
||||
/// This will change the state accordingly.
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
// let old = self.to_pod();
|
||||
|
||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||
let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options));
|
||||
let vm_factory = self.factories.vm.clone();
|
||||
let e = try!(Executive::new(self, env_info, engine, &vm_factory).transact(t, options));
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
@@ -254,27 +257,27 @@ impl State {
|
||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||
pub fn commit_into(
|
||||
trie_factory: &TrieFactory,
|
||||
factories: &Factories,
|
||||
db: &mut HashDB,
|
||||
root: &mut H256,
|
||||
accounts: &mut HashMap<Address,
|
||||
Option<Account>>
|
||||
accounts: &mut HashMap<Address, Option<Account>>
|
||||
) -> Result<(), Error> {
|
||||
// first, commit the sub trees.
|
||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match a {
|
||||
&mut&mut Some(ref mut account) if account.is_dirty() => {
|
||||
let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address));
|
||||
account.commit_storage(trie_factory, &mut account_db);
|
||||
account.commit_code(&mut account_db);
|
||||
let addr_hash = account.address_hash(address);
|
||||
let mut account_db = factories.accountdb.create(db, addr_hash);
|
||||
account.commit_storage(&factories.trie, account_db.as_hashdb_mut());
|
||||
account.commit_code(account_db.as_hashdb_mut());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut trie = trie_factory.from_existing(db, root).unwrap();
|
||||
let mut trie = factories.trie.from_existing(db, root).unwrap();
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match **a {
|
||||
Some(ref mut account) if account.is_dirty() => {
|
||||
@@ -293,7 +296,7 @@ impl State {
|
||||
/// Commits our cached account changes into the trie.
|
||||
pub fn commit(&mut self) -> Result<(), Error> {
|
||||
assert!(self.snapshots.borrow().is_empty());
|
||||
Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut())
|
||||
Self::commit_into(&self.factories, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut())
|
||||
}
|
||||
|
||||
/// Clear state cache
|
||||
@@ -351,7 +354,7 @@ impl State {
|
||||
where F: FnOnce(&Option<Account>) -> U {
|
||||
let have_key = self.cache.borrow().contains_key(a);
|
||||
if !have_key {
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(a) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
@@ -361,7 +364,8 @@ impl State {
|
||||
if require_code {
|
||||
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
|
||||
let addr_hash = account.address_hash(a);
|
||||
account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash));
|
||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash);
|
||||
account.cache_code(accountdb.as_hashdb());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +384,7 @@ impl State {
|
||||
{
|
||||
let contains_key = self.cache.borrow().contains_key(a);
|
||||
if !contains_key {
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(a) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
@@ -400,7 +404,8 @@ impl State {
|
||||
let account = c.get_mut(a).unwrap().as_mut().unwrap();
|
||||
if require_code {
|
||||
let addr_hash = account.address_hash(a);
|
||||
account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash));
|
||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash);
|
||||
account.cache_code(accountdb.as_hashdb());
|
||||
}
|
||||
account
|
||||
})
|
||||
@@ -421,7 +426,7 @@ impl Clone for State {
|
||||
cache: RefCell::new(self.cache.borrow().clone()),
|
||||
snapshots: RefCell::new(self.snapshots.borrow().clone()),
|
||||
account_start_nonce: self.account_start_nonce.clone(),
|
||||
trie_factory: self.trie_factory.clone(),
|
||||
factories: self.factories.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,8 +469,7 @@ fn should_apply_create_transaction() {
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 0,
|
||||
@@ -525,8 +529,7 @@ fn should_trace_failed_create_transaction() {
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
action: trace::Action::Create(trace::Create {
|
||||
@@ -564,8 +567,7 @@ fn should_trace_call_transaction() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
action: trace::Action::Call(trace::Call {
|
||||
@@ -607,8 +609,7 @@ fn should_trace_basic_call_transaction() {
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
action: trace::Action::Call(trace::Call {
|
||||
@@ -649,8 +650,7 @@ fn should_trace_call_transaction_to_builtin() {
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -693,8 +693,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
}.sign(&"".sha3());
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -738,8 +737,7 @@ fn should_not_trace_callcode() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -801,8 +799,7 @@ fn should_not_trace_delegatecall() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -861,8 +858,7 @@ fn should_trace_failed_call_transaction() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
action: trace::Action::Call(trace::Call {
|
||||
@@ -903,8 +899,7 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -963,8 +958,7 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
@@ -1019,8 +1013,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 0,
|
||||
@@ -1064,8 +1057,7 @@ fn should_trace_failed_subcall_transaction() {
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
@@ -1122,8 +1114,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
@@ -1198,8 +1189,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
@@ -1271,8 +1261,7 @@ fn should_trace_suicide() {
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
|
||||
state.add_balance(&0xa.into(), &50.into());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let result = state.apply(&info, &engine, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
trace_address: Default::default(),
|
||||
subtraces: 1,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethkey::KeyPair;
|
||||
use io::*;
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use common::*;
|
||||
@@ -139,14 +140,13 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
test_spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
||||
let vm_factory = Default::default();
|
||||
let genesis_header = test_spec.genesis_header();
|
||||
|
||||
let mut rolling_timestamp = 40;
|
||||
let mut last_hashes = vec![];
|
||||
let mut last_header = genesis_header.clone();
|
||||
|
||||
let kp = KeyPair::from_secret("".sha3()).unwrap() ;
|
||||
let kp = KeyPair::from_secret("".sha3()).unwrap();
|
||||
let author = kp.address();
|
||||
|
||||
let mut n = 0;
|
||||
@@ -156,7 +156,6 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
||||
// forge block.
|
||||
let mut b = OpenBlock::new(
|
||||
test_engine,
|
||||
&vm_factory,
|
||||
Default::default(),
|
||||
false,
|
||||
db,
|
||||
@@ -260,9 +259,9 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockCh
|
||||
let db = new_db(temp.as_str());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone());
|
||||
|
||||
let batch = db.transaction();
|
||||
let mut batch = db.transaction();
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![]);
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block(block_order, bc.best_block_hash()), vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
@@ -279,9 +278,9 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone());
|
||||
|
||||
|
||||
let batch = db.transaction();
|
||||
let mut batch = db.transaction();
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]);
|
||||
bc.insert_block(&mut batch, &create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]);
|
||||
bc.commit();
|
||||
}
|
||||
db.write(batch).unwrap();
|
||||
|
||||
@@ -142,7 +142,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
||||
false => [0x0]
|
||||
};
|
||||
|
||||
let batch = DBTransaction::new(&tracesdb);
|
||||
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();
|
||||
@@ -261,7 +261,14 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
|
||||
/// Traces of import request's enacted blocks are expected to be already in database
|
||||
/// or to be the currently inserted trace.
|
||||
fn import(&self, batch: &DBTransaction, request: ImportRequest) {
|
||||
fn import(&self, batch: &mut DBTransaction, request: ImportRequest) {
|
||||
// valid (canon): retracted 0, enacted 1 => false, true,
|
||||
// valid (branch): retracted 0, enacted 0 => false, false,
|
||||
// valid (bbcc): retracted 1, enacted 1 => true, true,
|
||||
// invalid: retracted 1, enacted 0 => true, false,
|
||||
let ret = request.retracted != 0;
|
||||
let ena = !request.enacted.is_empty();
|
||||
assert!(!(ret && !ena));
|
||||
// fast return if tracing is disabled
|
||||
if !self.tracing_enabled() {
|
||||
return;
|
||||
@@ -278,7 +285,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
}
|
||||
|
||||
// now let's rebuild the blooms
|
||||
{
|
||||
if !request.enacted.is_empty() {
|
||||
let range_start = request.block_number as Number + 1 - request.enacted.len();
|
||||
let range_end = range_start + request.retracted;
|
||||
let replaced_range = range_start..range_end;
|
||||
@@ -604,8 +611,8 @@ mod tests {
|
||||
|
||||
// import block 0
|
||||
let request = create_simple_import_request(0, block_0.clone());
|
||||
let batch = DBTransaction::new(&db);
|
||||
tracedb.import(&batch, request);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
tracedb.import(&mut batch, request);
|
||||
db.write(batch).unwrap();
|
||||
|
||||
let filter = Filter {
|
||||
@@ -620,8 +627,8 @@ mod tests {
|
||||
|
||||
// import block 1
|
||||
let request = create_simple_import_request(1, block_1.clone());
|
||||
let batch = DBTransaction::new(&db);
|
||||
tracedb.import(&batch, request);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
tracedb.import(&mut batch, request);
|
||||
db.write(batch).unwrap();
|
||||
|
||||
let filter = Filter {
|
||||
@@ -679,8 +686,8 @@ mod tests {
|
||||
|
||||
// import block 0
|
||||
let request = create_simple_import_request(0, block_0.clone());
|
||||
let batch = DBTransaction::new(&db);
|
||||
tracedb.import(&batch, request);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
tracedb.import(&mut batch, request);
|
||||
db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,12 @@ fn update_trace_address(traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
|
||||
let mut subtrace_subtraces_left = 0;
|
||||
traces.into_iter().map(|mut trace| {
|
||||
let is_top_subtrace = trace.trace_address.is_empty();
|
||||
let is_subtrace = trace.trace_address.len() == 1;
|
||||
trace.trace_address.push_front(top_subtrace_index);
|
||||
|
||||
if is_top_subtrace {
|
||||
subtrace_subtraces_left = trace.subtraces;
|
||||
} else {
|
||||
} else if is_subtrace {
|
||||
subtrace_subtraces_left -= 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ pub trait Database {
|
||||
fn tracing_enabled(&self) -> bool;
|
||||
|
||||
/// Imports new block traces.
|
||||
fn import(&self, batch: &DBTransaction, request: ImportRequest);
|
||||
fn import(&self, batch: &mut DBTransaction, request: ImportRequest);
|
||||
|
||||
/// Returns localized trace at given position.
|
||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace>;
|
||||
|
||||
@@ -16,18 +16,16 @@
|
||||
|
||||
//! Transaction data structure.
|
||||
|
||||
use util::{H256, Address, U256, H520};
|
||||
use std::ops::Deref;
|
||||
use util::rlp::*;
|
||||
use util::sha3::*;
|
||||
use util::{UtilError, CryptoError, Bytes, Signature, Secret, ec};
|
||||
use util::crypto::{signature_from_rsv, signature_to_rsv};
|
||||
use std::cell::*;
|
||||
use util::rlp::*;
|
||||
use util::sha3::Hashable;
|
||||
use util::{H256, Address, U256, Bytes};
|
||||
use ethkey::{Signature, sign, Secret, recover, public_to_address, Error as EthkeyError};
|
||||
use error::*;
|
||||
use evm::Schedule;
|
||||
use header::BlockNumber;
|
||||
use ethjson;
|
||||
use ethstore::ethkey::Signature as EthkeySignature;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Binary)]
|
||||
/// Transaction action type.
|
||||
@@ -139,19 +137,17 @@ impl Transaction {
|
||||
|
||||
/// Signs the transaction as coming from `sender`.
|
||||
pub fn sign(self, secret: &Secret) -> SignedTransaction {
|
||||
let sig = ec::sign(secret, &self.hash()).unwrap();
|
||||
self.with_signature(sig.into())
|
||||
let sig = sign(secret, &self.hash()).unwrap();
|
||||
self.with_signature(sig)
|
||||
}
|
||||
|
||||
/// Signs the transaction with signature.
|
||||
pub fn with_signature(self, sig: EthkeySignature) -> SignedTransaction {
|
||||
let sig: H520 = sig.into();
|
||||
let (r, s, v) = signature_to_rsv(&sig);
|
||||
pub fn with_signature(self, sig: Signature) -> SignedTransaction {
|
||||
SignedTransaction {
|
||||
unsigned: self,
|
||||
r: r,
|
||||
s: s,
|
||||
v: v + 27,
|
||||
r: sig.r().into(),
|
||||
s: sig.s().into(),
|
||||
v: sig.v() + 27,
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
@@ -290,12 +286,14 @@ impl SignedTransaction {
|
||||
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } }
|
||||
|
||||
/// Construct a signature object from the sig.
|
||||
pub fn signature(&self) -> Signature { signature_from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
|
||||
pub fn signature(&self) -> Signature {
|
||||
Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v())
|
||||
}
|
||||
|
||||
/// Checks whether the signature has a low 's' value.
|
||||
pub fn check_low_s(&self) -> Result<(), Error> {
|
||||
if !ec::is_low_s(&self.s) {
|
||||
Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)))
|
||||
if !self.signature().is_low_s() {
|
||||
Err(EthkeyError::InvalidSignature.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@@ -307,7 +305,7 @@ impl SignedTransaction {
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3());
|
||||
let s = public_to_address(&try!(recover(&self.signature(), &self.unsigned.hash())));
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
@@ -319,8 +317,8 @@ impl SignedTransaction {
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<SignedTransaction, Error> {
|
||||
if require_low && !ec::is_low_s(&self.s) {
|
||||
return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)));
|
||||
if require_low && !self.signature().is_low_s() {
|
||||
return Err(EthkeyError::InvalidSignature.into())
|
||||
}
|
||||
try!(self.sender());
|
||||
if self.gas < U256::from(self.gas_required(&schedule)) {
|
||||
@@ -368,7 +366,9 @@ fn sender_test() {
|
||||
|
||||
#[test]
|
||||
fn signing() {
|
||||
let key = ::util::crypto::KeyPair::create().unwrap();
|
||||
use ethkey::{Random, Generator};
|
||||
|
||||
let key = Random.generate().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
nonce: U256::from(42),
|
||||
|
||||
@@ -228,6 +228,7 @@ fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use ethkey::{Random, Generator};
|
||||
use header::*;
|
||||
use verification::*;
|
||||
use blockchain::extras::*;
|
||||
@@ -355,7 +356,7 @@ mod tests {
|
||||
good.timestamp = 40;
|
||||
good.number = 10;
|
||||
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let keypair = Random.generate().unwrap();
|
||||
|
||||
let tr1 = Transaction {
|
||||
action: Action::Create,
|
||||
|
||||
Reference in New Issue
Block a user