Merge branch 'master' into thread
This commit is contained in:
commit
57485a73ec
@ -13,6 +13,8 @@ matrix:
|
|||||||
allow_failures:
|
allow_failures:
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
include:
|
include:
|
||||||
|
- rust: stable
|
||||||
|
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||||
- rust: beta
|
- rust: beta
|
||||||
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
@ -52,7 +54,7 @@ after_success: |
|
|||||||
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* &&
|
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* &&
|
||||||
[ $TRAVIS_BRANCH = master ] &&
|
[ $TRAVIS_BRANCH = master ] &&
|
||||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||||
[ $TRAVIS_RUST_VERSION = beta ] &&
|
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||||
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
|
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
|
||||||
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
|
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
|
||||||
pip install --user ghp-import &&
|
pip install --user ghp-import &&
|
||||||
|
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -81,6 +81,15 @@ name = "cfg-if"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"num 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.0.44"
|
version = "0.0.44"
|
||||||
@ -215,16 +224,19 @@ name = "ethcore-rpc"
|
|||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethash 0.9.99",
|
||||||
"ethcore 0.9.99",
|
"ethcore 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
"ethsync 0.9.99",
|
"ethsync 0.9.99",
|
||||||
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-http-server 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -233,6 +245,7 @@ version = "0.9.99"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bigint 0.1.0",
|
"bigint 0.1.0",
|
||||||
|
"chrono 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -272,6 +285,7 @@ dependencies = [
|
|||||||
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -819,6 +833,14 @@ name = "traitobject"
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "transient-hashmap"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeable"
|
name = "typeable"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -172,7 +172,8 @@ fn get_data_size(block_number: u64) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_seedhash(block_number: u64) -> H256 {
|
/// Given the `block_number`, determine the seed hash for Ethash.
|
||||||
|
pub fn get_seedhash(block_number: u64) -> H256 {
|
||||||
let epochs = block_number / ETHASH_EPOCH_LENGTH;
|
let epochs = block_number / ETHASH_EPOCH_LENGTH;
|
||||||
let mut ret: H256 = [0u8; 32];
|
let mut ret: H256 = [0u8; 32];
|
||||||
for _ in 0..epochs {
|
for _ in 0..epochs {
|
||||||
|
@ -24,7 +24,7 @@ mod compute;
|
|||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use compute::Light;
|
use compute::Light;
|
||||||
pub use compute::{quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH};
|
pub use compute::{get_seedhash, quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH};
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ struct LightCache {
|
|||||||
prev: Option<Arc<Light>>,
|
prev: Option<Arc<Light>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lighy/Full cache manager
|
/// Light/Full cache manager.
|
||||||
pub struct EthashManager {
|
pub struct EthashManager {
|
||||||
cache: Mutex<LightCache>,
|
cache: Mutex<LightCache>,
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit f32954b3ddb5af2dc3dc9ec6d9a28bee848fdf70
|
Subproject commit 99afe8f5aad7bca5d0f1b1685390a4dea32d73c3
|
@ -21,7 +21,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
use state::*;
|
use state::*;
|
||||||
use verification::PreVerifiedBlock;
|
use verification::PreverifiedBlock;
|
||||||
|
|
||||||
/// A block, encoded as it is on the block chain.
|
/// A block, encoded as it is on the block chain.
|
||||||
// TODO: rename to Block
|
// TODO: rename to Block
|
||||||
@ -155,9 +155,9 @@ pub struct OpenBlock<'x> {
|
|||||||
/// Just like OpenBlock, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
|
/// Just like OpenBlock, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
|
||||||
/// and collected the uncles.
|
/// and collected the uncles.
|
||||||
///
|
///
|
||||||
/// There is no function available to push a transaction. If you want that you'll need to `reopen()` it.
|
/// There is no function available to push a transaction.
|
||||||
pub struct ClosedBlock<'x> {
|
pub struct ClosedBlock {
|
||||||
open_block: OpenBlock<'x>,
|
block: ExecutedBlock,
|
||||||
uncle_bytes: Bytes,
|
uncle_bytes: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,10 +178,12 @@ impl<'x> OpenBlock<'x> {
|
|||||||
last_hashes: last_hashes,
|
last_hashes: last_hashes,
|
||||||
};
|
};
|
||||||
|
|
||||||
r.block.base.header.set_number(parent.number() + 1);
|
r.block.base.header.parent_hash = parent.hash();
|
||||||
r.block.base.header.set_author(author);
|
r.block.base.header.number = parent.number + 1;
|
||||||
r.block.base.header.set_extra_data(extra_data);
|
r.block.base.header.author = author;
|
||||||
r.block.base.header.set_timestamp_now();
|
r.block.base.header.set_timestamp_now(parent.timestamp());
|
||||||
|
r.block.base.header.extra_data = extra_data;
|
||||||
|
r.block.base.header.note_dirty();
|
||||||
|
|
||||||
engine.populate_from_parent(&mut r.block.base.header, parent);
|
engine.populate_from_parent(&mut r.block.base.header, parent);
|
||||||
engine.on_new_block(&mut r.block);
|
engine.on_new_block(&mut r.block);
|
||||||
@ -218,8 +220,8 @@ impl<'x> OpenBlock<'x> {
|
|||||||
/// NOTE Will check chain constraints and the uncle number but will NOT check
|
/// NOTE Will check chain constraints and the uncle number but will NOT check
|
||||||
/// that the header itself is actually valid.
|
/// that the header itself is actually valid.
|
||||||
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
||||||
if self.block.base.uncles.len() >= self.engine.maximum_uncle_count() {
|
if self.block.base.uncles.len() + 1 > self.engine.maximum_uncle_count() {
|
||||||
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len()}));
|
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len() + 1}));
|
||||||
}
|
}
|
||||||
// TODO: check number
|
// TODO: check number
|
||||||
// TODO: check not a direct ancestor (use last_hashes for that)
|
// TODO: check not a direct ancestor (use last_hashes for that)
|
||||||
@ -259,7 +261,7 @@ impl<'x> OpenBlock<'x> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles.
|
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles.
|
||||||
pub fn close(self) -> ClosedBlock<'x> {
|
pub fn close(self) -> ClosedBlock {
|
||||||
let mut s = self;
|
let mut s = self;
|
||||||
s.engine.on_close_block(&mut s.block);
|
s.engine.on_close_block(&mut s.block);
|
||||||
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
|
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
|
||||||
@ -271,7 +273,10 @@ impl<'x> OpenBlock<'x> {
|
|||||||
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
||||||
s.block.base.header.note_dirty();
|
s.block.base.header.note_dirty();
|
||||||
|
|
||||||
ClosedBlock::new(s, uncle_bytes)
|
ClosedBlock {
|
||||||
|
block: s.block,
|
||||||
|
uncle_bytes: uncle_bytes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,38 +284,40 @@ impl<'x> IsBlock for OpenBlock<'x> {
|
|||||||
fn block(&self) -> &ExecutedBlock { &self.block }
|
fn block(&self) -> &ExecutedBlock { &self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x> IsBlock for ClosedBlock<'x> {
|
impl<'x> IsBlock for ClosedBlock {
|
||||||
fn block(&self) -> &ExecutedBlock { &self.open_block.block }
|
fn block(&self) -> &ExecutedBlock { &self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x> ClosedBlock<'x> {
|
impl ClosedBlock {
|
||||||
fn new(open_block: OpenBlock<'x>, uncle_bytes: Bytes) -> Self {
|
|
||||||
ClosedBlock {
|
|
||||||
open_block: open_block,
|
|
||||||
uncle_bytes: uncle_bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the hash of the header without seal arguments.
|
/// Get the hash of the header without seal arguments.
|
||||||
pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) }
|
pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) }
|
||||||
|
|
||||||
/// Provide a valid seal in order to turn this into a `SealedBlock`.
|
/// Provide a valid seal in order to turn this into a `SealedBlock`.
|
||||||
///
|
///
|
||||||
/// NOTE: This does not check the validity of `seal` with the engine.
|
/// NOTE: This does not check the validity of `seal` with the engine.
|
||||||
pub fn seal(self, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
|
pub fn seal(self, engine: &Engine, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
|
||||||
let mut s = self;
|
let mut s = self;
|
||||||
if seal.len() != s.open_block.engine.seal_fields() {
|
if seal.len() != engine.seal_fields() {
|
||||||
return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()}));
|
return Err(BlockError::InvalidSealArity(Mismatch{expected: engine.seal_fields(), found: seal.len()}));
|
||||||
}
|
}
|
||||||
s.open_block.block.base.header.set_seal(seal);
|
s.block.base.header.set_seal(seal);
|
||||||
Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes })
|
Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn this back into an `OpenBlock`.
|
/// Provide a valid seal in order to turn this into a `SealedBlock`.
|
||||||
pub fn reopen(self) -> OpenBlock<'x> { self.open_block }
|
/// This does check the validity of `seal` with the engine.
|
||||||
|
/// Returns the `ClosedBlock` back again if the seal is no good.
|
||||||
|
pub fn try_seal(self, engine: &Engine, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
|
||||||
|
let mut s = self;
|
||||||
|
s.block.base.header.set_seal(seal);
|
||||||
|
match engine.verify_block_seal(&s.block.base.header) {
|
||||||
|
Err(_) => Err(s),
|
||||||
|
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
pub fn drain(self) -> JournalDB { self.open_block.block.state.drop().1 }
|
pub fn drain(self) -> JournalDB { self.block.state.drop().1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SealedBlock {
|
impl SealedBlock {
|
||||||
@ -332,7 +339,7 @@ impl IsBlock for SealedBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by block header, transactions and uncles
|
/// Enact the block given by block header, transactions and uncles
|
||||||
pub fn enact<'x>(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock<'x>, Error> {
|
pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
{
|
{
|
||||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||||
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
|
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
|
||||||
@ -350,14 +357,14 @@ pub fn enact<'x>(header: &Header, transactions: &[SignedTransaction], uncles: &[
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
pub fn enact_bytes<'x>(block_bytes: &[u8], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock<'x>, Error> {
|
pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
let block = BlockView::new(block_bytes);
|
let block = BlockView::new(block_bytes);
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
|
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
pub fn enact_verified<'x>(block: &PreVerifiedBlock, engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock<'x>, Error> {
|
pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
|
||||||
let view = BlockView::new(&block.bytes);
|
let view = BlockView::new(&block.bytes);
|
||||||
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
|
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
|
||||||
}
|
}
|
||||||
@ -365,7 +372,7 @@ pub fn enact_verified<'x>(block: &PreVerifiedBlock, engine: &'x Engine, db: Jour
|
|||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||||
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> {
|
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<SealedBlock, Error> {
|
||||||
let header = BlockView::new(block_bytes).header_view();
|
let header = BlockView::new(block_bytes).header_view();
|
||||||
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(header.seal())))
|
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(engine, header.seal())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -386,7 +393,7 @@ mod tests {
|
|||||||
let last_hashes = vec![genesis_header.hash()];
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), vec![]);
|
||||||
let b = b.close();
|
let b = b.close();
|
||||||
let _ = b.seal(vec![]);
|
let _ = b.seal(engine.deref(), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -398,7 +405,7 @@ mod tests {
|
|||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_journal_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
engine.spec().ensure_db_good(&mut db);
|
engine.spec().ensure_db_good(&mut db);
|
||||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(vec![]).unwrap();
|
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(engine.deref(), vec![]).unwrap();
|
||||||
let orig_bytes = b.rlp_bytes();
|
let orig_bytes = b.rlp_bytes();
|
||||||
let orig_db = b.drain();
|
let orig_db = b.drain();
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ use service::*;
|
|||||||
use client::BlockStatus;
|
use client::BlockStatus;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
|
|
||||||
known_heap_size!(0, UnVerifiedBlock, VerifyingBlock, PreVerifiedBlock);
|
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
|
||||||
|
|
||||||
const MIN_MEM_LIMIT: usize = 16384;
|
const MIN_MEM_LIMIT: usize = 16384;
|
||||||
const MIN_QUEUE_LIMIT: usize = 512;
|
const MIN_QUEUE_LIMIT: usize = 512;
|
||||||
@ -105,14 +105,14 @@ pub struct BlockQueue {
|
|||||||
max_mem_use: usize,
|
max_mem_use: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnVerifiedBlock {
|
struct UnverifiedBlock {
|
||||||
header: Header,
|
header: Header,
|
||||||
bytes: Bytes,
|
bytes: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VerifyingBlock {
|
struct VerifyingBlock {
|
||||||
hash: H256,
|
hash: H256,
|
||||||
block: Option<PreVerifiedBlock>,
|
block: Option<PreverifiedBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QueueSignal {
|
struct QueueSignal {
|
||||||
@ -134,8 +134,8 @@ impl QueueSignal {
|
|||||||
|
|
||||||
struct Verification {
|
struct Verification {
|
||||||
// All locks must be captured in the order declared here.
|
// All locks must be captured in the order declared here.
|
||||||
unverified: Mutex<VecDeque<UnVerifiedBlock>>,
|
unverified: Mutex<VecDeque<UnverifiedBlock>>,
|
||||||
verified: Mutex<VecDeque<PreVerifiedBlock>>,
|
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||||
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
||||||
bad: Mutex<HashSet<H256>>,
|
bad: Mutex<HashSet<H256>>,
|
||||||
}
|
}
|
||||||
@ -252,7 +252,7 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreVerifiedBlock>, bad: &mut HashSet<H256>) {
|
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
|
||||||
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
||||||
let block = verifying.pop_front().unwrap().block.unwrap();
|
let block = verifying.pop_front().unwrap().block.unwrap();
|
||||||
if bad.contains(&block.header.parent_hash) {
|
if bad.contains(&block.header.parent_hash) {
|
||||||
@ -300,31 +300,31 @@ impl BlockQueue {
|
|||||||
let h = header.hash();
|
let h = header.hash();
|
||||||
{
|
{
|
||||||
if self.processing.read().unwrap().contains(&h) {
|
if self.processing.read().unwrap().contains(&h) {
|
||||||
return Err(ImportError::AlreadyQueued);
|
return Err(x!(ImportError::AlreadyQueued));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bad = self.verification.bad.lock().unwrap();
|
let mut bad = self.verification.bad.lock().unwrap();
|
||||||
if bad.contains(&h) {
|
if bad.contains(&h) {
|
||||||
return Err(ImportError::Bad(None));
|
return Err(x!(ImportError::KnownBad));
|
||||||
}
|
}
|
||||||
|
|
||||||
if bad.contains(&header.parent_hash) {
|
if bad.contains(&header.parent_hash) {
|
||||||
bad.insert(h.clone());
|
bad.insert(h.clone());
|
||||||
return Err(ImportError::Bad(None));
|
return Err(x!(ImportError::KnownBad));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.processing.write().unwrap().insert(h.clone());
|
self.processing.write().unwrap().insert(h.clone());
|
||||||
self.verification.unverified.lock().unwrap().push_back(UnVerifiedBlock { header: header, bytes: bytes });
|
self.verification.unverified.lock().unwrap().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||||
self.more_to_verify.notify_all();
|
self.more_to_verify.notify_all();
|
||||||
Ok(h)
|
Ok(h)
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
||||||
self.verification.bad.lock().unwrap().insert(h.clone());
|
self.verification.bad.lock().unwrap().insert(h.clone());
|
||||||
Err(From::from(err))
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,7 +362,7 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Removes up to `max` verified blocks from the queue
|
/// Removes up to `max` verified blocks from the queue
|
||||||
pub fn drain(&self, max: usize) -> Vec<PreVerifiedBlock> {
|
pub fn drain(&self, max: usize) -> Vec<PreverifiedBlock> {
|
||||||
let mut verified = self.verification.verified.lock().unwrap();
|
let mut verified = self.verification.verified.lock().unwrap();
|
||||||
let count = min(max, verified.len());
|
let count = min(max, verified.len());
|
||||||
let mut result = Vec::with_capacity(count);
|
let mut result = Vec::with_capacity(count);
|
||||||
@ -475,7 +475,7 @@ mod tests {
|
|||||||
match duplicate_import {
|
match duplicate_import {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
match e {
|
match e {
|
||||||
ImportError::AlreadyQueued => {},
|
Error::Import(ImportError::AlreadyQueued) => {},
|
||||||
_ => { panic!("must return AlreadyQueued error"); }
|
_ => { panic!("must return AlreadyQueued error"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ pub trait BlockProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a list of uncles for a given block.
|
/// Get a list of uncles for a given block.
|
||||||
/// Returns None if block deos not exist.
|
/// Returns None if block does not exist.
|
||||||
fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
|
fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
|
||||||
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
|
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
|
||||||
}
|
}
|
||||||
@ -231,6 +231,24 @@ impl BlockProvider for BlockChain {
|
|||||||
|
|
||||||
const COLLECTION_QUEUE_SIZE: usize = 8;
|
const COLLECTION_QUEUE_SIZE: usize = 8;
|
||||||
|
|
||||||
|
pub struct AncestryIter<'a> {
|
||||||
|
current: H256,
|
||||||
|
chain: &'a BlockChain,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for AncestryIter<'a> {
|
||||||
|
type Item = H256;
|
||||||
|
fn next(&mut self) -> Option<H256> {
|
||||||
|
if self.current.is_zero() {
|
||||||
|
Option::None
|
||||||
|
} else {
|
||||||
|
let mut n = self.chain.block_details(&self.current).unwrap().parent;
|
||||||
|
mem::swap(&mut self.current, &mut n);
|
||||||
|
Some(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BlockChain {
|
impl BlockChain {
|
||||||
/// Create new instance of blockchain from given Genesis
|
/// Create new instance of blockchain from given Genesis
|
||||||
pub fn new(config: BlockChainConfig, genesis: &[u8], path: &Path) -> BlockChain {
|
pub fn new(config: BlockChainConfig, genesis: &[u8], path: &Path) -> BlockChain {
|
||||||
@ -490,6 +508,37 @@ impl BlockChain {
|
|||||||
self.extras_db.write(batch).unwrap();
|
self.extras_db.write(batch).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
||||||
|
pub fn ancestry_iter(&self, first: H256) -> Option<AncestryIter> {
|
||||||
|
if self.is_known(&first) {
|
||||||
|
Some(AncestryIter {
|
||||||
|
current: first,
|
||||||
|
chain: &self,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a block's `parent`, find every block header which represents a valid possible uncle.
|
||||||
|
pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option<Vec<Header>> {
|
||||||
|
if !self.is_known(parent) { return None; }
|
||||||
|
|
||||||
|
let mut excluded = HashSet::new();
|
||||||
|
for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) {
|
||||||
|
excluded.extend(self.uncle_hashes(&a).unwrap().into_iter());
|
||||||
|
excluded.insert(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) {
|
||||||
|
ret.extend(self.block_details(&a).unwrap().children.iter()
|
||||||
|
.filter_map(|h| if excluded.contains(h) { None } else { self.block_header(h) })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get inserted block info which is critical to preapre extras updates.
|
/// Get inserted block info which is critical to preapre extras updates.
|
||||||
fn block_info(&self, block_bytes: &[u8]) -> BlockInfo {
|
fn block_info(&self, block_bytes: &[u8]) -> BlockInfo {
|
||||||
let block = BlockView::new(block_bytes);
|
let block = BlockView::new(block_bytes);
|
||||||
@ -773,11 +822,11 @@ impl BlockChain {
|
|||||||
|
|
||||||
blocks.shrink_to_fit();
|
blocks.shrink_to_fit();
|
||||||
block_details.shrink_to_fit();
|
block_details.shrink_to_fit();
|
||||||
block_hashes.shrink_to_fit();
|
block_hashes.shrink_to_fit();
|
||||||
transaction_addresses.shrink_to_fit();
|
transaction_addresses.shrink_to_fit();
|
||||||
block_logs.shrink_to_fit();
|
block_logs.shrink_to_fit();
|
||||||
blocks_blooms.shrink_to_fit();
|
blocks_blooms.shrink_to_fit();
|
||||||
block_receipts.shrink_to_fit();
|
block_receipts.shrink_to_fit();
|
||||||
}
|
}
|
||||||
if self.cache_size().total() < self.max_cache_size.load(AtomicOrder::Relaxed) { break; }
|
if self.cache_size().total() < self.max_cache_size.load(AtomicOrder::Relaxed) { break; }
|
||||||
}
|
}
|
||||||
@ -795,14 +844,15 @@ mod tests {
|
|||||||
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
use blockchain::helpers::generators::{ChainGenerator, ChainIterator};
|
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_blockchain_insert() {
|
fn basic_blockchain_insert() {
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let genesis = canon_chain.next().unwrap().rlp();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
let first = canon_chain.next().unwrap().rlp();
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let first = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||||
let first_hash = BlockView::new(&first).header_view().sha3();
|
let first_hash = BlockView::new(&first).header_view().sha3();
|
||||||
|
|
||||||
@ -827,18 +877,76 @@ mod tests {
|
|||||||
assert_eq!(bc.block_hash(2), None);
|
assert_eq!(bc.block_hash(2), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_ancestry_iter() {
|
||||||
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||||
|
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
|
|
||||||
|
let mut block_hashes = vec![genesis_hash.clone()];
|
||||||
|
for _ in 0..10 {
|
||||||
|
let block = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
block_hashes.push(BlockView::new(&block).header_view().sha3());
|
||||||
|
bc.insert_block(&block, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_hashes.reverse();
|
||||||
|
|
||||||
|
assert_eq!(bc.ancestry_iter(block_hashes[0].clone()).unwrap().collect::<Vec<_>>(), block_hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||||
|
fn test_find_uncles() {
|
||||||
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b1b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
|
let b1a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b2b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
|
let b2a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b3b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
|
let b3a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b4b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
|
let b4a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b5b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
|
let b5a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
|
bc.insert_block(&b1a, vec![]);
|
||||||
|
bc.insert_block(&b1b, vec![]);
|
||||||
|
bc.insert_block(&b2a, vec![]);
|
||||||
|
bc.insert_block(&b2b, vec![]);
|
||||||
|
bc.insert_block(&b3a, vec![]);
|
||||||
|
bc.insert_block(&b3b, vec![]);
|
||||||
|
bc.insert_block(&b4a, vec![]);
|
||||||
|
bc.insert_block(&b4b, vec![]);
|
||||||
|
bc.insert_block(&b5a, vec![]);
|
||||||
|
bc.insert_block(&b5b, vec![]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
[&b4b, &b3b, &b2b].iter().map(|b| BlockView::new(b).header()).collect::<Vec<_>>(),
|
||||||
|
bc.find_uncle_headers(&BlockView::new(&b4a).header_view().sha3(), 3).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||||
fn test_small_fork() {
|
fn test_small_fork() {
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let genesis = canon_chain.next().unwrap().rlp();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
let blocks = canon_chain.clone().take(3).map(|block| block.rlp()).collect::<Vec<_>>();
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let fork = canon_chain.skip(2).fork(1).take(1).next().unwrap().rlp();
|
let b1 = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let b2 = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let b1 = blocks[0].clone();
|
let b3b = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap();
|
||||||
let b2 = blocks[1].clone();
|
let b3a = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let b3a = blocks[2].clone();
|
|
||||||
let b3b = fork;
|
|
||||||
|
|
||||||
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||||
let b1_hash= BlockView::new(&b1).header_view().sha3();
|
let b1_hash= BlockView::new(&b1).header_view().sha3();
|
||||||
@ -922,22 +1030,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reopen_blockchain_db() {
|
fn test_reopen_blockchain_db() {
|
||||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a059262c330941f3fe2a34d16d6e3c7b30d2ceb37c6a0e9a994c494ee1a61d2410885aa4c8bf8e56e264c0c0".from_hex().unwrap();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let b1 = "f90261f901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0cb52de543653d86ccd13ba3ddf8b052525b04231c6884a4db3188a184681d878a0e78628dd45a1f8dc495594d83b76c588a3ee67463260f8b7d4a42f574aeab29aa0e9244cf7503b79c03d3a099e07a80d2dbc77bb0b502d8a89d51ac0d68dd31313b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884562791e580a051b3ecba4e3f2b49c11d42dd0851ec514b1be3138080f72a2b6e83868275d98f8877671f479c414b47f862f86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca09e2709d7ec9bbe6b1bbbf0b2088828d14cd5e8642a1fee22dc74bfa89761a7f9a04bd8813dee4be989accdb708b1c2e325a7e9c695a8024e30e89d6c644e424747c0".from_hex().unwrap();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
let genesis_hash = H256::from_str("5716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2").unwrap();
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let b1_hash = H256::from_str("437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4f").unwrap();
|
let first = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||||
|
let first_hash = BlockView::new(&first).header_view().sha3();
|
||||||
|
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
{
|
{
|
||||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
assert_eq!(bc.best_block_hash(), genesis_hash);
|
assert_eq!(bc.best_block_hash(), genesis_hash);
|
||||||
bc.insert_block(&b1, vec![]);
|
bc.insert_block(&first, vec![]);
|
||||||
assert_eq!(bc.best_block_hash(), b1_hash);
|
assert_eq!(bc.best_block_hash(), first_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
assert_eq!(bc.best_block_hash(), b1_hash);
|
assert_eq!(bc.best_block_hash(), first_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1001,29 +1111,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bloom_filter_simple() {
|
fn test_bloom_filter_simple() {
|
||||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a059262c330941f3fe2a34d16d6e3c7b30d2ceb37c6a0e9a994c494ee1a61d2410885aa4c8bf8e56e264c0c0".from_hex().unwrap();
|
// TODO: From here
|
||||||
|
|
||||||
// block b1 (child of genesis)
|
|
||||||
let b1 = "f90261f901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0cb52de543653d86ccd13ba3ddf8b052525b04231c6884a4db3188a184681d878a0e78628dd45a1f8dc495594d83b76c588a3ee67463260f8b7d4a42f574aeab29aa0e9244cf7503b79c03d3a099e07a80d2dbc77bb0b502d8a89d51ac0d68dd31313b90100000000200000000000000000000000000000000000000000020000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080004000000000000000000000020008302000001832fefd882520884562791e580a051b3ecba4e3f2b49c11d42dd0851ec514b1be3138080f72a2b6e83868275d98f8877671f479c414b47f862f86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca09e2709d7ec9bbe6b1bbbf0b2088828d14cd5e8642a1fee22dc74bfa89761a7f9a04bd8813dee4be989accdb708b1c2e325a7e9c695a8024e30e89d6c644e424747c0".from_hex().unwrap();
|
|
||||||
|
|
||||||
// block b2 (child of b1)
|
|
||||||
let b2 = "f902ccf901f9a04ef46c05763fffc5f7e59f92a7ef438ffccbb578e6e5d0f04e3df8a7fa6c02f6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000010000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
|
||||||
|
|
||||||
// prepare for fork (b1a, child of genesis)
|
|
||||||
let b1a = "f902ccf901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004001832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
|
||||||
|
|
||||||
// fork (b2a, child of b1a, with higher total difficulty)
|
|
||||||
let b2a = "f902ccf901f9a0626b0774a7cbdad7bdce07b87d74b6fa91c1c359d725076215d76348f8399f56a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
|
||||||
|
|
||||||
// fork back :)
|
|
||||||
let b3 = "f902ccf901f9a0e6cd7250e4c32b33c906aca30280911c560ac67bd0a05fbeb874f99ac7e7e47aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004003832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
|
||||||
|
|
||||||
let bloom_b1 = H2048::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap();
|
let bloom_b1 = H2048::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap();
|
||||||
|
|
||||||
let bloom_b2 = H2048::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let bloom_b2 = H2048::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
|
|
||||||
let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||||
|
|
||||||
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let mut fork = canon_chain.fork(1);
|
||||||
|
let mut fork_finalizer = finalizer.fork();
|
||||||
|
let b1 = fork.with_bloom(bloom_b1.clone()).generate(&mut fork_finalizer).unwrap();
|
||||||
|
let b2 = fork.with_bloom(bloom_b2.clone()).generate(&mut fork_finalizer).unwrap();
|
||||||
|
let b3 = fork.with_bloom(bloom_ba.clone()).generate(&mut fork_finalizer).unwrap();
|
||||||
|
let b1a = canon_chain.with_bloom(bloom_ba.clone()).generate(&mut finalizer).unwrap();
|
||||||
|
let b2a = canon_chain.with_bloom(bloom_ba.clone()).generate(&mut finalizer).unwrap();
|
||||||
|
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
|
|
||||||
|
64
ethcore/src/blockchain/generator/block.rs
Normal file
64
ethcore/src/blockchain/generator/block.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// 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::rlp::*;
|
||||||
|
use util::{H256, H2048};
|
||||||
|
use util::U256;
|
||||||
|
use util::bytes::Bytes;
|
||||||
|
use header::Header;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
|
||||||
|
use super::fork::Forkable;
|
||||||
|
use super::bloom::WithBloom;
|
||||||
|
use super::complete::CompleteBlock;
|
||||||
|
|
||||||
|
/// Helper structure, used for encoding blocks.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Block {
|
||||||
|
pub header: Header,
|
||||||
|
pub transactions: Vec<SignedTransaction>,
|
||||||
|
pub uncles: Vec<Header>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Block {
|
||||||
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
|
s.begin_list(3);
|
||||||
|
s.append(&self.header);
|
||||||
|
s.append(&self.transactions);
|
||||||
|
s.append(&self.uncles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Forkable for Block {
|
||||||
|
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
||||||
|
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithBloom for Block {
|
||||||
|
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
|
||||||
|
self.header.log_bloom = bloom;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompleteBlock for Block {
|
||||||
|
fn complete(mut self, parent_hash: H256) -> Bytes {
|
||||||
|
self.header.parent_hash = parent_hash;
|
||||||
|
encode(&self).to_vec()
|
||||||
|
}
|
||||||
|
}
|
35
ethcore/src/blockchain/generator/bloom.rs
Normal file
35
ethcore/src/blockchain/generator/bloom.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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::hash::H2048;
|
||||||
|
|
||||||
|
pub trait WithBloom {
|
||||||
|
fn with_bloom(self, bloom: H2048) -> Self where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bloom<'a, I> where I: 'a {
|
||||||
|
pub iter: &'a mut I,
|
||||||
|
pub bloom: H2048,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I> Iterator for Bloom<'a, I> where I: Iterator, <I as Iterator>::Item: WithBloom {
|
||||||
|
type Item = <I as Iterator>::Item;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|item| item.with_bloom(self.bloom.clone()))
|
||||||
|
}
|
||||||
|
}
|
53
ethcore/src/blockchain/generator/complete.rs
Normal file
53
ethcore/src/blockchain/generator/complete.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// 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::hash::H256;
|
||||||
|
use util::bytes::Bytes;
|
||||||
|
use util::sha3::Hashable;
|
||||||
|
use views::BlockView;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct BlockFinalizer {
|
||||||
|
parent_hash: H256
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockFinalizer {
|
||||||
|
pub fn fork(&self) -> Self {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CompleteBlock {
|
||||||
|
fn complete(self, parent_hash: H256) -> Bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Complete<'a, I> where I: 'a {
|
||||||
|
pub iter: &'a mut I,
|
||||||
|
pub finalizer: &'a mut BlockFinalizer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I> Iterator for Complete<'a, I> where I: Iterator, <I as Iterator>::Item: CompleteBlock {
|
||||||
|
type Item = Bytes;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|item| {
|
||||||
|
let rlp = item.complete(self.finalizer.parent_hash.clone());
|
||||||
|
self.finalizer.parent_hash = BlockView::new(&rlp).header_view().sha3();
|
||||||
|
rlp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
42
ethcore/src/blockchain/generator/fork.rs
Normal file
42
ethcore/src/blockchain/generator/fork.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
pub trait Forkable {
|
||||||
|
fn fork(self, fork_number: usize) -> Self where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Fork<I> {
|
||||||
|
pub iter: I,
|
||||||
|
pub fork_number: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Clone for Fork<I> where I: Iterator + Clone {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Fork {
|
||||||
|
iter: self.iter.clone(),
|
||||||
|
fork_number: self.fork_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Iterator for Fork<I> where I: Iterator, <I as Iterator>::Item: Forkable {
|
||||||
|
type Item = <I as Iterator>::Item;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|item| item.fork(self.fork_number))
|
||||||
|
}
|
||||||
|
}
|
@ -14,108 +14,52 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::rlp::*;
|
use util::hash::H2048;
|
||||||
use util::hash::{H256, H2048};
|
use util::numbers::U256;
|
||||||
use util::uint::{U256};
|
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use header::{BlockNumber, Header};
|
use header::BlockNumber;
|
||||||
use transaction::SignedTransaction;
|
use super::fork::Fork;
|
||||||
|
use super::bloom::Bloom;
|
||||||
pub trait Forkable {
|
use super::complete::{BlockFinalizer, CompleteBlock, Complete};
|
||||||
fn fork(self, fork_number: usize) -> Self where Self: Sized;
|
use super::block::Block;
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Fork<I> {
|
|
||||||
iter: I,
|
|
||||||
fork_number: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for Fork<I> where I: Iterator, <I as Iterator>::Item: Forkable {
|
|
||||||
type Item = <I as Iterator>::Item;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.iter.next().map(|item| item.fork(self.fork_number))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait WithBloom {
|
|
||||||
fn with_bloom(self, bloom: H2048) -> Self where Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Bloom<I> {
|
|
||||||
iter: I,
|
|
||||||
bloom: H2048,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for Bloom<I> where I: Iterator, <I as Iterator>::Item: WithBloom {
|
|
||||||
type Item = <I as Iterator>::Item;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.iter.next().map(|item| item.with_bloom(self.bloom.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Chain iterator interface.
|
/// Chain iterator interface.
|
||||||
pub trait ChainIterator: Iterator {
|
pub trait ChainIterator: Iterator + Sized {
|
||||||
/// Should be called to create a fork of current iterator.
|
/// Should be called to create a fork of current iterator.
|
||||||
/// Blocks generated by fork will have lower difficulty than current chain.
|
/// Blocks generated by fork will have lower difficulty than current chain.
|
||||||
fn fork(&mut self, fork_number: usize) -> Fork<Self> where Self: Sized;
|
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
||||||
/// Should be called to make every consecutive block have given bloom.
|
/// Should be called to make every consecutive block have given bloom.
|
||||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self> where Self: Sized;
|
fn with_bloom<'a>(&'a mut self, bloom: H2048) -> Bloom<'a, Self>;
|
||||||
|
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
||||||
|
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
||||||
|
/// Completes and generates block.
|
||||||
|
fn generate<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Option<Bytes> where Self::Item: CompleteBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> ChainIterator for I where I: Iterator + Sized + Clone {
|
impl<I> ChainIterator for I where I: Iterator + Sized {
|
||||||
fn fork(&mut self, fork_number: usize) -> Fork<Self> {
|
fn fork(&self, fork_number: usize) -> Fork<Self> where I: Clone {
|
||||||
Fork {
|
Fork {
|
||||||
iter: self.clone(),
|
iter: self.clone(),
|
||||||
fork_number: fork_number
|
fork_number: fork_number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self> {
|
fn with_bloom<'a>(&'a mut self, bloom: H2048) -> Bloom<'a, Self> {
|
||||||
Bloom {
|
Bloom {
|
||||||
iter: self.clone(),
|
iter: self,
|
||||||
bloom: bloom
|
bloom: bloom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper structure, used for encoding blocks.
|
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> {
|
||||||
#[derive(Default)]
|
Complete {
|
||||||
pub struct Block {
|
iter: self,
|
||||||
header: Header,
|
finalizer: finalizer
|
||||||
transactions: Vec<SignedTransaction>,
|
}
|
||||||
uncles: Vec<Header>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Block {
|
|
||||||
pub fn rlp(&self) -> Bytes {
|
|
||||||
encode(self).to_vec()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Encodable for Block {
|
fn generate<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Option<Bytes> where <I as Iterator>::Item: CompleteBlock {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
self.complete(finalizer).next()
|
||||||
s.begin_list(3);
|
|
||||||
s.append(&self.header);
|
|
||||||
s.append(&self.transactions);
|
|
||||||
s.append(&self.uncles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Forkable for Block {
|
|
||||||
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
|
||||||
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WithBloom for Block {
|
|
||||||
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
|
|
||||||
self.header.log_bloom = bloom;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +68,6 @@ impl WithBloom for Block {
|
|||||||
pub struct ChainGenerator {
|
pub struct ChainGenerator {
|
||||||
/// Next block number.
|
/// Next block number.
|
||||||
number: BlockNumber,
|
number: BlockNumber,
|
||||||
/// Next block parent hash.
|
|
||||||
parent_hash: H256,
|
|
||||||
/// Next block difficulty.
|
/// Next block difficulty.
|
||||||
difficulty: U256,
|
difficulty: U256,
|
||||||
}
|
}
|
||||||
@ -133,7 +75,6 @@ pub struct ChainGenerator {
|
|||||||
impl ChainGenerator {
|
impl ChainGenerator {
|
||||||
fn prepare_block(&self) -> Block {
|
fn prepare_block(&self) -> Block {
|
||||||
let mut block = Block::default();
|
let mut block = Block::default();
|
||||||
block.header.parent_hash = self.parent_hash.clone();
|
|
||||||
block.header.number = self.number;
|
block.header.number = self.number;
|
||||||
block.header.difficulty = self.difficulty;
|
block.header.difficulty = self.difficulty;
|
||||||
block
|
block
|
||||||
@ -144,7 +85,6 @@ impl Default for ChainGenerator {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ChainGenerator {
|
ChainGenerator {
|
||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: H256::default(),
|
|
||||||
difficulty: U256::from(1000),
|
difficulty: U256::from(1000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,30 +96,28 @@ impl Iterator for ChainGenerator {
|
|||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let block = self.prepare_block();
|
let block = self.prepare_block();
|
||||||
self.number += 1;
|
self.number += 1;
|
||||||
self.parent_hash = block.header.hash();
|
|
||||||
Some(block)
|
Some(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use util::hash::H256;
|
use util::hash::{H256, H2048};
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use super::{ChainIterator, ChainGenerator};
|
use blockchain::generator::{ChainIterator, ChainGenerator, BlockFinalizer};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn canon_chain_generator() {
|
fn canon_chain_generator() {
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
|
||||||
let genesis_rlp = canon_chain.next().unwrap().rlp();
|
let genesis_rlp = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let genesis = BlockView::new(&genesis_rlp);
|
let genesis = BlockView::new(&genesis_rlp);
|
||||||
|
|
||||||
assert_eq!(genesis.header_view().parent_hash(), H256::default());
|
assert_eq!(genesis.header_view().parent_hash(), H256::default());
|
||||||
assert_eq!(genesis.header_view().number(), 0);
|
assert_eq!(genesis.header_view().number(), 0);
|
||||||
|
|
||||||
let b1_rlp = canon_chain.next().unwrap().rlp();
|
let b1_rlp = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let b1 = BlockView::new(&b1_rlp);
|
let b1 = BlockView::new(&b1_rlp);
|
||||||
|
|
||||||
assert_eq!(b1.header_view().parent_hash(), genesis.header_view().sha3());
|
assert_eq!(b1.header_view().parent_hash(), genesis.header_view().sha3());
|
||||||
@ -187,17 +125,45 @@ mod tests {
|
|||||||
|
|
||||||
let mut fork_chain = canon_chain.fork(1);
|
let mut fork_chain = canon_chain.fork(1);
|
||||||
|
|
||||||
let b2_rlp_fork = fork_chain.next().unwrap().rlp();
|
let b2_rlp_fork = fork_chain.generate(&mut finalizer.fork()).unwrap();
|
||||||
let b2_fork = BlockView::new(&b2_rlp_fork);
|
let b2_fork = BlockView::new(&b2_rlp_fork);
|
||||||
|
|
||||||
assert_eq!(b2_fork.header_view().parent_hash(), b1.header_view().sha3());
|
assert_eq!(b2_fork.header_view().parent_hash(), b1.header_view().sha3());
|
||||||
assert_eq!(b2_fork.header_view().number(), 2);
|
assert_eq!(b2_fork.header_view().number(), 2);
|
||||||
|
|
||||||
let b2_rlp = canon_chain.next().unwrap().rlp();
|
let b2_rlp = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
let b2 = BlockView::new(&b2_rlp);
|
let b2 = BlockView::new(&b2_rlp);
|
||||||
|
|
||||||
assert_eq!(b2.header_view().parent_hash(), b1.header_view().sha3());
|
assert_eq!(b2.header_view().parent_hash(), b1.header_view().sha3());
|
||||||
assert_eq!(b2.header_view().number(), 2);
|
assert_eq!(b2.header_view().number(), 2);
|
||||||
assert!(b2.header_view().difficulty() > b2_fork.header_view().difficulty());
|
assert!(b2.header_view().difficulty() > b2_fork.header_view().difficulty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_bloom_generator() {
|
||||||
|
let bloom = H2048([0x1; 256]);
|
||||||
|
let mut gen = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
|
||||||
|
let block0_rlp = gen.with_bloom(bloom).generate(&mut finalizer).unwrap();
|
||||||
|
let block1_rlp = gen.generate(&mut finalizer).unwrap();
|
||||||
|
let block0 = BlockView::new(&block0_rlp);
|
||||||
|
let block1 = BlockView::new(&block1_rlp);
|
||||||
|
|
||||||
|
assert_eq!(block0.header_view().number(), 0);
|
||||||
|
assert_eq!(block0.header_view().parent_hash(), H256::default());
|
||||||
|
|
||||||
|
assert_eq!(block1.header_view().number(), 1);
|
||||||
|
assert_eq!(block1.header_view().parent_hash(), block0.header_view().sha3());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_1000_blocks() {
|
||||||
|
let generator = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let blocks: Vec<_> = generator.take(1000).complete(&mut finalizer).collect();
|
||||||
|
assert_eq!(blocks.len(), 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
24
ethcore/src/blockchain/generator/mod.rs
Normal file
24
ethcore/src/blockchain/generator/mod.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
mod bloom;
|
||||||
|
mod block;
|
||||||
|
mod complete;
|
||||||
|
mod fork;
|
||||||
|
pub mod generator;
|
||||||
|
|
||||||
|
pub use self::complete::BlockFinalizer;
|
||||||
|
pub use self::generator::{ChainIterator, ChainGenerator};
|
@ -24,7 +24,7 @@ mod cache;
|
|||||||
mod tree_route;
|
mod tree_route;
|
||||||
mod update;
|
mod update;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod helpers;
|
mod generator;
|
||||||
|
|
||||||
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||||
pub use self::cache::CacheSize;
|
pub use self::cache::CacheSize;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Multilevel blockchain bloom filter.
|
//! Multilevel blockchain bloom filter.
|
||||||
//!
|
//!
|
||||||
//! ```not_run
|
//! ```not_run
|
||||||
//! extern crate ethcore_util as util;
|
//! extern crate ethcore_util as util;
|
||||||
//! extern crate ethcore;
|
//! extern crate ethcore;
|
||||||
@ -23,33 +23,33 @@
|
|||||||
//! use util::sha3::*;
|
//! use util::sha3::*;
|
||||||
//! use util::hash::*;
|
//! use util::hash::*;
|
||||||
//! use ethcore::chainfilter::*;
|
//! use ethcore::chainfilter::*;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let (index_size, bloom_levels) = (16, 3);
|
//! let (index_size, bloom_levels) = (16, 3);
|
||||||
//! let mut cache = MemoryCache::new();
|
//! let mut cache = MemoryCache::new();
|
||||||
//!
|
//!
|
||||||
//! let address = Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap();
|
//! let address = Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap();
|
||||||
//!
|
//!
|
||||||
//! // borrow cache for reading inside the scope
|
//! // borrow cache for reading inside the scope
|
||||||
//! let modified_blooms = {
|
//! let modified_blooms = {
|
||||||
//! let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
//! let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
//! let block_number = 39;
|
//! let block_number = 39;
|
||||||
//! let mut bloom = H2048::new();
|
//! let mut bloom = H2048::new();
|
||||||
//! bloom.shift_bloomed(&address.sha3());
|
//! bloom.shift_bloomed(&address.sha3());
|
||||||
//! filter.add_bloom(&bloom, block_number)
|
//! filter.add_bloom(&bloom, block_number)
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! // number of updated blooms is equal number of levels
|
//! // number of updated blooms is equal number of levels
|
||||||
//! assert_eq!(modified_blooms.len(), bloom_levels as usize);
|
//! assert_eq!(modified_blooms.len(), bloom_levels as usize);
|
||||||
//!
|
//!
|
||||||
//! // lets inserts modified blooms into the cache
|
//! // lets inserts modified blooms into the cache
|
||||||
//! cache.insert_blooms(modified_blooms);
|
//! cache.insert_blooms(modified_blooms);
|
||||||
//!
|
//!
|
||||||
//! // borrow cache for another reading operations
|
//! // borrow cache for another reading operations
|
||||||
//! {
|
//! {
|
||||||
//! let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
//! let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
//! let blocks = filter.blocks_with_address(&address, 10, 40);
|
//! let blocks = filter.blocks_with_address(&address, 10, 40);
|
||||||
//! assert_eq!(blocks.len(), 1);
|
//! assert_eq!(blocks.len(), 1);
|
||||||
//! assert_eq!(blocks[0], 39);
|
//! assert_eq!(blocks[0], 39);
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
@ -71,7 +71,7 @@ pub struct ChainFilter<'a, D>
|
|||||||
impl<'a, D> ChainFilter<'a, D> where D: FilterDataSource
|
impl<'a, D> ChainFilter<'a, D> where D: FilterDataSource
|
||||||
{
|
{
|
||||||
/// Creates new filter instance.
|
/// Creates new filter instance.
|
||||||
///
|
///
|
||||||
/// Borrows `FilterDataSource` for reading.
|
/// Borrows `FilterDataSource` for reading.
|
||||||
pub fn new(data_source: &'a D, index_size: usize, levels: u8) -> Self {
|
pub fn new(data_source: &'a D, index_size: usize, levels: u8) -> Self {
|
||||||
ChainFilter {
|
ChainFilter {
|
||||||
@ -88,7 +88,7 @@ impl<'a, D> ChainFilter<'a, D> where D: FilterDataSource
|
|||||||
None => return None,
|
None => return None,
|
||||||
Some(level_bloom) => match level {
|
Some(level_bloom) => match level {
|
||||||
// if we are on the lowest level
|
// if we are on the lowest level
|
||||||
0 => return match offset < to_block {
|
0 => return match offset <= to_block {
|
||||||
// take the value if its smaller than to_block
|
// take the value if its smaller than to_block
|
||||||
true if level_bloom.contains(bloom) => Some(vec![offset]),
|
true if level_bloom.contains(bloom) => Some(vec![offset]),
|
||||||
// return None if it is is equal to to_block
|
// return None if it is is equal to to_block
|
||||||
@ -153,7 +153,7 @@ impl<'a, D> ChainFilter<'a, D> where D: FilterDataSource
|
|||||||
for i in 0..blooms.len() {
|
for i in 0..blooms.len() {
|
||||||
|
|
||||||
let index = self.indexer.bloom_index(block_number + i, level);
|
let index = self.indexer.bloom_index(block_number + i, level);
|
||||||
let new_bloom = {
|
let new_bloom = {
|
||||||
// use new blooms before db blooms where necessary
|
// use new blooms before db blooms where necessary
|
||||||
let bloom_at = | index | { result.get(&index).cloned().or_else(|| self.data_source.bloom_at_index(&index)) };
|
let bloom_at = | index | { result.get(&index).cloned().or_else(|| self.data_source.bloom_at_index(&index)) };
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use util::sha3::*;
|
|||||||
use chainfilter::{BloomIndex, FilterDataSource, ChainFilter};
|
use chainfilter::{BloomIndex, FilterDataSource, ChainFilter};
|
||||||
|
|
||||||
/// In memory cache for blooms.
|
/// In memory cache for blooms.
|
||||||
///
|
///
|
||||||
/// Stores all blooms in HashMap, which indexes them by `BloomIndex`.
|
/// Stores all blooms in HashMap, which indexes them by `BloomIndex`.
|
||||||
pub struct MemoryCache {
|
pub struct MemoryCache {
|
||||||
blooms: HashMap<BloomIndex, H2048>,
|
blooms: HashMap<BloomIndex, H2048>,
|
||||||
@ -35,7 +35,7 @@ impl MemoryCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// inserts all blooms into cache
|
/// inserts all blooms into cache
|
||||||
///
|
///
|
||||||
/// if bloom at given index already exists, overwrites it
|
/// if bloom at given index already exists, overwrites it
|
||||||
pub fn insert_blooms(&mut self, blooms: HashMap<BloomIndex, H2048>) {
|
pub fn insert_blooms(&mut self, blooms: HashMap<BloomIndex, H2048>) {
|
||||||
self.blooms.extend(blooms);
|
self.blooms.extend(blooms);
|
||||||
@ -81,13 +81,13 @@ fn test_topic_basic_search() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
let blocks = filter.blocks_with_bloom(&to_bloom(&topic), 0, 23);
|
let blocks = filter.blocks_with_bloom(&to_bloom(&topic), 0, 22);
|
||||||
assert_eq!(blocks.len(), 0);
|
assert_eq!(blocks.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
let blocks = filter.blocks_with_bloom(&to_bloom(&topic), 23, 24);
|
let blocks = filter.blocks_with_bloom(&to_bloom(&topic), 23, 23);
|
||||||
assert_eq!(blocks.len(), 1);
|
assert_eq!(blocks.len(), 1);
|
||||||
assert_eq!(blocks[0], 23);
|
assert_eq!(blocks[0], 23);
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ fn test_reset_chain_head_simple() {
|
|||||||
|
|
||||||
cache.insert_blooms(modified_blooms_3);
|
cache.insert_blooms(modified_blooms_3);
|
||||||
|
|
||||||
|
|
||||||
let reset_modified_blooms = {
|
let reset_modified_blooms = {
|
||||||
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
filter.reset_chain_head(&[to_bloom(&topic_4), to_bloom(&topic_5)], 15, 17)
|
filter.reset_chain_head(&[to_bloom(&topic_4), to_bloom(&topic_5)], 15, 17)
|
||||||
@ -183,7 +183,7 @@ fn for_each_bloom<F>(bytes: &[u8], mut f: F) where F: FnMut(usize, &H2048) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn for_each_log<F>(bytes: &[u8], mut f: F) where F: FnMut(usize, &Address, &[H256]) {
|
fn for_each_log<F>(bytes: &[u8], mut f: F) where F: FnMut(usize, &Address, &[H256]) {
|
||||||
let mut reader = BufReader::new(bytes);
|
let mut reader = BufReader::new(bytes);
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
while reader.read_line(&mut line).unwrap() > 0 {
|
while reader.read_line(&mut line).unwrap() > 0 {
|
||||||
{
|
{
|
||||||
@ -235,11 +235,11 @@ fn test_chainfilter_real_data_short_searches() {
|
|||||||
for_each_log(include_bytes!("logs.txt"), | block_number, address, topics | {
|
for_each_log(include_bytes!("logs.txt"), | block_number, address, topics | {
|
||||||
println!("block_number: {:?}", block_number);
|
println!("block_number: {:?}", block_number);
|
||||||
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
let filter = ChainFilter::new(&cache, index_size, bloom_levels);
|
||||||
let blocks = filter.blocks_with_bloom(&to_bloom(address), block_number, block_number + 1);
|
let blocks = filter.blocks_with_bloom(&to_bloom(address), block_number, block_number);
|
||||||
assert_eq!(blocks.len(), 1);
|
assert_eq!(blocks.len(), 1);
|
||||||
for (i, topic) in topics.iter().enumerate() {
|
for (i, topic) in topics.iter().enumerate() {
|
||||||
println!("topic: {:?}", i);
|
println!("topic: {:?}", i);
|
||||||
let blocks = filter.blocks_with_bloom(&to_bloom(topic), block_number, block_number + 1);
|
let blocks = filter.blocks_with_bloom(&to_bloom(topic), block_number, block_number);
|
||||||
assert_eq!(blocks.len(), 1);
|
assert_eq!(blocks.len(), 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
//! Blockchain database client.
|
//! Blockchain database client.
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
use util::*;
|
use util::*;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
use blockchain::{BlockChain, BlockProvider};
|
use blockchain::{BlockChain, BlockProvider};
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use error::*;
|
use error::*;
|
||||||
use header::{BlockNumber, Header};
|
use header::{BlockNumber};
|
||||||
use state::State;
|
use state::State;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
@ -35,6 +37,7 @@ use transaction::LocalizedTransaction;
|
|||||||
use extras::TransactionAddress;
|
use extras::TransactionAddress;
|
||||||
use filter::Filter;
|
use filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
|
use util::keys::store::SecretStore;
|
||||||
pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
|
pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
|
||||||
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
|
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
|
||||||
|
|
||||||
@ -76,12 +79,24 @@ pub enum BlockStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Client configuration. Includes configs for all sub-systems.
|
/// Client configuration. Includes configs for all sub-systems.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct ClientConfig {
|
pub struct ClientConfig {
|
||||||
/// Block queue configuration.
|
/// Block queue configuration.
|
||||||
pub queue: BlockQueueConfig,
|
pub queue: BlockQueueConfig,
|
||||||
/// Blockchain configuration.
|
/// Blockchain configuration.
|
||||||
pub blockchain: BlockChainConfig,
|
pub blockchain: BlockChainConfig,
|
||||||
|
/// Prefer journal rather than archive.
|
||||||
|
pub prefer_journal: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClientConfig {
|
||||||
|
fn default() -> ClientConfig {
|
||||||
|
ClientConfig {
|
||||||
|
queue: Default::default(),
|
||||||
|
blockchain: Default::default(),
|
||||||
|
prefer_journal: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the blockchain gathered together.
|
/// Information about the blockchain gathered together.
|
||||||
@ -123,6 +138,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get block total difficulty.
|
/// Get block total difficulty.
|
||||||
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
||||||
|
|
||||||
|
/// Get block hash.
|
||||||
|
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
||||||
|
|
||||||
/// Get address code.
|
/// Get address code.
|
||||||
fn code(&self, address: &Address) -> Option<Bytes>;
|
fn code(&self, address: &Address) -> Option<Bytes>;
|
||||||
|
|
||||||
@ -176,7 +194,7 @@ pub struct ClientReport {
|
|||||||
|
|
||||||
impl ClientReport {
|
impl ClientReport {
|
||||||
/// Alter internal reporting to reflect the additional `block` has been processed.
|
/// Alter internal reporting to reflect the additional `block` has been processed.
|
||||||
pub fn accrue_block(&mut self, block: &PreVerifiedBlock) {
|
pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
|
||||||
self.blocks_imported += 1;
|
self.blocks_imported += 1;
|
||||||
self.transactions_applied += block.transactions.len();
|
self.transactions_applied += block.transactions.len();
|
||||||
self.gas_processed = self.gas_processed + block.header.gas_used;
|
self.gas_processed = self.gas_processed + block.header.gas_used;
|
||||||
@ -185,7 +203,7 @@ impl ClientReport {
|
|||||||
|
|
||||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||||
pub struct Client {
|
pub struct Client<V = CanonVerifier> where V: Verifier {
|
||||||
chain: Arc<BlockChain>,
|
chain: Arc<BlockChain>,
|
||||||
engine: Arc<Box<Engine>>,
|
engine: Arc<Box<Engine>>,
|
||||||
state_db: Mutex<JournalDB>,
|
state_db: Mutex<JournalDB>,
|
||||||
@ -193,18 +211,33 @@ pub struct Client {
|
|||||||
report: RwLock<ClientReport>,
|
report: RwLock<ClientReport>,
|
||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
panic_handler: Arc<PanicHandler>,
|
panic_handler: Arc<PanicHandler>,
|
||||||
|
|
||||||
|
// for sealing...
|
||||||
|
sealing_enabled: AtomicBool,
|
||||||
|
sealing_block: Mutex<Option<ClosedBlock>>,
|
||||||
|
author: RwLock<Address>,
|
||||||
|
extra_data: RwLock<Bytes>,
|
||||||
|
verifier: PhantomData<V>,
|
||||||
|
secret_store: Arc<RwLock<SecretStore>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const HISTORY: u64 = 1000;
|
const HISTORY: u64 = 1000;
|
||||||
const CLIENT_DB_VER_STR: &'static str = "4.0";
|
const CLIENT_DB_VER_STR: &'static str = "4.0";
|
||||||
|
|
||||||
impl Client {
|
impl Client<CanonVerifier> {
|
||||||
/// Create a new client with given spec and DB path.
|
/// Create a new client with given spec and DB path.
|
||||||
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
||||||
|
Client::<CanonVerifier>::new_with_verifier(config, spec, path, message_channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V> Client<V> where V: Verifier {
|
||||||
|
/// Create a new client with given spec and DB path and custom verifier.
|
||||||
|
pub fn new_with_verifier(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client<V>>, Error> {
|
||||||
let mut dir = path.to_path_buf();
|
let mut dir = path.to_path_buf();
|
||||||
dir.push(H64::from(spec.genesis_header().hash()).hex());
|
dir.push(H64::from(spec.genesis_header().hash()).hex());
|
||||||
//TODO: sec/fat: pruned/full versioning
|
//TODO: sec/fat: pruned/full versioning
|
||||||
dir.push(format!("v{}-sec-pruned", CLIENT_DB_VER_STR));
|
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, if config.prefer_journal { "pruned" } else { "archive" }));
|
||||||
let path = dir.as_path();
|
let path = dir.as_path();
|
||||||
let gb = spec.genesis_block();
|
let gb = spec.genesis_block();
|
||||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, path));
|
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, path));
|
||||||
@ -212,7 +245,7 @@ impl Client {
|
|||||||
state_path.push("state");
|
state_path.push("state");
|
||||||
|
|
||||||
let engine = Arc::new(try!(spec.to_engine()));
|
let engine = Arc::new(try!(spec.to_engine()));
|
||||||
let mut state_db = JournalDB::new(state_path.to_str().unwrap());
|
let mut state_db = JournalDB::from_prefs(state_path.to_str().unwrap(), config.prefer_journal);
|
||||||
if state_db.is_empty() && engine.spec().ensure_db_good(&mut state_db) {
|
if state_db.is_empty() && engine.spec().ensure_db_good(&mut state_db) {
|
||||||
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||||
}
|
}
|
||||||
@ -221,6 +254,9 @@ impl Client {
|
|||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
panic_handler.forward_from(&block_queue);
|
panic_handler.forward_from(&block_queue);
|
||||||
|
|
||||||
|
let secret_store = Arc::new(RwLock::new(SecretStore::new()));
|
||||||
|
secret_store.write().unwrap().try_import_existing();
|
||||||
|
|
||||||
Ok(Arc::new(Client {
|
Ok(Arc::new(Client {
|
||||||
chain: chain,
|
chain: chain,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
@ -228,7 +264,13 @@ impl Client {
|
|||||||
block_queue: block_queue,
|
block_queue: block_queue,
|
||||||
report: RwLock::new(Default::default()),
|
report: RwLock::new(Default::default()),
|
||||||
import_lock: Mutex::new(()),
|
import_lock: Mutex::new(()),
|
||||||
panic_handler: panic_handler
|
panic_handler: panic_handler,
|
||||||
|
sealing_enabled: AtomicBool::new(false),
|
||||||
|
sealing_block: Mutex::new(None),
|
||||||
|
author: RwLock::new(Address::new()),
|
||||||
|
extra_data: RwLock::new(Vec::new()),
|
||||||
|
verifier: PhantomData,
|
||||||
|
secret_store: secret_store,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,10 +279,10 @@ impl Client {
|
|||||||
self.block_queue.flush();
|
self.block_queue.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_last_hashes(&self, header: &Header) -> LastHashes {
|
fn build_last_hashes(&self, parent_hash: H256) -> LastHashes {
|
||||||
let mut last_hashes = LastHashes::new();
|
let mut last_hashes = LastHashes::new();
|
||||||
last_hashes.resize(256, H256::new());
|
last_hashes.resize(256, H256::new());
|
||||||
last_hashes[0] = header.parent_hash.clone();
|
last_hashes[0] = parent_hash;
|
||||||
for i in 0..255 {
|
for i in 0..255 {
|
||||||
match self.chain.block_details(&last_hashes[i]) {
|
match self.chain.block_details(&last_hashes[i]) {
|
||||||
Some(details) => {
|
Some(details) => {
|
||||||
@ -252,12 +294,24 @@ impl Client {
|
|||||||
last_hashes
|
last_hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_and_close_block(&self, block: &PreVerifiedBlock) -> Result<ClosedBlock, ()> {
|
/// Secret store (key manager)
|
||||||
|
pub fn secret_store(&self) -> &Arc<RwLock<SecretStore>> {
|
||||||
|
&self.secret_store
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<ClosedBlock, ()> {
|
||||||
let engine = self.engine.deref().deref();
|
let engine = self.engine.deref().deref();
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
|
|
||||||
|
// Check the block isn't so old we won't be able to enact it.
|
||||||
|
let best_block_number = self.chain.best_block_number();
|
||||||
|
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
||||||
|
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
// Verify Block Family
|
// Verify Block Family
|
||||||
let verify_family_result = verify_block_family(&header, &block.bytes, engine, self.chain.deref());
|
let verify_family_result = V::verify_block_family(&header, &block.bytes, engine, self.chain.deref());
|
||||||
if let Err(e) = verify_family_result {
|
if let Err(e) = verify_family_result {
|
||||||
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -272,7 +326,7 @@ impl Client {
|
|||||||
|
|
||||||
// Enact Verified Block
|
// Enact Verified Block
|
||||||
let parent = chain_has_parent.unwrap();
|
let parent = chain_has_parent.unwrap();
|
||||||
let last_hashes = self.build_last_hashes(header);
|
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||||
let db = self.state_db.lock().unwrap().clone();
|
let db = self.state_db.lock().unwrap().clone();
|
||||||
|
|
||||||
let enact_result = enact_verified(&block, engine, db, &parent, last_hashes);
|
let enact_result = enact_verified(&block, engine, db, &parent, last_hashes);
|
||||||
@ -283,7 +337,7 @@ impl Client {
|
|||||||
|
|
||||||
// Final Verification
|
// Final Verification
|
||||||
let closed_block = enact_result.unwrap();
|
let closed_block = enact_result.unwrap();
|
||||||
if let Err(e) = verify_block_final(&header, closed_block.block().header()) {
|
if let Err(e) = V::verify_block_final(&header, closed_block.block().header()) {
|
||||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
@ -301,6 +355,8 @@ impl Client {
|
|||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
|
|
||||||
|
let original_best = self.chain_info().best_block_hash;
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
|
|
||||||
@ -353,6 +409,10 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.chain_info().best_block_hash != original_best && self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
||||||
|
self.prepare_sealing();
|
||||||
|
}
|
||||||
|
|
||||||
imported
|
imported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,9 +459,85 @@ impl Client {
|
|||||||
BlockId::Latest => Some(self.chain.best_block_number())
|
BlockId::Latest => Some(self.chain.best_block_number())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the author that we will seal blocks as.
|
||||||
|
pub fn author(&self) -> Address {
|
||||||
|
self.author.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the author that we will seal blocks as.
|
||||||
|
pub fn set_author(&self, author: Address) {
|
||||||
|
*self.author.write().unwrap() = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the extra_data that we will seal blocks wuth.
|
||||||
|
pub fn extra_data(&self) -> Bytes {
|
||||||
|
self.extra_data.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the extra_data that we will seal blocks with.
|
||||||
|
pub fn set_extra_data(&self, extra_data: Bytes) {
|
||||||
|
*self.extra_data.write().unwrap() = extra_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// New chain head event. Restart mining operation.
|
||||||
|
pub fn prepare_sealing(&self) {
|
||||||
|
let h = self.chain.best_block_hash();
|
||||||
|
let mut b = OpenBlock::new(
|
||||||
|
self.engine.deref().deref(),
|
||||||
|
self.state_db.lock().unwrap().clone(),
|
||||||
|
match self.chain.block_header(&h) { Some(ref x) => x, None => {return;} },
|
||||||
|
self.build_last_hashes(h.clone()),
|
||||||
|
self.author(),
|
||||||
|
self.extra_data()
|
||||||
|
);
|
||||||
|
|
||||||
|
self.chain.find_uncle_headers(&h, self.engine.deref().deref().maximum_uncle_age()).unwrap().into_iter().take(self.engine.deref().deref().maximum_uncle_count()).foreach(|h| { b.push_uncle(h).unwrap(); });
|
||||||
|
|
||||||
|
// TODO: push transactions.
|
||||||
|
|
||||||
|
let b = b.close();
|
||||||
|
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
||||||
|
*self.sealing_block.lock().unwrap() = Some(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
pub fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
if self.sealing_block.lock().unwrap().is_none() {
|
||||||
|
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
||||||
|
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
||||||
|
self.prepare_sealing();
|
||||||
|
}
|
||||||
|
&self.sealing_block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
pub fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
let mut maybe_b = self.sealing_block.lock().unwrap();
|
||||||
|
match *maybe_b {
|
||||||
|
Some(ref b) if b.hash() == pow_hash => {}
|
||||||
|
_ => { return Err(Error::PowHashInvalid); }
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = maybe_b.take();
|
||||||
|
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
||||||
|
Err(old) => {
|
||||||
|
*maybe_b = Some(old);
|
||||||
|
Err(Error::PowInvalid)
|
||||||
|
}
|
||||||
|
Ok(sealed) => {
|
||||||
|
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
||||||
|
try!(self.import_block(sealed.rlp_bytes()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockChainClient for Client {
|
// TODO: need MinerService MinerIoHandler
|
||||||
|
|
||||||
|
impl<V> BlockChainClient for Client<V> where V: Verifier {
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
||||||
}
|
}
|
||||||
@ -436,6 +572,10 @@ impl BlockChainClient for Client {
|
|||||||
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
||||||
|
Self::block_hash(&self.chain, id)
|
||||||
|
}
|
||||||
|
|
||||||
fn code(&self, address: &Address) -> Option<Bytes> {
|
fn code(&self, address: &Address) -> Option<Bytes> {
|
||||||
self.state().code(address)
|
self.state().code(address)
|
||||||
}
|
}
|
||||||
@ -466,12 +606,14 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_block(&self, bytes: Bytes) -> ImportResult {
|
fn import_block(&self, bytes: Bytes) -> ImportResult {
|
||||||
let header = BlockView::new(&bytes).header();
|
{
|
||||||
if self.chain.is_known(&header.hash()) {
|
let header = BlockView::new(&bytes).header_view();
|
||||||
return Err(ImportError::AlreadyInChain);
|
if self.chain.is_known(&header.sha3()) {
|
||||||
}
|
return Err(x!(ImportError::AlreadyInChain));
|
||||||
if self.block_status(BlockId::Hash(header.parent_hash)) == BlockStatus::Unknown {
|
}
|
||||||
return Err(ImportError::UnknownParent);
|
if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||||
|
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.block_queue.import_block(bytes)
|
self.block_queue.import_block(bytes)
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@ pub trait Engine : Sync + Send {
|
|||||||
|
|
||||||
/// The number of additional header fields required for this engine.
|
/// The number of additional header fields required for this engine.
|
||||||
fn seal_fields(&self) -> usize { 0 }
|
fn seal_fields(&self) -> usize { 0 }
|
||||||
/// Default values of the additional fields RLP-encoded in a raw (non-list) harness.
|
|
||||||
fn seal_rlp(&self) -> Bytes { vec![] }
|
|
||||||
|
|
||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||||
@ -49,6 +47,8 @@ pub trait Engine : Sync + Send {
|
|||||||
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||||
/// Maximum number of uncles a block is allowed to declare.
|
/// Maximum number of uncles a block is allowed to declare.
|
||||||
fn maximum_uncle_count(&self) -> usize { 2 }
|
fn maximum_uncle_count(&self) -> usize { 2 }
|
||||||
|
/// The number of generations back that uncles can be.
|
||||||
|
fn maximum_uncle_age(&self) -> usize { 6 }
|
||||||
/// The nonce with which accounts begin.
|
/// The nonce with which accounts begin.
|
||||||
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
|
||||||
|
|
||||||
@ -76,9 +76,20 @@ pub trait Engine : Sync + Send {
|
|||||||
/// Verify a particular transaction is valid.
|
/// Verify a particular transaction is valid.
|
||||||
fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
|
fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||||
|
|
||||||
/// Don't forget to call Super::populateFromParent when subclassing & overriding.
|
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||||
|
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||||
|
/// methods are needed for an Engine, this may be overridden.
|
||||||
|
fn verify_block_seal(&self, header: &Header) -> Result<(), Error> {
|
||||||
|
self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Don't forget to call Super::populate_from_parent when subclassing & overriding.
|
||||||
// TODO: consider including State in the params.
|
// TODO: consider including State in the params.
|
||||||
fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) {}
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
|
||||||
|
header.difficulty = parent.difficulty;
|
||||||
|
header.gas_limit = parent.gas_limit;
|
||||||
|
header.note_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
|
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
|
||||||
// from Spec into here and removing the Spec::builtins field.
|
// from Spec into here and removing the Spec::builtins field.
|
||||||
|
@ -131,25 +131,14 @@ pub enum BlockError {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Import to the block queue result
|
/// Import to the block queue result
|
||||||
pub enum ImportError {
|
pub enum ImportError {
|
||||||
/// Bad block detected
|
/// Already in the block chain.
|
||||||
Bad(Option<Error>),
|
|
||||||
/// Already in the block chain
|
|
||||||
AlreadyInChain,
|
AlreadyInChain,
|
||||||
/// Already in the block queue
|
/// Already in the block queue.
|
||||||
AlreadyQueued,
|
AlreadyQueued,
|
||||||
/// Unknown parent
|
/// Already marked as bad from a previous import (could mean parent is bad).
|
||||||
UnknownParent,
|
KnownBad,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for ImportError {
|
|
||||||
fn from(err: Error) -> ImportError {
|
|
||||||
ImportError::Bad(Some(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of import block operation.
|
|
||||||
pub type ImportResult = Result<H256, ImportError>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// General error type which should be capable of representing all errors in ethcore.
|
/// General error type which should be capable of representing all errors in ethcore.
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -163,14 +152,29 @@ pub enum Error {
|
|||||||
Execution(ExecutionError),
|
Execution(ExecutionError),
|
||||||
/// Error concerning transaction processing.
|
/// Error concerning transaction processing.
|
||||||
Transaction(TransactionError),
|
Transaction(TransactionError),
|
||||||
|
/// Error concerning block import.
|
||||||
|
Import(ImportError),
|
||||||
|
/// PoW hash is invalid or out of date.
|
||||||
|
PowHashInvalid,
|
||||||
|
/// The value of the nonce or mishash is invalid.
|
||||||
|
PowInvalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Result of import block operation.
|
||||||
|
pub type ImportResult = Result<H256, Error>;
|
||||||
|
|
||||||
impl From<TransactionError> for Error {
|
impl From<TransactionError> for Error {
|
||||||
fn from(err: TransactionError) -> Error {
|
fn from(err: TransactionError) -> Error {
|
||||||
Error::Transaction(err)
|
Error::Transaction(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ImportError> for Error {
|
||||||
|
fn from(err: ImportError) -> Error {
|
||||||
|
Error::Import(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<BlockError> for Error {
|
impl From<BlockError> for Error {
|
||||||
fn from(err: BlockError) -> Error {
|
fn from(err: BlockError) -> Error {
|
||||||
Error::Block(err)
|
Error::Block(err)
|
||||||
|
@ -74,8 +74,6 @@ impl Engine for Ethash {
|
|||||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||||
// Two fields - mix
|
// Two fields - mix
|
||||||
fn seal_fields(&self) -> usize { 2 }
|
fn seal_fields(&self) -> usize { 2 }
|
||||||
// Two empty data items in RLP.
|
|
||||||
fn seal_rlp(&self) -> Bytes { encode(&H64::new()).to_vec() }
|
|
||||||
|
|
||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||||
@ -106,7 +104,7 @@ impl Engine for Ethash {
|
|||||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor)
|
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
header.note_dirty();
|
||||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,9 +142,10 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
|
||||||
&Ethash::to_ethash(header.bare_hash()),
|
&Ethash::to_ethash(header.bare_hash()),
|
||||||
header.nonce().low_u64(),
|
header.nonce().low_u64(),
|
||||||
&Ethash::to_ethash(header.mix_hash()))));
|
&Ethash::to_ethash(header.mix_hash())
|
||||||
|
)));
|
||||||
if difficulty < header.difficulty {
|
if difficulty < header.difficulty {
|
||||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
|
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
|
||||||
}
|
}
|
||||||
@ -241,10 +240,21 @@ impl Ethash {
|
|||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
fn boundary_to_difficulty(boundary: &H256) -> U256 {
|
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
|
||||||
|
pub fn boundary_to_difficulty(boundary: &H256) -> U256 {
|
||||||
U256::from((U512::one() << 256) / x!(U256::from(boundary.as_slice())))
|
U256::from((U512::one() << 256) / x!(U256::from(boundary.as_slice())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
|
||||||
|
pub fn difficulty_to_boundary(difficulty: &U256) -> H256 {
|
||||||
|
x!(U256::from((U512::one() << 256) / x!(difficulty)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given the `block_number`, determine the seed hash for Ethash.
|
||||||
|
pub fn get_seedhash(number: BlockNumber) -> H256 {
|
||||||
|
Self::from_ethash(ethash::get_seedhash(number))
|
||||||
|
}
|
||||||
|
|
||||||
fn to_ethash(hash: H256) -> EH256 {
|
fn to_ethash(hash: H256) -> EH256 {
|
||||||
unsafe { mem::transmute(hash) }
|
unsafe { mem::transmute(hash) }
|
||||||
}
|
}
|
||||||
@ -255,12 +265,20 @@ impl Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
fn nonce(&self) -> H64 {
|
/// Get the none field of the header.
|
||||||
|
pub fn nonce(&self) -> H64 {
|
||||||
decode(&self.seal()[1])
|
decode(&self.seal()[1])
|
||||||
}
|
}
|
||||||
fn mix_hash(&self) -> H256 {
|
|
||||||
|
/// Get the mix hash field of the header.
|
||||||
|
pub fn mix_hash(&self) -> H256 {
|
||||||
decode(&self.seal()[0])
|
decode(&self.seal()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the nonce and mix hash fields of the header.
|
||||||
|
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
|
||||||
|
self.seal = vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -42,6 +42,22 @@ pub struct Filter {
|
|||||||
pub topics: [Option<Vec<H256>>; 4],
|
pub topics: [Option<Vec<H256>>; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Filter {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let mut topics = [None, None, None, None];
|
||||||
|
for i in 0..4 {
|
||||||
|
topics[i] = self.topics[i].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter {
|
||||||
|
from_block: self.from_block.clone(),
|
||||||
|
to_block: self.to_block.clone(),
|
||||||
|
address: self.address.clone(),
|
||||||
|
topics: topics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Filter {
|
impl Filter {
|
||||||
/// Returns combinations of each address and topic.
|
/// Returns combinations of each address and topic.
|
||||||
pub fn bloom_possibilities(&self) -> Vec<H2048> {
|
pub fn bloom_possibilities(&self) -> Vec<H2048> {
|
||||||
|
@ -29,7 +29,7 @@ pub type BlockNumber = u64;
|
|||||||
/// which is non-specific.
|
/// which is non-specific.
|
||||||
///
|
///
|
||||||
/// Doesn't do all that much on its own.
|
/// Doesn't do all that much on its own.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
// TODO: make all private.
|
// TODO: make all private.
|
||||||
/// Parent hash.
|
/// Parent hash.
|
||||||
@ -70,6 +70,25 @@ pub struct Header {
|
|||||||
pub bare_hash: RefCell<Option<H256>>,
|
pub bare_hash: RefCell<Option<H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Header {
|
||||||
|
fn eq(&self, c: &Header) -> bool {
|
||||||
|
self.parent_hash == c.parent_hash &&
|
||||||
|
self.timestamp == c.timestamp &&
|
||||||
|
self.number == c.number &&
|
||||||
|
self.author == c.author &&
|
||||||
|
self.transactions_root == c.transactions_root &&
|
||||||
|
self.uncles_hash == c.uncles_hash &&
|
||||||
|
self.extra_data == c.extra_data &&
|
||||||
|
self.state_root == c.state_root &&
|
||||||
|
self.receipts_root == c.receipts_root &&
|
||||||
|
self.log_bloom == c.log_bloom &&
|
||||||
|
self.gas_used == c.gas_used &&
|
||||||
|
self.gas_limit == c.gas_limit &&
|
||||||
|
self.difficulty == c.difficulty &&
|
||||||
|
self.seal == c.seal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Header {
|
impl Default for Header {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Header {
|
Header {
|
||||||
@ -102,10 +121,12 @@ impl Header {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number field of the header.
|
/// Get the parent_hash field of the header.
|
||||||
pub fn number(&self) -> BlockNumber { self.number }
|
pub fn parent_hash(&self) -> &H256 { &self.parent_hash }
|
||||||
/// Get the timestamp field of the header.
|
/// Get the timestamp field of the header.
|
||||||
pub fn timestamp(&self) -> u64 { self.timestamp }
|
pub fn timestamp(&self) -> u64 { self.timestamp }
|
||||||
|
/// Get the number field of the header.
|
||||||
|
pub fn number(&self) -> BlockNumber { self.number }
|
||||||
/// Get the author field of the header.
|
/// Get the author field of the header.
|
||||||
pub fn author(&self) -> &Address { &self.author }
|
pub fn author(&self) -> &Address { &self.author }
|
||||||
|
|
||||||
@ -127,11 +148,13 @@ impl Header {
|
|||||||
// TODO: seal_at, set_seal_at &c.
|
// TODO: seal_at, set_seal_at &c.
|
||||||
|
|
||||||
/// Set the number field of the header.
|
/// Set the number field of the header.
|
||||||
pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); }
|
pub fn set_parent_hash(&mut self, a: H256) { self.parent_hash = a; self.note_dirty(); }
|
||||||
/// Set the timestamp field of the header.
|
/// Set the timestamp field of the header.
|
||||||
pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); }
|
pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); }
|
||||||
/// Set the timestamp field of the header to the current time.
|
/// Set the timestamp field of the header to the current time.
|
||||||
pub fn set_timestamp_now(&mut self) { self.timestamp = now_utc().to_timespec().sec as u64; self.note_dirty(); }
|
pub fn set_timestamp_now(&mut self, but_later_than: u64) { self.timestamp = max(now_utc().to_timespec().sec as u64, but_later_than + 1); self.note_dirty(); }
|
||||||
|
/// Set the number field of the header.
|
||||||
|
pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); }
|
||||||
/// Set the author field of the header.
|
/// Set the author field of the header.
|
||||||
pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } }
|
pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } }
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"}
|
|||||||
declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"}
|
declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"}
|
||||||
declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"}
|
declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"}
|
||||||
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
|
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
|
||||||
//declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
|
declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
|
||||||
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}
|
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use client::{BlockChainClient, Client, ClientConfig, BlockId};
|
use client::{BlockChainClient, Client, ClientConfig, BlockId};
|
||||||
|
use block::IsBlock;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use common::*;
|
use common::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
@ -106,3 +107,41 @@ fn can_collect_garbage() {
|
|||||||
client.tick();
|
client.tick();
|
||||||
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_handle_long_fork() {
|
||||||
|
let client_result = generate_dummy_client(1200);
|
||||||
|
let client = client_result.reference();
|
||||||
|
for _ in 0..10 {
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
}
|
||||||
|
assert_eq!(1200, client.chain_info().best_block_number);
|
||||||
|
|
||||||
|
push_blocks_to_client(client, 45, 1201, 800);
|
||||||
|
push_blocks_to_client(client, 49, 1201, 800);
|
||||||
|
push_blocks_to_client(client, 53, 1201, 600);
|
||||||
|
|
||||||
|
for _ in 0..20 {
|
||||||
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
|
}
|
||||||
|
assert_eq!(2000, client.chain_info().best_block_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_mine() {
|
||||||
|
let dummy_blocks = get_good_dummy_block_seq(2);
|
||||||
|
let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]);
|
||||||
|
let client = client_result.reference();
|
||||||
|
let b = client.sealing_block();
|
||||||
|
let pow_hash = {
|
||||||
|
let u = b.lock().unwrap();
|
||||||
|
match *u {
|
||||||
|
Some(ref b) => {
|
||||||
|
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
||||||
|
b.hash()
|
||||||
|
}
|
||||||
|
None => { panic!(); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert!(client.submit_seal(pow_hash, vec![]).is_ok());
|
||||||
|
}
|
||||||
|
@ -156,10 +156,9 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
|||||||
rolling_block_number = rolling_block_number + 1;
|
rolling_block_number = rolling_block_number + 1;
|
||||||
rolling_timestamp = rolling_timestamp + 10;
|
rolling_timestamp = rolling_timestamp + 10;
|
||||||
|
|
||||||
if let Err(_) = client.import_block(create_test_block(&header)) {
|
if let Err(e) = client.import_block(create_test_block(&header)) {
|
||||||
panic!("error importing block which is valid by definition");
|
panic!("error importing block which is valid by definition: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
client.flush_queue();
|
client.flush_queue();
|
||||||
client.import_verified_blocks(&IoChannel::disconnected());
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
@ -170,6 +169,34 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting_number: usize, block_number: usize) {
|
||||||
|
let test_spec = get_test_spec();
|
||||||
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
|
let state_root = test_engine.spec().genesis_header().state_root;
|
||||||
|
let mut rolling_hash = client.chain_info().best_block_hash;
|
||||||
|
let mut rolling_block_number = starting_number as u64;
|
||||||
|
let mut rolling_timestamp = timestamp_salt + starting_number as u64 * 10;
|
||||||
|
|
||||||
|
for _ in 0..block_number {
|
||||||
|
let mut header = Header::new();
|
||||||
|
|
||||||
|
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
header.timestamp = rolling_timestamp;
|
||||||
|
header.number = rolling_block_number;
|
||||||
|
header.parent_hash = rolling_hash;
|
||||||
|
header.state_root = state_root.clone();
|
||||||
|
|
||||||
|
rolling_hash = header.hash();
|
||||||
|
rolling_block_number = rolling_block_number + 1;
|
||||||
|
rolling_timestamp = rolling_timestamp + 10;
|
||||||
|
|
||||||
|
if let Err(e) = client.import_block(create_test_block(&header)) {
|
||||||
|
panic!("error importing block which is valid by definition: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
|
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
|
||||||
let dir = RandomTempPath::new();
|
let dir = RandomTempPath::new();
|
||||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||||
@ -253,18 +280,29 @@ pub fn get_temp_state_in(path: &Path) -> State {
|
|||||||
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
|
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
|
||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let test_engine = test_spec.to_engine().unwrap();
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
let mut parent = test_engine.spec().genesis_header().hash();
|
get_good_dummy_block_fork_seq(1, count, &test_engine.spec().genesis_header().hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_hash: &H256) -> Vec<Bytes> {
|
||||||
|
let test_spec = get_test_spec();
|
||||||
|
let test_engine = test_spec.to_engine().unwrap();
|
||||||
|
let mut rolling_timestamp = start_number as u64 * 10;
|
||||||
|
let mut parent = *parent_hash;
|
||||||
let mut r = Vec::new();
|
let mut r = Vec::new();
|
||||||
for i in 1 .. count + 1 {
|
for i in start_number .. start_number + count + 1 {
|
||||||
let mut block_header = Header::new();
|
let mut block_header = Header::new();
|
||||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
block_header.difficulty = U256::from(i).mul(U256([0, 1, 0, 0]));
|
||||||
block_header.timestamp = i as u64;
|
block_header.timestamp = rolling_timestamp;
|
||||||
block_header.number = i as u64;
|
block_header.number = i as u64;
|
||||||
block_header.parent_hash = parent;
|
block_header.parent_hash = parent;
|
||||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||||
|
|
||||||
parent = block_header.hash();
|
parent = block_header.hash();
|
||||||
|
rolling_timestamp = rolling_timestamp + 10;
|
||||||
|
|
||||||
r.push(create_test_block(&block_header));
|
r.push(create_test_block(&block_header));
|
||||||
|
|
||||||
}
|
}
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
34
ethcore/src/verification/canon_verifier.rs
Normal file
34
ethcore/src/verification/canon_verifier.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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 blockchain::BlockProvider;
|
||||||
|
use engine::Engine;
|
||||||
|
use error::Error;
|
||||||
|
use header::Header;
|
||||||
|
use super::Verifier;
|
||||||
|
use super::verification;
|
||||||
|
|
||||||
|
pub struct CanonVerifier;
|
||||||
|
|
||||||
|
impl Verifier for CanonVerifier {
|
||||||
|
fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
|
||||||
|
verification::verify_block_family(header, bytes, engine, bc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
|
||||||
|
verification::verify_block_final(expected, got)
|
||||||
|
}
|
||||||
|
}
|
25
ethcore/src/verification/mod.rs
Normal file
25
ethcore/src/verification/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
pub mod verification;
|
||||||
|
pub mod verifier;
|
||||||
|
mod canon_verifier;
|
||||||
|
mod noop_verifier;
|
||||||
|
|
||||||
|
pub use self::verification::*;
|
||||||
|
pub use self::verifier::Verifier;
|
||||||
|
pub use self::canon_verifier::CanonVerifier;
|
||||||
|
pub use self::noop_verifier::NoopVerifier;
|
33
ethcore/src/verification/noop_verifier.rs
Normal file
33
ethcore/src/verification/noop_verifier.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 blockchain::BlockProvider;
|
||||||
|
use engine::Engine;
|
||||||
|
use error::Error;
|
||||||
|
use header::Header;
|
||||||
|
use super::Verifier;
|
||||||
|
|
||||||
|
pub struct NoopVerifier;
|
||||||
|
|
||||||
|
impl Verifier for NoopVerifier {
|
||||||
|
fn verify_block_family(_header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_final(_expected: &Header, _got: &Header) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ use engine::Engine;
|
|||||||
use blockchain::*;
|
use blockchain::*;
|
||||||
|
|
||||||
/// Preprocessed block data gathered in `verify_block_unordered` call
|
/// Preprocessed block data gathered in `verify_block_unordered` call
|
||||||
pub struct PreVerifiedBlock {
|
pub struct PreverifiedBlock {
|
||||||
/// Populated block header
|
/// Populated block header
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
/// Populated block transactions
|
/// Populated block transactions
|
||||||
@ -55,13 +55,13 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
|
|||||||
|
|
||||||
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
|
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
|
||||||
/// Still operates on a individual block
|
/// Still operates on a individual block
|
||||||
/// Returns a PreVerifiedBlock structure populated with transactions
|
/// Returns a PreverifiedBlock structure populated with transactions
|
||||||
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreVerifiedBlock, Error> {
|
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> {
|
||||||
try!(engine.verify_block_unordered(&header, Some(&bytes)));
|
try!(engine.verify_block_unordered(&header, Some(&bytes)));
|
||||||
for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
try!(engine.verify_block_unordered(&u, None));
|
try!(engine.verify_block_unordered(&u, None));
|
||||||
}
|
}
|
||||||
// Verify transactions.
|
// Verify transactions.
|
||||||
let mut transactions = Vec::new();
|
let mut transactions = Vec::new();
|
||||||
{
|
{
|
||||||
let v = BlockView::new(&bytes);
|
let v = BlockView::new(&bytes);
|
||||||
@ -70,7 +70,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) ->
|
|||||||
transactions.push(t);
|
transactions.push(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PreVerifiedBlock {
|
Ok(PreverifiedBlock {
|
||||||
header: header,
|
header: header,
|
||||||
transactions: transactions,
|
transactions: transactions,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
@ -78,7 +78,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 3 verification. Check block information against parent and uncles.
|
/// Phase 3 verification. Check block information against parent and uncles.
|
||||||
pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
|
pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
|
||||||
// TODO: verify timestamp
|
// TODO: verify timestamp
|
||||||
let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
|
let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
|
||||||
try!(verify_parent(&header, &parent));
|
try!(verify_parent(&header, &parent));
|
||||||
@ -94,7 +94,7 @@ pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, b
|
|||||||
excluded.insert(header.hash());
|
excluded.insert(header.hash());
|
||||||
let mut hash = header.parent_hash.clone();
|
let mut hash = header.parent_hash.clone();
|
||||||
excluded.insert(hash.clone());
|
excluded.insert(hash.clone());
|
||||||
for _ in 0..6 {
|
for _ in 0..engine.maximum_uncle_age() {
|
||||||
match bc.block_details(&hash) {
|
match bc.block_details(&hash) {
|
||||||
Some(details) => {
|
Some(details) => {
|
||||||
excluded.insert(details.parent.clone());
|
excluded.insert(details.parent.clone());
|
||||||
@ -121,7 +121,7 @@ pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, b
|
|||||||
// (8 Invalid)
|
// (8 Invalid)
|
||||||
|
|
||||||
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
|
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
|
||||||
if depth > 6 {
|
if depth > engine.maximum_uncle_age() as u64 {
|
||||||
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
|
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
|
||||||
}
|
}
|
||||||
else if depth < 1 {
|
else if depth < 1 {
|
||||||
@ -141,7 +141,7 @@ pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, b
|
|||||||
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
|
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
|
||||||
for _ in 0..depth {
|
for _ in 0..depth {
|
||||||
match bc.block_details(&expected_uncle_parent) {
|
match bc.block_details(&expected_uncle_parent) {
|
||||||
Some(details) => {
|
Some(details) => {
|
||||||
expected_uncle_parent = details.parent;
|
expected_uncle_parent = details.parent;
|
||||||
},
|
},
|
||||||
None => break
|
None => break
|
||||||
@ -468,7 +468,7 @@ mod tests {
|
|||||||
header.number = 9;
|
header.number = 9;
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
||||||
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
let mut bad_uncles = good_uncles.clone();
|
let mut bad_uncles = good_uncles.clone();
|
||||||
bad_uncles.push(good_uncle1.clone());
|
bad_uncles.push(good_uncle1.clone());
|
26
ethcore/src/verification/verifier.rs
Normal file
26
ethcore/src/verification/verifier.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 blockchain::BlockProvider;
|
||||||
|
use engine::Engine;
|
||||||
|
use error::Error;
|
||||||
|
use header::Header;
|
||||||
|
|
||||||
|
/// Should be used to verify blocks.
|
||||||
|
pub trait Verifier: Send + Sync {
|
||||||
|
fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>;
|
||||||
|
fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>;
|
||||||
|
}
|
@ -569,7 +569,7 @@ function run_installer()
|
|||||||
if [[ $isSudo == false ]]; then
|
if [[ $isSudo == false ]]; then
|
||||||
apt-get install -q -y sudo
|
apt-get install -q -y sudo
|
||||||
fi
|
fi
|
||||||
curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sudo sh -s -- --yes
|
curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sudo sh -s -- --yes
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ Usage:
|
|||||||
Options:
|
Options:
|
||||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
||||||
or frontier, mainnet, morden, or testnet [default: frontier].
|
or frontier, mainnet, morden, or testnet [default: frontier].
|
||||||
|
--archive Client should not prune the state/storage trie.
|
||||||
-d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
-d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
||||||
|
|
||||||
@ -85,6 +86,10 @@ Options:
|
|||||||
--jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545].
|
--jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545].
|
||||||
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
||||||
|
|
||||||
|
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
||||||
|
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
|
--extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
||||||
|
|
||||||
-l --logging LOGGING Specify the logging level.
|
-l --logging LOGGING Specify the logging level.
|
||||||
-v --version Show information about version.
|
-v --version Show information about version.
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
@ -98,6 +103,7 @@ struct Args {
|
|||||||
flag_chain: String,
|
flag_chain: String,
|
||||||
flag_db_path: String,
|
flag_db_path: String,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
|
flag_archive: bool,
|
||||||
flag_no_bootstrap: bool,
|
flag_no_bootstrap: bool,
|
||||||
flag_listen_address: String,
|
flag_listen_address: String,
|
||||||
flag_public_address: Option<String>,
|
flag_public_address: Option<String>,
|
||||||
@ -114,6 +120,8 @@ struct Args {
|
|||||||
flag_jsonrpc_cors: String,
|
flag_jsonrpc_cors: String,
|
||||||
flag_logging: Option<String>,
|
flag_logging: Option<String>,
|
||||||
flag_version: bool,
|
flag_version: bool,
|
||||||
|
flag_author: String,
|
||||||
|
flag_extra_data: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_log(init: &Option<String>) {
|
fn setup_log(init: &Option<String>) {
|
||||||
@ -196,6 +204,18 @@ impl Configuration {
|
|||||||
self.args.flag_db_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
self.args.flag_db_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn author(&self) -> Address {
|
||||||
|
Address::from_str(&self.args.flag_author).unwrap_or_else(|_| die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_data(&self) -> Bytes {
|
||||||
|
match self.args.flag_extra_data {
|
||||||
|
Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(),
|
||||||
|
None => version_data(),
|
||||||
|
Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn _keys_path(&self) -> String {
|
fn _keys_path(&self) -> String {
|
||||||
self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
||||||
}
|
}
|
||||||
@ -293,9 +313,12 @@ impl Configuration {
|
|||||||
let mut client_config = ClientConfig::default();
|
let mut client_config = ClientConfig::default();
|
||||||
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
|
client_config.prefer_journal = !self.args.flag_archive;
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
let client = service.client().clone();
|
let client = service.client().clone();
|
||||||
|
client.set_author(self.author());
|
||||||
|
client.set_extra_data(self.extra_data());
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client);
|
let sync = EthSync::register(service.network(), sync_config, client);
|
||||||
@ -354,7 +377,6 @@ impl Default for Informant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Informant {
|
impl Informant {
|
||||||
|
|
||||||
fn format_bytes(b: usize) -> String {
|
fn format_bytes(b: usize) -> String {
|
||||||
match binary_prefix(b as f64) {
|
match binary_prefix(b as f64) {
|
||||||
Standalone(bytes) => format!("{} bytes", bytes),
|
Standalone(bytes) => format!("{} bytes", bytes),
|
||||||
|
@ -9,15 +9,18 @@ build = "build.rs"
|
|||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
serde_json = "0.7.0"
|
serde_json = "0.7.0"
|
||||||
jsonrpc-core = "1.2"
|
jsonrpc-core = "1.2"
|
||||||
jsonrpc-http-server = "2.1"
|
jsonrpc-http-server = "2.1"
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
|
ethash = { path = "../ethash" }
|
||||||
ethsync = { path = "../sync" }
|
ethsync = { path = "../sync" }
|
||||||
clippy = { version = "0.0.44", optional = true }
|
clippy = { version = "0.0.44", optional = true }
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
|
transient-hashmap = "0.1"
|
||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -9,8 +9,8 @@ mod inner {
|
|||||||
pub fn main() {
|
pub fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
let src = Path::new("src/lib.rs.in");
|
let src = Path::new("src/v1/types/mod.rs.in");
|
||||||
let dst = Path::new(&out_dir).join("lib.rs");
|
let dst = Path::new(&out_dir).join("mod.rs");
|
||||||
|
|
||||||
let mut registry = syntex::Registry::new();
|
let mut registry = syntex::Registry::new();
|
||||||
|
|
||||||
|
@ -27,9 +27,35 @@ extern crate jsonrpc_http_server;
|
|||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
|
extern crate transient_hashmap;
|
||||||
|
|
||||||
#[cfg(feature = "serde_macros")]
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
include!("lib.rs.in");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "serde_macros"))]
|
pub mod v1;
|
||||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
|
||||||
|
/// Http server.
|
||||||
|
pub struct HttpServer {
|
||||||
|
handler: IoHandler,
|
||||||
|
threads: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpServer {
|
||||||
|
/// Construct new http server object with given number of threads.
|
||||||
|
pub fn new(threads: usize) -> HttpServer {
|
||||||
|
HttpServer {
|
||||||
|
handler: IoHandler::new(),
|
||||||
|
threads: threads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add io delegate.
|
||||||
|
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||||
|
self.handler.add_delegate(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start server asynchronously in new thread
|
||||||
|
pub fn start_async(self, addr: &str, cors_domain: &str) {
|
||||||
|
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
||||||
|
server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
|
||||||
|
|
||||||
pub mod v1;
|
|
||||||
|
|
||||||
/// Http server.
|
|
||||||
pub struct HttpServer {
|
|
||||||
handler: IoHandler,
|
|
||||||
threads: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HttpServer {
|
|
||||||
/// Construct new http server object with given number of threads.
|
|
||||||
pub fn new(threads: usize) -> HttpServer {
|
|
||||||
HttpServer {
|
|
||||||
handler: IoHandler::new(),
|
|
||||||
threads: threads
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add io delegate.
|
|
||||||
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
|
||||||
self.handler.add_delegate(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start server asynchronously in new thread
|
|
||||||
pub fn start_async(self, addr: &str, cors_domain: &str) {
|
|
||||||
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
|
||||||
server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,4 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
pub mod generators;
|
mod poll_manager;
|
||||||
|
mod poll_filter;
|
||||||
|
|
||||||
|
pub use self::poll_manager::PollManager;
|
||||||
|
pub use self::poll_filter::PollFilter;
|
10
rpc/src/v1/helpers/poll_filter.rs
Normal file
10
rpc/src/v1/helpers/poll_filter.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//! Helper type with all filter possibilities.
|
||||||
|
|
||||||
|
use ethcore::filter::Filter;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum PollFilter {
|
||||||
|
Block,
|
||||||
|
PendingTransaction,
|
||||||
|
Logs(Filter)
|
||||||
|
}
|
144
rpc/src/v1/helpers/poll_manager.rs
Normal file
144
rpc/src/v1/helpers/poll_manager.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Indexes all rpc poll requests.
|
||||||
|
|
||||||
|
use transient_hashmap::{TransientHashMap, Timer, StandardTimer};
|
||||||
|
|
||||||
|
/// Lifetime of poll (in seconds).
|
||||||
|
const POLL_LIFETIME: u64 = 60;
|
||||||
|
|
||||||
|
pub type PollId = usize;
|
||||||
|
pub type BlockNumber = u64;
|
||||||
|
|
||||||
|
pub struct PollInfo<F> {
|
||||||
|
pub filter: F,
|
||||||
|
pub block_number: BlockNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> Clone for PollInfo<F> where F: Clone {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
PollInfo {
|
||||||
|
filter: self.filter.clone(),
|
||||||
|
block_number: self.block_number.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indexes all poll requests.
|
||||||
|
///
|
||||||
|
/// Lazily garbage collects unused polls info.
|
||||||
|
pub struct PollManager<F, T = StandardTimer> where T: Timer {
|
||||||
|
polls: TransientHashMap<PollId, PollInfo<F>, T>,
|
||||||
|
next_available_id: PollId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> PollManager<F, StandardTimer> {
|
||||||
|
/// Creates new instance of indexer.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PollManager::new_with_timer(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T> PollManager<F, T> where T: Timer {
|
||||||
|
pub fn new_with_timer(timer: T) -> Self {
|
||||||
|
PollManager {
|
||||||
|
polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer),
|
||||||
|
next_available_id: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns id which can be used for new poll.
|
||||||
|
///
|
||||||
|
/// Stores information when last poll happend.
|
||||||
|
pub fn create_poll(&mut self, filter: F, block: BlockNumber) -> PollId {
|
||||||
|
self.polls.prune();
|
||||||
|
let id = self.next_available_id;
|
||||||
|
self.next_available_id += 1;
|
||||||
|
self.polls.insert(id, PollInfo {
|
||||||
|
filter: filter,
|
||||||
|
block_number: block,
|
||||||
|
});
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates information when last poll happend.
|
||||||
|
pub fn update_poll(&mut self, id: &PollId, block: BlockNumber) {
|
||||||
|
self.polls.prune();
|
||||||
|
if let Some(info) = self.polls.get_mut(id) {
|
||||||
|
info.block_number = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of block when last poll happend.
|
||||||
|
pub fn get_poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
||||||
|
self.polls.prune();
|
||||||
|
self.polls.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes poll info.
|
||||||
|
pub fn remove_poll(&mut self, id: &PollId) {
|
||||||
|
self.polls.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use transient_hashmap::Timer;
|
||||||
|
use v1::helpers::PollManager;
|
||||||
|
|
||||||
|
struct TestTimer<'a> {
|
||||||
|
time: &'a RefCell<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Timer for TestTimer<'a> {
|
||||||
|
fn get_time(&self) -> i64 {
|
||||||
|
*self.time.borrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poll_indexer() {
|
||||||
|
let time = RefCell::new(0);
|
||||||
|
let timer = TestTimer {
|
||||||
|
time: &time,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut indexer = PollManager::new_with_timer(timer);
|
||||||
|
assert_eq!(indexer.create_poll(false, 20), 0);
|
||||||
|
assert_eq!(indexer.create_poll(true, 20), 1);
|
||||||
|
|
||||||
|
*time.borrow_mut() = 10;
|
||||||
|
indexer.update_poll(&0, 21);
|
||||||
|
assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false);
|
||||||
|
assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21);
|
||||||
|
|
||||||
|
*time.borrow_mut() = 30;
|
||||||
|
indexer.update_poll(&1, 23);
|
||||||
|
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
||||||
|
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
|
*time.borrow_mut() = 75;
|
||||||
|
indexer.update_poll(&0, 30);
|
||||||
|
assert!(indexer.get_poll_info(&0).is_none());
|
||||||
|
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
||||||
|
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
|
indexer.remove_poll(&1);
|
||||||
|
assert!(indexer.get_poll_info(&1).is_none());
|
||||||
|
}
|
||||||
|
}
|
@ -15,21 +15,28 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Eth rpc implementation.
|
//! Eth rpc implementation.
|
||||||
use std::sync::{Arc, Weak};
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Weak, Mutex, RwLock};
|
||||||
use ethsync::{EthSync, SyncState};
|
use ethsync::{EthSync, SyncState};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
|
use util::rlp::encode;
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
|
use ethcore::block::{IsBlock};
|
||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
|
//#[macro_use] extern crate log;
|
||||||
|
use ethcore::ethereum::Ethash;
|
||||||
use ethcore::ethereum::denominations::shannon;
|
use ethcore::ethereum::denominations::shannon;
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::{Eth, EthFilter};
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log};
|
||||||
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient {
|
pub struct EthClient {
|
||||||
client: Weak<Client>,
|
client: Weak<Client>,
|
||||||
sync: Weak<EthSync>
|
sync: Weak<EthSync>,
|
||||||
|
hashrates: RwLock<HashMap<H256, u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthClient {
|
impl EthClient {
|
||||||
@ -37,7 +44,8 @@ impl EthClient {
|
|||||||
pub fn new(client: &Arc<Client>, sync: &Arc<EthSync>) -> Self {
|
pub fn new(client: &Arc<Client>, sync: &Arc<EthSync>) -> Self {
|
||||||
EthClient {
|
EthClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
sync: Arc::downgrade(sync)
|
sync: Arc::downgrade(sync),
|
||||||
|
hashrates: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +132,7 @@ impl Eth for EthClient {
|
|||||||
// TODO: return real value of mining once it's implemented.
|
// TODO: return real value of mining once it's implemented.
|
||||||
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => Ok(Value::Bool(false)),
|
Params::None => to_value(&!self.hashrates.read().unwrap().is_empty()),
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +140,7 @@ impl Eth for EthClient {
|
|||||||
// TODO: return real hashrate once we have mining
|
// TODO: return real hashrate once we have mining
|
||||||
fn hashrate(&self, params: Params) -> Result<Value, Error> {
|
fn hashrate(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&U256::zero()),
|
Params::None => to_value(&self.hashrates.read().unwrap().iter().fold(0u64, |sum, (_, v)| sum + v)),
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,32 +216,139 @@ impl Eth for EthClient {
|
|||||||
to_value(&logs)
|
to_value(&logs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn work(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => {
|
||||||
|
let c = take_weak!(self.client);
|
||||||
|
let u = c.sealing_block().lock().unwrap();
|
||||||
|
match *u {
|
||||||
|
Some(ref b) => {
|
||||||
|
let pow_hash = b.hash();
|
||||||
|
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
||||||
|
let seed_hash = Ethash::get_seedhash(b.block().header().number());
|
||||||
|
to_value(&(pow_hash, seed_hash, target))
|
||||||
|
}
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
|
||||||
|
// trace!("Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
||||||
|
let c = take_weak!(self.client);
|
||||||
|
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
||||||
|
let r = c.submit_seal(pow_hash, seal);
|
||||||
|
to_value(&r.is_ok())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_hashrate(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
// TODO: Index should be U256.
|
||||||
|
from_params::<(Index, H256)>(params).and_then(|(rate, id)| {
|
||||||
|
self.hashrates.write().unwrap().insert(id, rate.value() as u64);
|
||||||
|
to_value(&true)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation.
|
||||||
pub struct EthFilterClient {
|
pub struct EthFilterClient {
|
||||||
client: Weak<Client>
|
client: Weak<Client>,
|
||||||
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilterClient {
|
impl EthFilterClient {
|
||||||
/// Creates new Eth filter client.
|
/// Creates new Eth filter client.
|
||||||
pub fn new(client: &Arc<Client>) -> Self {
|
pub fn new(client: &Arc<Client>) -> Self {
|
||||||
EthFilterClient {
|
EthFilterClient {
|
||||||
client: Arc::downgrade(client)
|
client: Arc::downgrade(client),
|
||||||
|
polls: Mutex::new(PollManager::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilter for EthFilterClient {
|
impl EthFilter for EthFilterClient {
|
||||||
fn new_block_filter(&self, _params: Params) -> Result<Value, Error> {
|
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
Ok(Value::U64(0))
|
from_params::<(Filter,)>(params)
|
||||||
|
.and_then(|(filter,)| {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let id = polls.create_poll(PollFilter::Logs(filter.into()), take_weak!(self.client).chain_info().best_block_number);
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_pending_transaction_filter(&self, _params: Params) -> Result<Value, Error> {
|
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
Ok(Value::U64(1))
|
match params {
|
||||||
|
Params::None => {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let id = polls.create_poll(PollFilter::Block, take_weak!(self.client).chain_info().best_block_number);
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
},
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_changes(&self, _: Params) -> Result<Value, Error> {
|
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
to_value(&take_weak!(self.client).chain_info().best_block_hash).map(|v| Value::Array(vec![v]))
|
match params {
|
||||||
|
Params::None => {
|
||||||
|
let mut polls = self.polls.lock().unwrap();
|
||||||
|
let id = polls.create_poll(PollFilter::PendingTransaction, take_weak!(self.client).chain_info().best_block_number);
|
||||||
|
to_value(&U256::from(id))
|
||||||
|
},
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
let client = take_weak!(self.client);
|
||||||
|
from_params::<(Index,)>(params)
|
||||||
|
.and_then(|(index,)| {
|
||||||
|
let info = self.polls.lock().unwrap().get_poll_info(&index.value()).cloned();
|
||||||
|
match info {
|
||||||
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
|
Some(info) => match info.filter {
|
||||||
|
PollFilter::Block => {
|
||||||
|
let current_number = client.chain_info().best_block_number;
|
||||||
|
let hashes = (info.block_number..current_number).into_iter()
|
||||||
|
.map(BlockId::Number)
|
||||||
|
.filter_map(|id| client.block_hash(id))
|
||||||
|
.collect::<Vec<H256>>();
|
||||||
|
|
||||||
|
self.polls.lock().unwrap().update_poll(&index.value(), current_number);
|
||||||
|
|
||||||
|
to_value(&hashes)
|
||||||
|
},
|
||||||
|
PollFilter::PendingTransaction => {
|
||||||
|
// TODO: fix implementation once TransactionQueue is merged
|
||||||
|
to_value(&vec![] as &Vec<H256>)
|
||||||
|
},
|
||||||
|
PollFilter::Logs(mut filter) => {
|
||||||
|
filter.from_block = BlockId::Number(info.block_number);
|
||||||
|
filter.to_block = BlockId::Latest;
|
||||||
|
let logs = client.logs(filter)
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
|
let current_number = client.chain_info().best_block_number;
|
||||||
|
self.polls.lock().unwrap().update_poll(&index.value(), current_number);
|
||||||
|
|
||||||
|
to_value(&logs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Index,)>(params)
|
||||||
|
.and_then(|(index,)| {
|
||||||
|
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||||
|
to_value(&true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ macro_rules! take_weak {
|
|||||||
mod web3;
|
mod web3;
|
||||||
mod eth;
|
mod eth;
|
||||||
mod net;
|
mod net;
|
||||||
|
mod personal;
|
||||||
|
|
||||||
pub use self::web3::Web3Client;
|
pub use self::web3::Web3Client;
|
||||||
pub use self::eth::{EthClient, EthFilterClient};
|
pub use self::eth::{EthClient, EthFilterClient};
|
||||||
pub use self::net::NetClient;
|
pub use self::net::NetClient;
|
||||||
|
pub use self::personal::PersonalClient;
|
||||||
|
78
rpc/src/v1/impls/personal.rs
Normal file
78
rpc/src/v1/impls/personal.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Account management (personal) rpc implementation
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
use v1::traits::Personal;
|
||||||
|
use util::keys::store::*;
|
||||||
|
use util::Address;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
/// Account management (personal) rpc implementation.
|
||||||
|
pub struct PersonalClient {
|
||||||
|
secret_store: Weak<RwLock<SecretStore>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersonalClient {
|
||||||
|
/// Creates new PersonalClient
|
||||||
|
pub fn new(store: &Arc<RwLock<SecretStore>>) -> Self {
|
||||||
|
PersonalClient {
|
||||||
|
secret_store: Arc::downgrade(store),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Personal for PersonalClient {
|
||||||
|
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
let store_wk = take_weak!(self.secret_store);
|
||||||
|
let store = store_wk.read().unwrap();
|
||||||
|
match store.accounts() {
|
||||||
|
Ok(account_list) => {
|
||||||
|
Ok(Value::Array(account_list.iter()
|
||||||
|
.map(|&(account, _)| Value::String(format!("{:?}", account)))
|
||||||
|
.collect::<Vec<Value>>())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(_) => Err(Error::internal_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(String, )>(params).and_then(
|
||||||
|
|(pass, )| {
|
||||||
|
let store_wk = take_weak!(self.secret_store);
|
||||||
|
let mut store = store_wk.write().unwrap();
|
||||||
|
match store.new_account(&pass) {
|
||||||
|
Ok(address) => Ok(Value::String(format!("{:?}", address))),
|
||||||
|
Err(_) => Err(Error::internal_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Address, String, u64)>(params).and_then(
|
||||||
|
|(account, account_pass, _)|{
|
||||||
|
let store_wk = take_weak!(self.secret_store);
|
||||||
|
let store = store_wk.read().unwrap();
|
||||||
|
match store.unlock_account(&account, &account_pass) {
|
||||||
|
Ok(_) => Ok(Value::Bool(true)),
|
||||||
|
Err(_) => Ok(Value::Bool(false)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -30,9 +30,7 @@ impl Web3Client {
|
|||||||
impl Web3 for Web3Client {
|
impl Web3 for Web3Client {
|
||||||
fn client_version(&self, params: Params) -> Result<Value, Error> {
|
fn client_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => {
|
Params::None => Ok(Value::String(version().to_owned().replace("Parity/", "Parity//"))),
|
||||||
Ok(Value::String(version())),
|
|
||||||
}
|
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethcore rpc v1.
|
//! Ethcore rpc v1.
|
||||||
//!
|
//!
|
||||||
//! Compliant with ethereum rpc.
|
//! Compliant with ethereum rpc.
|
||||||
|
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
@ -23,6 +23,7 @@ mod impls;
|
|||||||
mod types;
|
mod types;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
pub use self::traits::{Web3, Eth, EthFilter, Net};
|
pub use self::traits::{Web3, Eth, EthFilter, Personal, Net};
|
||||||
pub use self::impls::*;
|
pub use self::impls::*;
|
||||||
|
@ -23,7 +23,9 @@ macro_rules! rpc_unimplemented {
|
|||||||
pub mod web3;
|
pub mod web3;
|
||||||
pub mod eth;
|
pub mod eth;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
pub mod personal;
|
||||||
|
|
||||||
pub use self::web3::Web3;
|
pub use self::web3::Web3;
|
||||||
pub use self::eth::{Eth, EthFilter};
|
pub use self::eth::{Eth, EthFilter};
|
||||||
pub use self::net::Net;
|
pub use self::net::Net;
|
||||||
|
pub use self::personal::Personal;
|
||||||
|
41
rpc/src/v1/traits/personal.rs
Normal file
41
rpc/src/v1/traits/personal.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Personal rpc interface.
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::*;
|
||||||
|
|
||||||
|
/// Personal rpc interface.
|
||||||
|
pub trait Personal: Sized + Send + Sync + 'static {
|
||||||
|
|
||||||
|
/// Lists all stored accounts
|
||||||
|
fn accounts(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Creates new account (it becomes new current unlocked account)
|
||||||
|
fn new_account(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Unlocks specified account for use (can only be one unlocked account at one moment)
|
||||||
|
fn unlock_account(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Should be used to convert object to io delegate.
|
||||||
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
|
delegate.add_method("personal_listAccounts", Personal::accounts);
|
||||||
|
delegate.add_method("personal_newAccount", Personal::new_account);
|
||||||
|
delegate.add_method("personal_unlockAccount", Personal::unlock_account);
|
||||||
|
delegate
|
||||||
|
}
|
||||||
|
}
|
@ -14,22 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
mod block;
|
#[cfg(feature = "serde_macros")]
|
||||||
mod block_number;
|
include!("mod.rs.in");
|
||||||
mod bytes;
|
|
||||||
mod filter;
|
|
||||||
mod index;
|
|
||||||
mod log;
|
|
||||||
mod optionals;
|
|
||||||
mod sync;
|
|
||||||
mod transaction;
|
|
||||||
|
|
||||||
pub use self::block::{Block, BlockTransactions};
|
#[cfg(not(feature = "serde_macros"))]
|
||||||
pub use self::block_number::BlockNumber;
|
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||||
pub use self::bytes::Bytes;
|
|
||||||
pub use self::filter::Filter;
|
|
||||||
pub use self::index::Index;
|
|
||||||
pub use self::log::Log;
|
|
||||||
pub use self::optionals::OptionalValue;
|
|
||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
|
||||||
pub use self::transaction::Transaction;
|
|
||||||
|
35
rpc/src/v1/types/mod.rs.in
Normal file
35
rpc/src/v1/types/mod.rs.in
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
mod block;
|
||||||
|
mod block_number;
|
||||||
|
mod bytes;
|
||||||
|
mod filter;
|
||||||
|
mod index;
|
||||||
|
mod log;
|
||||||
|
mod optionals;
|
||||||
|
mod sync;
|
||||||
|
mod transaction;
|
||||||
|
|
||||||
|
pub use self::block::{Block, BlockTransactions};
|
||||||
|
pub use self::block_number::BlockNumber;
|
||||||
|
pub use self::bytes::Bytes;
|
||||||
|
pub use self::filter::Filter;
|
||||||
|
pub use self::index::Index;
|
||||||
|
pub use self::log::Log;
|
||||||
|
pub use self::optionals::OptionalValue;
|
||||||
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
|
pub use self::transaction::Transaction;
|
@ -9,13 +9,14 @@ authors = ["Ethcore <admin@ethcore.io"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = ".." }
|
ethcore = { path = "../ethcore" }
|
||||||
clippy = { version = "0.0.44", optional = true }
|
clippy = { version = "0.0.44", optional = true }
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
env_logger = "0.3"
|
env_logger = "0.3"
|
||||||
time = "0.1.34"
|
time = "0.1.34"
|
||||||
rand = "0.3.13"
|
rand = "0.3.13"
|
||||||
heapsize = "0.3"
|
heapsize = "0.3"
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -33,7 +33,7 @@ use util::*;
|
|||||||
use std::mem::{replace};
|
use std::mem::{replace};
|
||||||
use ethcore::views::{HeaderView};
|
use ethcore::views::{HeaderView};
|
||||||
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
||||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockId};
|
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
|
||||||
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
||||||
use ethcore::error::*;
|
use ethcore::error::*;
|
||||||
use ethcore::block::Block;
|
use ethcore::block::Block;
|
||||||
@ -475,21 +475,24 @@ impl ChainSync {
|
|||||||
peer.latest_number = Some(header.number());
|
peer.latest_number = Some(header.number());
|
||||||
}
|
}
|
||||||
// TODO: Decompose block and add to self.headers and self.bodies instead
|
// TODO: Decompose block and add to self.headers and self.bodies instead
|
||||||
if header.number == From::from(self.current_base_block() + 1) {
|
if header.number <= From::from(self.current_base_block() + 1) {
|
||||||
match io.chain().import_block(block_rlp.as_raw().to_vec()) {
|
match io.chain().import_block(block_rlp.as_raw().to_vec()) {
|
||||||
Err(ImportError::AlreadyInChain) => {
|
Err(Error::Import(ImportError::AlreadyInChain)) => {
|
||||||
trace!(target: "sync", "New block already in chain {:?}", h);
|
trace!(target: "sync", "New block already in chain {:?}", h);
|
||||||
},
|
},
|
||||||
Err(ImportError::AlreadyQueued) => {
|
Err(Error::Import(ImportError::AlreadyQueued)) => {
|
||||||
trace!(target: "sync", "New block already queued {:?}", h);
|
trace!(target: "sync", "New block already queued {:?}", h);
|
||||||
},
|
},
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.last_imported_block = Some(header.number);
|
if self.current_base_block() < header.number {
|
||||||
|
self.last_imported_block = Some(header.number);
|
||||||
|
self.remove_downloaded_blocks(header.number);
|
||||||
|
}
|
||||||
trace!(target: "sync", "New block queued {:?}", h);
|
trace!(target: "sync", "New block queued {:?}", h);
|
||||||
},
|
},
|
||||||
Err(ImportError::UnknownParent) => {
|
Err(Error::Block(BlockError::UnknownParent(p))) => {
|
||||||
unknown = true;
|
unknown = true;
|
||||||
trace!(target: "sync", "New block with unknown parent {:?}", h);
|
trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(target: "sync", "Bad new block {:?} : {:?}", h, e);
|
debug!(target: "sync", "Bad new block {:?} : {:?}", h, e);
|
||||||
@ -572,7 +575,7 @@ impl ChainSync {
|
|||||||
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
||||||
trace!(target: "sync", "== Connected {}", peer);
|
trace!(target: "sync", "== Connected {}", peer);
|
||||||
if let Err(e) = self.send_status(io) {
|
if let Err(e) = self.send_status(io) {
|
||||||
warn!(target:"sync", "Error sending status request: {:?}", e);
|
trace!(target:"sync", "Error sending status request: {:?}", e);
|
||||||
io.disable_peer(peer);
|
io.disable_peer(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -635,16 +638,7 @@ impl ChainSync {
|
|||||||
match self.last_imported_block { None => 0, Some(x) => x }
|
match self.last_imported_block { None => 0, Some(x) => x }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find some headers or blocks to download for a peer.
|
fn find_block_bodies_hashes_to_request(&self, ignore_others: bool) -> (Vec<H256>, Vec<BlockNumber>) {
|
||||||
fn request_blocks(&mut self, io: &mut SyncIo, peer_id: PeerId, ignore_others: bool) {
|
|
||||||
self.clear_peer_download(peer_id);
|
|
||||||
|
|
||||||
if io.chain().queue_info().is_full() {
|
|
||||||
self.pause_sync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if we need to download any block bodies first
|
|
||||||
let mut needed_bodies: Vec<H256> = Vec::new();
|
let mut needed_bodies: Vec<H256> = Vec::new();
|
||||||
let mut needed_numbers: Vec<BlockNumber> = Vec::new();
|
let mut needed_numbers: Vec<BlockNumber> = Vec::new();
|
||||||
|
|
||||||
@ -664,74 +658,88 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(needed_bodies, needed_numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find some headers or blocks to download for a peer.
|
||||||
|
fn request_blocks(&mut self, io: &mut SyncIo, peer_id: PeerId, ignore_others: bool) {
|
||||||
|
self.clear_peer_download(peer_id);
|
||||||
|
|
||||||
|
if io.chain().queue_info().is_full() {
|
||||||
|
self.pause_sync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if we need to download any block bodies first
|
||||||
|
let (needed_bodies, needed_numbers) = self.find_block_bodies_hashes_to_request(ignore_others);
|
||||||
if !needed_bodies.is_empty() {
|
if !needed_bodies.is_empty() {
|
||||||
let (head, _) = self.headers.range_iter().next().unwrap();
|
let (head, _) = self.headers.range_iter().next().unwrap();
|
||||||
if needed_numbers.first().unwrap() - head > self.max_download_ahead_blocks as BlockNumber {
|
if needed_numbers.first().unwrap() - head > self.max_download_ahead_blocks as BlockNumber {
|
||||||
trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading block bodies", peer_id, needed_numbers.first().unwrap(), head);
|
trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading block bodies", peer_id, needed_numbers.first().unwrap(), head);
|
||||||
self.request_blocks(io, peer_id, true);
|
self.request_blocks(io, peer_id, true);
|
||||||
return;
|
} else {
|
||||||
|
self.downloading_bodies.extend(needed_numbers.iter());
|
||||||
|
replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, needed_numbers);
|
||||||
|
self.request_bodies(io, peer_id, needed_bodies);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if need to download headers
|
||||||
|
let mut start = 0;
|
||||||
|
if !self.have_common_block {
|
||||||
|
// download backwards until common block is found 1 header at a time
|
||||||
|
let chain_info = io.chain().chain_info();
|
||||||
|
start = chain_info.best_block_number;
|
||||||
|
if !self.headers.is_empty() {
|
||||||
|
start = min(start, self.headers.range_iter().next().unwrap().0 - 1);
|
||||||
|
}
|
||||||
|
if start == 0 {
|
||||||
|
self.have_common_block = true; //reached genesis
|
||||||
|
self.last_imported_hash = Some(chain_info.genesis_hash);
|
||||||
|
self.last_imported_block = Some(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.have_common_block {
|
||||||
|
let mut headers: Vec<BlockNumber> = Vec::new();
|
||||||
|
let mut prev = self.current_base_block() + 1;
|
||||||
|
let head = self.headers.range_iter().next().map(|(h, _)| h);
|
||||||
|
for (next, ref items) in self.headers.range_iter() {
|
||||||
|
if !headers.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if next <= prev {
|
||||||
|
prev = next + items.len() as BlockNumber;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut block = prev;
|
||||||
|
while block < next && headers.len() < MAX_HEADERS_TO_REQUEST {
|
||||||
|
if ignore_others || !self.downloading_headers.contains(&(block as BlockNumber)) {
|
||||||
|
headers.push(block as BlockNumber);
|
||||||
|
}
|
||||||
|
block += 1;
|
||||||
|
}
|
||||||
|
prev = next + items.len() as BlockNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !headers.is_empty() {
|
||||||
|
start = headers[0];
|
||||||
|
if head.is_some() && start > head.unwrap() && start - head.unwrap() > self.max_download_ahead_blocks as BlockNumber {
|
||||||
|
trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading headers", peer_id, start, head.unwrap());
|
||||||
|
self.request_blocks(io, peer_id, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let count = headers.len();
|
||||||
|
self.downloading_headers.extend(headers.iter());
|
||||||
|
replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, headers);
|
||||||
|
assert!(!self.headers.have_item(&start));
|
||||||
|
self.request_headers_by_number(io, peer_id, start, count, 0, false);
|
||||||
}
|
}
|
||||||
self.downloading_bodies.extend(needed_numbers.iter());
|
|
||||||
replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, needed_numbers);
|
|
||||||
self.request_bodies(io, peer_id, needed_bodies);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// check if need to download headers
|
// continue search for common block
|
||||||
let mut start = 0;
|
self.downloading_headers.insert(start);
|
||||||
if !self.have_common_block {
|
self.request_headers_by_number(io, peer_id, start, 1, 0, false);
|
||||||
// download backwards until common block is found 1 header at a time
|
|
||||||
let chain_info = io.chain().chain_info();
|
|
||||||
start = chain_info.best_block_number;
|
|
||||||
if !self.headers.is_empty() {
|
|
||||||
start = min(start, self.headers.range_iter().next().unwrap().0 - 1);
|
|
||||||
}
|
|
||||||
if start == 0 {
|
|
||||||
self.have_common_block = true; //reached genesis
|
|
||||||
self.last_imported_hash = Some(chain_info.genesis_hash);
|
|
||||||
self.last_imported_block = Some(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.have_common_block {
|
|
||||||
let mut headers: Vec<BlockNumber> = Vec::new();
|
|
||||||
let mut prev = self.current_base_block() + 1;
|
|
||||||
let head = self.headers.range_iter().next().map(|(h, _)| h);
|
|
||||||
for (next, ref items) in self.headers.range_iter() {
|
|
||||||
if !headers.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if next <= prev {
|
|
||||||
prev = next + items.len() as BlockNumber;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut block = prev;
|
|
||||||
while block < next && headers.len() < MAX_HEADERS_TO_REQUEST {
|
|
||||||
if ignore_others || !self.downloading_headers.contains(&(block as BlockNumber)) {
|
|
||||||
headers.push(block as BlockNumber);
|
|
||||||
}
|
|
||||||
block += 1;
|
|
||||||
}
|
|
||||||
prev = next + items.len() as BlockNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !headers.is_empty() {
|
|
||||||
start = headers[0];
|
|
||||||
if head.is_some() && start > head.unwrap() && start - head.unwrap() > self.max_download_ahead_blocks as BlockNumber {
|
|
||||||
trace!(target: "sync", "{}: Stalled download ({} vs {}), helping with downloading headers", peer_id, start, head.unwrap());
|
|
||||||
self.request_blocks(io, peer_id, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let count = headers.len();
|
|
||||||
self.downloading_headers.extend(headers.iter());
|
|
||||||
replace(&mut self.peers.get_mut(&peer_id).unwrap().asking_blocks, headers);
|
|
||||||
assert!(!self.headers.have_item(&start));
|
|
||||||
self.request_headers_by_number(io, peer_id, start, count, 0, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// continue search for common block
|
|
||||||
self.downloading_headers.insert(start);
|
|
||||||
self.request_headers_by_number(io, peer_id, start, 1, 0, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,12 +789,12 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match io.chain().import_block(block_rlp.out()) {
|
match io.chain().import_block(block_rlp.out()) {
|
||||||
Err(ImportError::AlreadyInChain) => {
|
Err(Error::Import(ImportError::AlreadyInChain)) => {
|
||||||
trace!(target: "sync", "Block already in chain {:?}", h);
|
trace!(target: "sync", "Block already in chain {:?}", h);
|
||||||
self.last_imported_block = Some(headers.0 + i as BlockNumber);
|
self.last_imported_block = Some(headers.0 + i as BlockNumber);
|
||||||
self.last_imported_hash = Some(h.clone());
|
self.last_imported_hash = Some(h.clone());
|
||||||
},
|
},
|
||||||
Err(ImportError::AlreadyQueued) => {
|
Err(Error::Import(ImportError::AlreadyQueued)) => {
|
||||||
trace!(target: "sync", "Block already queued {:?}", h);
|
trace!(target: "sync", "Block already queued {:?}", h);
|
||||||
self.last_imported_block = Some(headers.0 + i as BlockNumber);
|
self.last_imported_block = Some(headers.0 + i as BlockNumber);
|
||||||
self.last_imported_hash = Some(h.clone());
|
self.last_imported_hash = Some(h.clone());
|
||||||
@ -1151,9 +1159,7 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// returns peer ids that have less blocks than our chain
|
/// returns peer ids that have less blocks than our chain
|
||||||
fn get_lagging_peers(&mut self, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> {
|
fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> {
|
||||||
let chain = io.chain();
|
|
||||||
let chain_info = chain.chain_info();
|
|
||||||
let latest_hash = chain_info.best_block_hash;
|
let latest_hash = chain_info.best_block_hash;
|
||||||
let latest_number = chain_info.best_block_number;
|
let latest_number = chain_info.best_block_number;
|
||||||
self.peers.iter_mut().filter_map(|(&id, ref mut peer_info)|
|
self.peers.iter_mut().filter_map(|(&id, ref mut peer_info)|
|
||||||
@ -1172,9 +1178,9 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// propagates latest block to lagging peers
|
/// propagates latest block to lagging peers
|
||||||
fn propagate_blocks(&mut self, local_best: &H256, best_number: BlockNumber, io: &mut SyncIo) -> usize {
|
fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize {
|
||||||
let updated_peers = {
|
let updated_peers = {
|
||||||
let lagging_peers = self.get_lagging_peers(io);
|
let lagging_peers = self.get_lagging_peers(chain_info, io);
|
||||||
|
|
||||||
// sqrt(x)/x scaled to max u32
|
// sqrt(x)/x scaled to max u32
|
||||||
let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32;
|
let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32;
|
||||||
@ -1191,30 +1197,30 @@ impl ChainSync {
|
|||||||
for peer_id in updated_peers {
|
for peer_id in updated_peers {
|
||||||
let rlp = ChainSync::create_latest_block_rlp(io.chain());
|
let rlp = ChainSync::create_latest_block_rlp(io.chain());
|
||||||
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
|
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
|
||||||
self.peers.get_mut(&peer_id).unwrap().latest_hash = local_best.clone();
|
self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone();
|
||||||
self.peers.get_mut(&peer_id).unwrap().latest_number = Some(best_number);
|
self.peers.get_mut(&peer_id).unwrap().latest_number = Some(chain_info.best_block_number);
|
||||||
sent = sent + 1;
|
sent = sent + 1;
|
||||||
}
|
}
|
||||||
sent
|
sent
|
||||||
}
|
}
|
||||||
|
|
||||||
/// propagates new known hashes to all peers
|
/// propagates new known hashes to all peers
|
||||||
fn propagate_new_hashes(&mut self, local_best: &H256, best_number: BlockNumber, io: &mut SyncIo) -> usize {
|
fn propagate_new_hashes(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize {
|
||||||
let updated_peers = self.get_lagging_peers(io);
|
let updated_peers = self.get_lagging_peers(chain_info, io);
|
||||||
let mut sent = 0;
|
let mut sent = 0;
|
||||||
let last_parent = HeaderView::new(&io.chain().block_header(BlockId::Hash(local_best.clone())).unwrap()).parent_hash();
|
let last_parent = HeaderView::new(&io.chain().block_header(BlockId::Hash(chain_info.best_block_hash.clone())).unwrap()).parent_hash();
|
||||||
for (peer_id, peer_number) in updated_peers {
|
for (peer_id, peer_number) in updated_peers {
|
||||||
let mut peer_best = self.peers.get(&peer_id).unwrap().latest_hash.clone();
|
let mut peer_best = self.peers.get(&peer_id).unwrap().latest_hash.clone();
|
||||||
if best_number - peer_number > MAX_PEERS_PROPAGATION as BlockNumber {
|
if chain_info.best_block_number - peer_number > MAX_PEERS_PROPAGATION as BlockNumber {
|
||||||
// If we think peer is too far behind just send one latest hash
|
// If we think peer is too far behind just send one latest hash
|
||||||
peer_best = last_parent.clone();
|
peer_best = last_parent.clone();
|
||||||
}
|
}
|
||||||
sent = sent + match ChainSync::create_new_hashes_rlp(io.chain(), &peer_best, &local_best) {
|
sent = sent + match ChainSync::create_new_hashes_rlp(io.chain(), &peer_best, &chain_info.best_block_hash) {
|
||||||
Some(rlp) => {
|
Some(rlp) => {
|
||||||
{
|
{
|
||||||
let peer = self.peers.get_mut(&peer_id).unwrap();
|
let peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
peer.latest_hash = local_best.clone();
|
peer.latest_hash = chain_info.best_block_hash.clone();
|
||||||
peer.latest_number = Some(best_number);
|
peer.latest_number = Some(chain_info.best_block_number);
|
||||||
}
|
}
|
||||||
self.send_packet(io, peer_id, NEW_BLOCK_HASHES_PACKET, rlp);
|
self.send_packet(io, peer_id, NEW_BLOCK_HASHES_PACKET, rlp);
|
||||||
1
|
1
|
||||||
@ -1234,8 +1240,8 @@ impl ChainSync {
|
|||||||
pub fn chain_blocks_verified(&mut self, io: &mut SyncIo) {
|
pub fn chain_blocks_verified(&mut self, io: &mut SyncIo) {
|
||||||
let chain = io.chain().chain_info();
|
let chain = io.chain().chain_info();
|
||||||
if (((chain.best_block_number as i64) - (self.last_send_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
if (((chain.best_block_number as i64) - (self.last_send_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
||||||
let blocks = self.propagate_blocks(&chain.best_block_hash, chain.best_block_number, io);
|
let blocks = self.propagate_blocks(&chain, io);
|
||||||
let hashes = self.propagate_new_hashes(&chain.best_block_hash, chain.best_block_number, io);
|
let hashes = self.propagate_new_hashes(&chain, io);
|
||||||
if blocks != 0 || hashes != 0 {
|
if blocks != 0 || hashes != 0 {
|
||||||
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
||||||
}
|
}
|
||||||
@ -1385,9 +1391,10 @@ mod tests {
|
|||||||
client.add_blocks(100, false);
|
client.add_blocks(100, false);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10));
|
||||||
|
let chain_info = client.chain_info();
|
||||||
let io = TestIo::new(&mut client, &mut queue, None);
|
let io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
let lagging_peers = sync.get_lagging_peers(&io);
|
let lagging_peers = sync.get_lagging_peers(&chain_info, &io);
|
||||||
|
|
||||||
assert_eq!(1, lagging_peers.len())
|
assert_eq!(1, lagging_peers.len())
|
||||||
}
|
}
|
||||||
@ -1415,11 +1422,10 @@ mod tests {
|
|||||||
client.add_blocks(100, false);
|
client.add_blocks(100, false);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let best_hash = client.chain_info().best_block_hash.clone();
|
let chain_info = client.chain_info();
|
||||||
let best_number = client.chain_info().best_block_number;
|
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
let peer_count = sync.propagate_new_hashes(&best_hash, best_number, &mut io);
|
let peer_count = sync.propagate_new_hashes(&chain_info, &mut io);
|
||||||
|
|
||||||
// 1 message should be send
|
// 1 message should be send
|
||||||
assert_eq!(1, io.queue.len());
|
assert_eq!(1, io.queue.len());
|
||||||
@ -1435,11 +1441,9 @@ mod tests {
|
|||||||
client.add_blocks(100, false);
|
client.add_blocks(100, false);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let best_hash = client.chain_info().best_block_hash.clone();
|
let chain_info = client.chain_info();
|
||||||
let best_number = client.chain_info().best_block_number;
|
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
let peer_count = sync.propagate_blocks(&chain_info, &mut io);
|
||||||
let peer_count = sync.propagate_blocks(&best_hash, best_number, &mut io);
|
|
||||||
|
|
||||||
// 1 message should be send
|
// 1 message should be send
|
||||||
assert_eq!(1, io.queue.len());
|
assert_eq!(1, io.queue.len());
|
||||||
@ -1541,11 +1545,10 @@ mod tests {
|
|||||||
client.add_blocks(100, false);
|
client.add_blocks(100, false);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let best_hash = client.chain_info().best_block_hash.clone();
|
let chain_info = client.chain_info();
|
||||||
let best_number = client.chain_info().best_block_number;
|
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
sync.propagate_new_hashes(&best_hash, best_number, &mut io);
|
sync.propagate_new_hashes(&chain_info, &mut io);
|
||||||
|
|
||||||
let data = &io.queue[0].data.clone();
|
let data = &io.queue[0].data.clone();
|
||||||
let result = sync.on_peer_new_hashes(&mut io, 0, &UntrustedRlp::new(&data));
|
let result = sync.on_peer_new_hashes(&mut io, 0, &UntrustedRlp::new(&data));
|
||||||
@ -1560,11 +1563,10 @@ mod tests {
|
|||||||
client.add_blocks(100, false);
|
client.add_blocks(100, false);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let best_hash = client.chain_info().best_block_hash.clone();
|
let chain_info = client.chain_info();
|
||||||
let best_number = client.chain_info().best_block_number;
|
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
sync.propagate_blocks(&best_hash, best_number, &mut io);
|
sync.propagate_blocks(&chain_info, &mut io);
|
||||||
|
|
||||||
let data = &io.queue[0].data.clone();
|
let data = &io.queue[0].data.clone();
|
||||||
let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(&data));
|
let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(&data));
|
||||||
|
@ -70,6 +70,8 @@ use io::NetSyncIo;
|
|||||||
mod chain;
|
mod chain;
|
||||||
mod io;
|
mod io;
|
||||||
mod range_collection;
|
mod range_collection;
|
||||||
|
// TODO [todr] Made public to suppress dead code warnings
|
||||||
|
pub mod transaction_queue;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -105,6 +105,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
Some(U256::zero())
|
Some(U256::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_hash(&self, _id: BlockId) -> Option<H256> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn code(&self, _address: &Address) -> Option<Bytes> {
|
fn code(&self, _address: &Address) -> Option<Bytes> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
683
sync/src/transaction_queue.rs
Normal file
683
sync/src/transaction_queue.rs
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// TODO [todr] - own transactions should have higher priority
|
||||||
|
|
||||||
|
//! Transaction Queue
|
||||||
|
|
||||||
|
use std::cmp::{Ordering};
|
||||||
|
use std::collections::{HashMap, BTreeSet};
|
||||||
|
use util::numbers::{Uint, U256};
|
||||||
|
use util::hash::{Address, H256};
|
||||||
|
use util::table::*;
|
||||||
|
use ethcore::transaction::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct TransactionOrder {
|
||||||
|
nonce_height: U256,
|
||||||
|
gas_price: U256,
|
||||||
|
hash: H256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionOrder {
|
||||||
|
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
||||||
|
TransactionOrder {
|
||||||
|
nonce_height: tx.nonce() - base_nonce,
|
||||||
|
gas_price: tx.transaction.gas_price,
|
||||||
|
hash: tx.hash(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_height(mut self, nonce: U256, base_nonce: U256) -> Self {
|
||||||
|
self.nonce_height = nonce - base_nonce;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for TransactionOrder {}
|
||||||
|
impl PartialEq for TransactionOrder {
|
||||||
|
fn eq(&self, other: &TransactionOrder) -> bool {
|
||||||
|
self.cmp(other) == Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialOrd for TransactionOrder {
|
||||||
|
fn partial_cmp(&self, other: &TransactionOrder) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for TransactionOrder {
|
||||||
|
fn cmp(&self, b: &TransactionOrder) -> Ordering {
|
||||||
|
// First check nonce_height
|
||||||
|
if self.nonce_height != b.nonce_height {
|
||||||
|
return self.nonce_height.cmp(&b.nonce_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then compare gas_prices
|
||||||
|
let a_gas = self.gas_price;
|
||||||
|
let b_gas = b.gas_price;
|
||||||
|
if a_gas != b_gas {
|
||||||
|
return a_gas.cmp(&b_gas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare hashes
|
||||||
|
self.hash.cmp(&b.hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VerifiedTransaction {
|
||||||
|
transaction: SignedTransaction
|
||||||
|
}
|
||||||
|
impl VerifiedTransaction {
|
||||||
|
fn new(transaction: SignedTransaction) -> Self {
|
||||||
|
VerifiedTransaction {
|
||||||
|
transaction: transaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(&self) -> H256 {
|
||||||
|
self.transaction.hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nonce(&self) -> U256 {
|
||||||
|
self.transaction.nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sender(&self) -> Address {
|
||||||
|
self.transaction.sender().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransactionSet {
|
||||||
|
by_priority: BTreeSet<TransactionOrder>,
|
||||||
|
by_address: Table<Address, U256, TransactionOrder>,
|
||||||
|
limit: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionSet {
|
||||||
|
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) {
|
||||||
|
self.by_priority.insert(order.clone());
|
||||||
|
self.by_address.insert(sender, nonce, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enforce_limit(&mut self, by_hash: &HashMap<H256, VerifiedTransaction>) {
|
||||||
|
let len = self.by_priority.len();
|
||||||
|
if len <= self.limit {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let to_drop : Vec<&VerifiedTransaction> = {
|
||||||
|
self.by_priority
|
||||||
|
.iter()
|
||||||
|
.skip(self.limit)
|
||||||
|
.map(|order| by_hash.get(&order.hash).expect("Inconsistency in queue detected."))
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
for tx in to_drop {
|
||||||
|
self.drop(&tx.sender(), &tx.nonce());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
||||||
|
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
||||||
|
self.by_priority.remove(&tx_order);
|
||||||
|
return Some(tx_order);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.by_priority.clear();
|
||||||
|
self.by_address.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Current status of the queue
|
||||||
|
pub struct TransactionQueueStatus {
|
||||||
|
/// Number of pending transactions (ready to go to block)
|
||||||
|
pub pending: usize,
|
||||||
|
/// Number of future transactions (waiting for transactions with lower nonces first)
|
||||||
|
pub future: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TransactionQueue implementation
|
||||||
|
pub struct TransactionQueue {
|
||||||
|
/// Priority queue for transactions that can go to block
|
||||||
|
current: TransactionSet,
|
||||||
|
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
||||||
|
future: TransactionSet,
|
||||||
|
/// All transactions managed by queue indexed by hash
|
||||||
|
by_hash: HashMap<H256, VerifiedTransaction>,
|
||||||
|
/// Last nonce of transaction in current (to quickly check next expected transaction)
|
||||||
|
last_nonces: HashMap<Address, U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionQueue {
|
||||||
|
/// Creates new instance of this Queue
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::with_limits(1024, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create new instance of this Queue with specified limits
|
||||||
|
pub fn with_limits(current_limit: usize, future_limit: usize) -> Self {
|
||||||
|
let current = TransactionSet {
|
||||||
|
by_priority: BTreeSet::new(),
|
||||||
|
by_address: Table::new(),
|
||||||
|
limit: current_limit,
|
||||||
|
};
|
||||||
|
let future = TransactionSet {
|
||||||
|
by_priority: BTreeSet::new(),
|
||||||
|
by_address: Table::new(),
|
||||||
|
limit: future_limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
TransactionQueue {
|
||||||
|
current: current,
|
||||||
|
future: future,
|
||||||
|
by_hash: HashMap::new(),
|
||||||
|
last_nonces: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns current status for this queue
|
||||||
|
pub fn status(&self) -> TransactionQueueStatus {
|
||||||
|
TransactionQueueStatus {
|
||||||
|
pending: self.current.by_priority.len(),
|
||||||
|
future: self.future.by_priority.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds all signed transactions to queue to be verified and imported
|
||||||
|
pub fn add_all<T>(&mut self, txs: Vec<SignedTransaction>, fetch_nonce: T)
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
for tx in txs.into_iter() {
|
||||||
|
self.add(tx, &fetch_nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add signed transaction to queue to be verified and imported
|
||||||
|
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T)
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
self.import_tx(VerifiedTransaction::new(tx), fetch_nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all transactions identified by hashes given in slice
|
||||||
|
///
|
||||||
|
/// If gap is introduced marks subsequent transactions as future
|
||||||
|
pub fn remove_all<T>(&mut self, txs: &[H256], fetch_nonce: T)
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
for tx in txs {
|
||||||
|
self.remove(&tx, &fetch_nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes transaction identified by hashes from queue.
|
||||||
|
///
|
||||||
|
/// If gap is introduced marks subsequent transactions as future
|
||||||
|
pub fn remove<T>(&mut self, hash: &H256, fetch_nonce: &T)
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
let transaction = self.by_hash.remove(hash);
|
||||||
|
if transaction.is_none() {
|
||||||
|
// We don't know this transaction
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let transaction = transaction.unwrap();
|
||||||
|
let sender = transaction.sender();
|
||||||
|
let nonce = transaction.nonce();
|
||||||
|
|
||||||
|
println!("Removing tx: {:?}", transaction.transaction);
|
||||||
|
// Remove from future
|
||||||
|
self.future.drop(&sender, &nonce);
|
||||||
|
|
||||||
|
// Remove from current
|
||||||
|
let order = self.current.drop(&sender, &nonce);
|
||||||
|
if order.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's remove transactions where tx.nonce < current_nonce
|
||||||
|
// and if there are any future transactions matching current_nonce+1 - move to current
|
||||||
|
let current_nonce = fetch_nonce(&sender);
|
||||||
|
// We will either move transaction to future or remove it completely
|
||||||
|
// so there will be no transactions from this sender in current
|
||||||
|
self.last_nonces.remove(&sender);
|
||||||
|
|
||||||
|
let all_nonces_from_sender = match self.current.by_address.row(&sender) {
|
||||||
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
for k in all_nonces_from_sender {
|
||||||
|
// Goes to future or is removed
|
||||||
|
let order = self.current.drop(&sender, &k).unwrap();
|
||||||
|
if k >= current_nonce {
|
||||||
|
println!("Moving to future: {:?}", order);
|
||||||
|
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
||||||
|
} else {
|
||||||
|
self.by_hash.remove(&order.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.future.enforce_limit(&self.by_hash);
|
||||||
|
|
||||||
|
// And now lets check if there is some chain of transactions in future
|
||||||
|
// that should be placed in current
|
||||||
|
if let Some(new_current_top) = self.move_future_txs(sender.clone(), current_nonce - U256::one(), current_nonce) {
|
||||||
|
self.last_nonces.insert(sender, new_current_top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns top transactions from the queue
|
||||||
|
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> {
|
||||||
|
self.current.by_priority
|
||||||
|
.iter()
|
||||||
|
.take(size)
|
||||||
|
.map(|t| self.by_hash.get(&t.hash).expect("Transaction Queue Inconsistency"))
|
||||||
|
.map(|t| t.transaction.clone())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all elements (in any state) from the queue
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.current.clear();
|
||||||
|
self.future.clear();
|
||||||
|
self.by_hash.clear();
|
||||||
|
self.last_nonces.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_future_txs(&mut self, address: Address, current_nonce: U256, first_nonce: U256) -> Option<U256> {
|
||||||
|
println!("Moving from future for: {:?} base: {:?}", current_nonce, first_nonce);
|
||||||
|
let mut current_nonce = current_nonce + U256::one();
|
||||||
|
{
|
||||||
|
let by_nonce = self.future.by_address.row_mut(&address);
|
||||||
|
if let None = by_nonce {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut by_nonce = by_nonce.unwrap();
|
||||||
|
while let Some(order) = by_nonce.remove(¤t_nonce) {
|
||||||
|
// remove also from priority and hash
|
||||||
|
self.future.by_priority.remove(&order);
|
||||||
|
// Put to current
|
||||||
|
println!("Moved: {:?}", order);
|
||||||
|
let order = order.update_height(current_nonce.clone(), first_nonce);
|
||||||
|
self.current.insert(address.clone(), current_nonce, order);
|
||||||
|
current_nonce = current_nonce + U256::one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.future.by_address.clear_if_empty(&address);
|
||||||
|
// Returns last inserted nonce
|
||||||
|
Some(current_nonce - U256::one())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T)
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
let nonce = tx.nonce();
|
||||||
|
let address = tx.sender();
|
||||||
|
|
||||||
|
let next_nonce = self.last_nonces
|
||||||
|
.get(&address)
|
||||||
|
.cloned()
|
||||||
|
.map_or_else(|| fetch_nonce(&address), |n| n + U256::one());
|
||||||
|
|
||||||
|
println!("Expected next: {:?}, got: {:?}", next_nonce, nonce);
|
||||||
|
// Check height
|
||||||
|
if nonce > next_nonce {
|
||||||
|
let order = TransactionOrder::for_transaction(&tx, next_nonce);
|
||||||
|
// Insert to by_hash
|
||||||
|
self.by_hash.insert(tx.hash(), tx);
|
||||||
|
// We have a gap - put to future
|
||||||
|
self.future.insert(address, nonce, order);
|
||||||
|
self.future.enforce_limit(&self.by_hash);
|
||||||
|
return;
|
||||||
|
} else if next_nonce > nonce {
|
||||||
|
// Droping transaction
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_nonce = fetch_nonce(&address);
|
||||||
|
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||||
|
// Insert to by_hash
|
||||||
|
self.by_hash.insert(tx.hash(), tx);
|
||||||
|
|
||||||
|
// Insert to current
|
||||||
|
self.current.insert(address.clone(), nonce, order);
|
||||||
|
// But maybe there are some more items waiting in future?
|
||||||
|
let new_last_nonce = self.move_future_txs(address.clone(), nonce, base_nonce);
|
||||||
|
self.last_nonces.insert(address.clone(), new_last_nonce.unwrap_or(nonce));
|
||||||
|
// Enforce limit
|
||||||
|
self.current.enforce_limit(&self.by_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
extern crate rustc_serialize;
|
||||||
|
use self::rustc_serialize::hex::FromHex;
|
||||||
|
use std::collections::{HashMap, BTreeSet};
|
||||||
|
use util::crypto::KeyPair;
|
||||||
|
use util::numbers::{U256, Uint};
|
||||||
|
use util::hash::{Address};
|
||||||
|
use util::table::*;
|
||||||
|
use ethcore::transaction::*;
|
||||||
|
use super::*;
|
||||||
|
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||||
|
|
||||||
|
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
||||||
|
Transaction {
|
||||||
|
action: Action::Create,
|
||||||
|
value: U256::from(100),
|
||||||
|
data: "3331600055".from_hex().unwrap(),
|
||||||
|
gas: U256::from(100_000),
|
||||||
|
gas_price: U256::one(),
|
||||||
|
nonce: nonce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_tx() -> SignedTransaction {
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
new_unsigned_tx(U256::from(123)).sign(&keypair.secret())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_nonce(_address: &Address) -> U256 {
|
||||||
|
U256::from(123)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let secret = &keypair.secret();
|
||||||
|
let nonce = U256::from(123);
|
||||||
|
let tx = new_unsigned_tx(nonce);
|
||||||
|
let tx2 = new_unsigned_tx(nonce + second_nonce);
|
||||||
|
|
||||||
|
(tx.sign(secret), tx2.sign(secret))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_create_transaction_set() {
|
||||||
|
// given
|
||||||
|
let mut set = TransactionSet {
|
||||||
|
by_priority: BTreeSet::new(),
|
||||||
|
by_address: Table::new(),
|
||||||
|
limit: 1
|
||||||
|
};
|
||||||
|
let (tx1, tx2) = new_txs(U256::from(1));
|
||||||
|
let tx1 = VerifiedTransaction::new(tx1);
|
||||||
|
let tx2 = VerifiedTransaction::new(tx2);
|
||||||
|
let by_hash = {
|
||||||
|
let mut x = HashMap::new();
|
||||||
|
let tx1 = VerifiedTransaction::new(tx1.transaction.clone());
|
||||||
|
let tx2 = VerifiedTransaction::new(tx2.transaction.clone());
|
||||||
|
x.insert(tx1.hash(), tx1);
|
||||||
|
x.insert(tx2.hash(), tx2);
|
||||||
|
x
|
||||||
|
};
|
||||||
|
// Insert both transactions
|
||||||
|
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||||
|
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||||
|
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
||||||
|
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||||
|
assert_eq!(set.by_priority.len(), 2);
|
||||||
|
assert_eq!(set.by_address.len(), 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
set.enforce_limit(&by_hash);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(set.by_priority.len(), 1);
|
||||||
|
assert_eq!(set.by_address.len(), 1);
|
||||||
|
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order1);
|
||||||
|
set.clear();
|
||||||
|
assert_eq!(set.by_priority.len(), 0);
|
||||||
|
assert_eq!(set.by_address.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_import_tx() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let tx = new_tx();
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx, &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_import_txs_from_same_sender() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let top = txq.top_transactions(5);
|
||||||
|
assert_eq!(top[0], tx);
|
||||||
|
assert_eq!(top[1], tx2);
|
||||||
|
assert_eq!(top.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_put_transaction_to_futures_if_gap_detected() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
|
let (tx, tx2) = new_txs(U256::from(2));
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 1);
|
||||||
|
assert_eq!(stats.future, 1);
|
||||||
|
let top = txq.top_transactions(5);
|
||||||
|
assert_eq!(top.len(), 1);
|
||||||
|
assert_eq!(top[0], tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_move_transactions_if_gap_filled() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let kp = KeyPair::create().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);
|
||||||
|
|
||||||
|
txq.add(tx, &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
txq.add(tx2, &default_nonce);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx1, &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 3);
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
txq2.add(tx2.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq2.status().pending, 1);
|
||||||
|
assert_eq!(txq2.status().future, 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq2.remove(&tx.hash(), &default_nonce);
|
||||||
|
txq2.remove(&tx2.hash(), &default_nonce);
|
||||||
|
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq2.status();
|
||||||
|
assert_eq!(stats.pending, 0);
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
txq.add(tx3.clone(), &default_nonce);
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx.hash(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 1);
|
||||||
|
assert_eq!(stats.pending, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_clear_queue() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let (tx, tx2) = new_txs(U256::one());
|
||||||
|
|
||||||
|
// add
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.clear();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::with_limits(1, 1);
|
||||||
|
let (tx, tx2) = new_txs(U256::one());
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let t = txq.top_transactions(2);
|
||||||
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
assert_eq!(t.len(), 1);
|
||||||
|
assert_eq!(t[0], tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_limit_future_transactions() {
|
||||||
|
let mut txq = TransactionQueue::with_limits(10, 1);
|
||||||
|
let (tx1, tx2) = new_txs(U256::from(4));
|
||||||
|
let (tx3, tx4) = new_txs(U256::from(4));
|
||||||
|
txq.add(tx1.clone(), &default_nonce);
|
||||||
|
txq.add(tx3.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
txq.add(tx4.clone(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_drop_transactions_with_old_nonces() {
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let tx = new_tx();
|
||||||
|
let last_nonce = tx.nonce.clone() + U256::one();
|
||||||
|
let fetch_last_nonce = |_a: &Address| last_nonce;
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx, &fetch_last_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 0);
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_accept_same_transaction_twice() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let (tx1, tx2) = new_txs(U256::from(1));
|
||||||
|
txq.add(tx1.clone(), &default_nonce);
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx1.hash(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
txq.add(tx1.clone(), &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(stats.pending, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
||||||
|
// given
|
||||||
|
let next_nonce = |a: &Address| default_nonce(a) + U256::one();
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
let tx3 = new_tx();
|
||||||
|
txq.add(tx2.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
txq.add(tx3.clone(), &default_nonce);
|
||||||
|
txq.add(tx.clone(), &default_nonce);
|
||||||
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx.hash(), &next_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(stats.pending, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -36,6 +36,7 @@ libc = "0.2.7"
|
|||||||
vergen = "0.1"
|
vergen = "0.1"
|
||||||
target_info = "0.1"
|
target_info = "0.1"
|
||||||
bigint = { path = "bigint" }
|
bigint = { path = "bigint" }
|
||||||
|
chrono = "0.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -28,7 +28,7 @@ extern crate ethcore_util;
|
|||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
|
||||||
use test::{Bencher, black_box};
|
use test::{Bencher, black_box};
|
||||||
use ethcore_util::uint::*;
|
use ethcore_util::numbers::*;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn u256_add(b: &mut Bencher) {
|
fn u256_add(b: &mut Bencher) {
|
||||||
|
@ -28,7 +28,7 @@ extern crate ethcore_util;
|
|||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use ethcore_util::rlp::*;
|
use ethcore_util::rlp::*;
|
||||||
use ethcore_util::uint::U256;
|
use ethcore_util::numbers::U256;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_stream_u64_value(b: &mut Bencher) {
|
fn bench_stream_u64_value(b: &mut Bencher) {
|
||||||
|
@ -40,20 +40,13 @@ use std::fmt;
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
|
||||||
use std::slice;
|
|
||||||
use std::result;
|
|
||||||
use std::option;
|
|
||||||
use std::str::{FromStr};
|
use std::str::{FromStr};
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
use std::cmp::*;
|
use std::cmp::*;
|
||||||
use std::collections::*;
|
|
||||||
|
|
||||||
use serde;
|
use serde;
|
||||||
use rustc_serialize::json::Json;
|
|
||||||
use rustc_serialize::base64::FromBase64;
|
|
||||||
use rustc_serialize::hex::{FromHex, FromHexError, ToHex};
|
use rustc_serialize::hex::{FromHex, FromHexError, ToHex};
|
||||||
|
|
||||||
|
|
||||||
@ -785,6 +778,35 @@ macro_rules! construct_uint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl serde::Deserialize for $name {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<$name, D::Error>
|
||||||
|
where D: serde::Deserializer {
|
||||||
|
struct UintVisitor;
|
||||||
|
|
||||||
|
impl serde::de::Visitor for UintVisitor {
|
||||||
|
type Value = $name;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
// 0x + len
|
||||||
|
if value.len() != 2 + $n_words / 8 {
|
||||||
|
return Err(serde::Error::custom("Invalid length."));
|
||||||
|
}
|
||||||
|
|
||||||
|
match $name::from_str(&value[2..]) {
|
||||||
|
Ok(val) => Ok(val),
|
||||||
|
Err(_) => { return Err(serde::Error::custom("Invalid length.")); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
|
||||||
|
self.visit_str(value.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize(UintVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u64> for $name {
|
impl From<u64> for $name {
|
||||||
fn from(value: u64) -> $name {
|
fn from(value: u64) -> $name {
|
||||||
let mut ret = [0; $n_words];
|
let mut ret = [0; $n_words];
|
||||||
@ -1273,6 +1295,33 @@ impl From<U512> for U256 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a U256> for U512 {
|
||||||
|
fn from(value: &'a U256) -> U512 {
|
||||||
|
let U256(ref arr) = *value;
|
||||||
|
let mut ret = [0; 8];
|
||||||
|
ret[0] = arr[0];
|
||||||
|
ret[1] = arr[1];
|
||||||
|
ret[2] = arr[2];
|
||||||
|
ret[3] = arr[3];
|
||||||
|
U512(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a U512> for U256 {
|
||||||
|
fn from(value: &'a U512) -> U256 {
|
||||||
|
let U512(ref arr) = *value;
|
||||||
|
if arr[4] | arr[5] | arr[6] | arr[7] != 0 {
|
||||||
|
panic!("Overflow");
|
||||||
|
}
|
||||||
|
let mut ret = [0; 4];
|
||||||
|
ret[0] = arr[0];
|
||||||
|
ret[1] = arr[1];
|
||||||
|
ret[2] = arr[2];
|
||||||
|
ret[3] = arr[3];
|
||||||
|
U256(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<U256> for U128 {
|
impl From<U256> for U128 {
|
||||||
fn from(value: U256) -> U128 {
|
fn from(value: U256) -> U128 {
|
||||||
let U256(ref arr) = value;
|
let U256(ref arr) = value;
|
||||||
@ -1983,6 +2032,7 @@ mod tests {
|
|||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(feature = "dev", allow(cyclomatic_complexity))]
|
||||||
fn u256_multi_full_mul() {
|
fn u256_multi_full_mul() {
|
||||||
let result = U256([0, 0, 0, 0]).full_mul(U256([0, 0, 0, 0]));
|
let result = U256([0, 0, 0, 0]).full_mul(U256([0, 0, 0, 0]));
|
||||||
assert_eq!(U512([0, 0, 0, 0, 0, 0, 0, 0]), result);
|
assert_eq!(U512([0, 0, 0, 0, 0, 0, 0, 0]), result);
|
||||||
|
@ -25,7 +25,10 @@ use kvdb::{Database, DBTransaction, DatabaseConfig};
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
||||||
/// and latent-removal semantics.
|
/// and, possibly, latent-removal semantics.
|
||||||
|
///
|
||||||
|
/// If `counters` is `None`, then it behaves exactly like OverlayDB. If not it behaves
|
||||||
|
/// differently:
|
||||||
///
|
///
|
||||||
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
||||||
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
@ -34,7 +37,7 @@ use std::env;
|
|||||||
pub struct JournalDB {
|
pub struct JournalDB {
|
||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
counters: Arc<RwLock<HashMap<H256, i32>>>,
|
counters: Option<Arc<RwLock<HashMap<H256, i32>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for JournalDB {
|
impl Clone for JournalDB {
|
||||||
@ -48,10 +51,11 @@ impl Clone for JournalDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// all keys must be at least 12 bytes
|
// all keys must be at least 12 bytes
|
||||||
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
|
||||||
const DB_VERSION: u32 = 3;
|
const DB_VERSION : u32 = 3;
|
||||||
|
const DB_VERSION_NO_JOURNAL : u32 = 3 + 256;
|
||||||
|
|
||||||
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
||||||
|
|
||||||
@ -59,25 +63,38 @@ impl JournalDB {
|
|||||||
|
|
||||||
/// Create a new instance from file
|
/// Create a new instance from file
|
||||||
pub fn new(path: &str) -> JournalDB {
|
pub fn new(path: &str) -> JournalDB {
|
||||||
|
Self::from_prefs(path, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance from file
|
||||||
|
pub fn from_prefs(path: &str, prefer_journal: bool) -> JournalDB {
|
||||||
let opts = DatabaseConfig {
|
let opts = DatabaseConfig {
|
||||||
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
||||||
};
|
};
|
||||||
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
||||||
panic!("Error opening state db: {}", e);
|
panic!("Error opening state db: {}", e);
|
||||||
});
|
});
|
||||||
|
let with_journal;
|
||||||
if !backing.is_empty() {
|
if !backing.is_empty() {
|
||||||
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
||||||
Ok(Some(DB_VERSION)) => {},
|
Ok(Some(DB_VERSION)) => { with_journal = true; },
|
||||||
|
Ok(Some(DB_VERSION_NO_JOURNAL)) => { with_journal = false; },
|
||||||
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
backing.put(&VERSION_KEY, &encode(&(if prefer_journal { DB_VERSION } else { DB_VERSION_NO_JOURNAL }))).expect("Error writing version to database");
|
||||||
|
with_journal = prefer_journal;
|
||||||
}
|
}
|
||||||
let counters = JournalDB::read_counters(&backing);
|
|
||||||
|
let counters = if with_journal {
|
||||||
|
Some(Arc::new(RwLock::new(JournalDB::read_counters(&backing))))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
JournalDB {
|
JournalDB {
|
||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: Arc::new(backing),
|
backing: Arc::new(backing),
|
||||||
counters: Arc::new(RwLock::new(counters)),
|
counters: counters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,9 +111,47 @@ impl JournalDB {
|
|||||||
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Commit all recent insert operations.
|
||||||
|
pub fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
|
let have_counters = self.counters.is_some();
|
||||||
|
if have_counters {
|
||||||
|
self.commit_with_counters(now, id, end)
|
||||||
|
} else {
|
||||||
|
self.commit_without_counters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Drain the overlay and place it into a batch for the DB.
|
||||||
|
fn batch_overlay_insertions(overlay: &mut MemoryDB, batch: &DBTransaction) -> usize {
|
||||||
|
let mut inserts = 0usize;
|
||||||
|
let mut deletes = 0usize;
|
||||||
|
for i in overlay.drain().into_iter() {
|
||||||
|
let (key, (value, rc)) = i;
|
||||||
|
if rc > 0 {
|
||||||
|
assert!(rc == 1);
|
||||||
|
batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
inserts += 1;
|
||||||
|
}
|
||||||
|
if rc < 0 {
|
||||||
|
assert!(rc == -1);
|
||||||
|
deletes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("commit: Inserted {}, Deleted {} nodes", inserts, deletes);
|
||||||
|
inserts + deletes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Just commit the overlay into the backing DB.
|
||||||
|
fn commit_without_counters(&mut self) -> Result<u32, UtilError> {
|
||||||
|
let batch = DBTransaction::new();
|
||||||
|
let ret = Self::batch_overlay_insertions(&mut self.overlay, &batch);
|
||||||
|
try!(self.backing.write(batch));
|
||||||
|
Ok(ret as u32)
|
||||||
|
}
|
||||||
|
|
||||||
/// Commit all recent insert operations and historical removals from the old era
|
/// Commit all recent insert operations and historical removals from the old era
|
||||||
/// to the backing database.
|
/// to the backing database.
|
||||||
pub fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
// journal format:
|
// journal format:
|
||||||
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ]
|
// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
@ -121,20 +176,30 @@ impl JournalDB {
|
|||||||
// and the key is safe to delete.
|
// and the key is safe to delete.
|
||||||
|
|
||||||
// record new commit's details.
|
// record new commit's details.
|
||||||
|
trace!("commit: #{} ({}), end era: {:?}", now, id, end);
|
||||||
|
let mut counters = self.counters.as_ref().unwrap().write().unwrap();
|
||||||
let batch = DBTransaction::new();
|
let batch = DBTransaction::new();
|
||||||
let mut counters = self.counters.write().unwrap();
|
|
||||||
{
|
{
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
let mut last;
|
let mut last;
|
||||||
|
|
||||||
while try!(self.backing.get({
|
while {
|
||||||
let mut r = RlpStream::new_list(3);
|
let record = try!(self.backing.get({
|
||||||
r.append(&now);
|
let mut r = RlpStream::new_list(3);
|
||||||
r.append(&index);
|
r.append(&now);
|
||||||
r.append(&&PADDING[..]);
|
r.append(&index);
|
||||||
last = r.drain();
|
r.append(&&PADDING[..]);
|
||||||
&last
|
last = r.drain();
|
||||||
})).is_some() {
|
&last
|
||||||
|
}));
|
||||||
|
match record {
|
||||||
|
Some(r) => {
|
||||||
|
assert!(&Rlp::new(&r).val_at::<H256>(0) != id);
|
||||||
|
true
|
||||||
|
},
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
} {
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,16 +232,20 @@ impl JournalDB {
|
|||||||
&last
|
&last
|
||||||
})) {
|
})) {
|
||||||
let rlp = Rlp::new(&rlp_data);
|
let rlp = Rlp::new(&rlp_data);
|
||||||
let inserts: Vec<H256> = rlp.val_at(1);
|
let mut inserts: Vec<H256> = rlp.val_at(1);
|
||||||
JournalDB::decrease_counters(&inserts, &mut counters);
|
JournalDB::decrease_counters(&inserts, &mut counters);
|
||||||
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
||||||
if canon_id == rlp.val_at(0) {
|
if canon_id == rlp.val_at(0) {
|
||||||
to_remove.extend(rlp.at(2).iter().map(|r| r.as_val::<H256>()));
|
let mut canon_deletes: Vec<H256> = rlp.val_at(2);
|
||||||
|
trace!("Purging nodes deleted from canon: {:?}", canon_deletes);
|
||||||
|
to_remove.append(&mut canon_deletes);
|
||||||
canon_inserts = inserts;
|
canon_inserts = inserts;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
to_remove.extend(inserts);
|
trace!("Purging nodes inserted in non-canon: {:?}", inserts);
|
||||||
|
to_remove.append(&mut inserts);
|
||||||
}
|
}
|
||||||
|
trace!("commit: Delete journal for time #{}.{}: {}, (canon was {}): {} entries", end_era, index, rlp.val_at::<H256>(0), canon_id, to_remove.len());
|
||||||
try!(batch.delete(&last));
|
try!(batch.delete(&last));
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
@ -184,33 +253,18 @@ impl JournalDB {
|
|||||||
let canon_inserts = canon_inserts.drain(..).collect::<HashSet<_>>();
|
let canon_inserts = canon_inserts.drain(..).collect::<HashSet<_>>();
|
||||||
// Purge removed keys if they are not referenced and not re-inserted in the canon commit
|
// Purge removed keys if they are not referenced and not re-inserted in the canon commit
|
||||||
let mut deletes = 0;
|
let mut deletes = 0;
|
||||||
|
trace!("Purging filtered nodes: {:?}", to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)).collect::<Vec<_>>());
|
||||||
for h in to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)) {
|
for h in to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)) {
|
||||||
try!(batch.delete(&h));
|
try!(batch.delete(&h));
|
||||||
deletes += 1;
|
deletes += 1;
|
||||||
}
|
}
|
||||||
trace!("JournalDB: delete journal for time #{}.{}, (canon was {}): {} entries", end_era, index, canon_id, deletes);
|
trace!("Total nodes purged: {}", deletes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit overlay insertions
|
// Commit overlay insertions
|
||||||
let mut ret = 0u32;
|
let ret = Self::batch_overlay_insertions(&mut self.overlay, &batch);
|
||||||
let mut deletes = 0usize;
|
|
||||||
for i in self.overlay.drain().into_iter() {
|
|
||||||
let (key, (value, rc)) = i;
|
|
||||||
if rc > 0 {
|
|
||||||
assert!(rc == 1);
|
|
||||||
batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?");
|
|
||||||
ret += 1;
|
|
||||||
}
|
|
||||||
if rc < 0 {
|
|
||||||
assert!(rc == -1);
|
|
||||||
ret += 1;
|
|
||||||
deletes += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try!(self.backing.write(batch));
|
try!(self.backing.write(batch));
|
||||||
trace!("JournalDB::commit() deleted {} nodes", deletes);
|
Ok(ret as u32)
|
||||||
Ok(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -461,17 +461,17 @@ enum KeyFileLoadError {
|
|||||||
pub struct KeyDirectory {
|
pub struct KeyDirectory {
|
||||||
/// Directory path for key management.
|
/// Directory path for key management.
|
||||||
path: String,
|
path: String,
|
||||||
cache: RefCell<HashMap<Uuid, KeyFileContent>>,
|
cache: RwLock<HashMap<Uuid, KeyFileContent>>,
|
||||||
cache_usage: RefCell<VecDeque<Uuid>>,
|
cache_usage: RwLock<VecDeque<Uuid>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyDirectory {
|
impl KeyDirectory {
|
||||||
/// Initializes new cache directory context with a given `path`
|
/// Initializes new cache directory context with a given `path`
|
||||||
pub fn new(path: &Path) -> KeyDirectory {
|
pub fn new(path: &Path) -> KeyDirectory {
|
||||||
KeyDirectory {
|
KeyDirectory {
|
||||||
cache: RefCell::new(HashMap::new()),
|
cache: RwLock::new(HashMap::new()),
|
||||||
path: path.to_str().expect("Initialized key directory with empty path").to_owned(),
|
path: path.to_str().expect("Initialized key directory with empty path").to_owned(),
|
||||||
cache_usage: RefCell::new(VecDeque::new()),
|
cache_usage: RwLock::new(VecDeque::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +484,7 @@ impl KeyDirectory {
|
|||||||
let json_bytes = json_text.into_bytes();
|
let json_bytes = json_text.into_bytes();
|
||||||
try!(file.write(&json_bytes));
|
try!(file.write(&json_bytes));
|
||||||
}
|
}
|
||||||
let mut cache = self.cache.borrow_mut();
|
let mut cache = self.cache.write().unwrap();
|
||||||
let id = key_file.id.clone();
|
let id = key_file.id.clone();
|
||||||
cache.insert(id.clone(), key_file);
|
cache.insert(id.clone(), key_file);
|
||||||
Ok(id.clone())
|
Ok(id.clone())
|
||||||
@ -495,14 +495,14 @@ impl KeyDirectory {
|
|||||||
pub fn get(&self, id: &Uuid) -> Option<KeyFileContent> {
|
pub fn get(&self, id: &Uuid) -> Option<KeyFileContent> {
|
||||||
let path = self.key_path(id);
|
let path = self.key_path(id);
|
||||||
{
|
{
|
||||||
let mut usage = self.cache_usage.borrow_mut();
|
let mut usage = self.cache_usage.write().unwrap();
|
||||||
usage.push_back(id.clone());
|
usage.push_back(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.cache.borrow().contains_key(id) {
|
if !self.cache.read().unwrap().contains_key(id) {
|
||||||
match KeyDirectory::load_key(&path) {
|
match KeyDirectory::load_key(&path) {
|
||||||
Ok(loaded_key) => {
|
Ok(loaded_key) => {
|
||||||
self.cache.borrow_mut().insert(id.to_owned(), loaded_key);
|
self.cache.write().unwrap().insert(id.to_owned(), loaded_key);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
warn!(target: "sstore", "error loading key {:?}: {:?}", id, error);
|
warn!(target: "sstore", "error loading key {:?}: {:?}", id, error);
|
||||||
@ -512,7 +512,7 @@ impl KeyDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// todo: replace with Ref::map when it stabilized to avoid copies
|
// todo: replace with Ref::map when it stabilized to avoid copies
|
||||||
Some(self.cache.borrow().get(id)
|
Some(self.cache.read().unwrap().get(id)
|
||||||
.expect("Key should be there, we have just inserted or checked it.")
|
.expect("Key should be there, we have just inserted or checked it.")
|
||||||
.clone())
|
.clone())
|
||||||
}
|
}
|
||||||
@ -524,7 +524,7 @@ impl KeyDirectory {
|
|||||||
|
|
||||||
/// Removes keys that never been requested during last `MAX_USAGE_TRACK` times
|
/// Removes keys that never been requested during last `MAX_USAGE_TRACK` times
|
||||||
pub fn collect_garbage(&mut self) {
|
pub fn collect_garbage(&mut self) {
|
||||||
let mut cache_usage = self.cache_usage.borrow_mut();
|
let mut cache_usage = self.cache_usage.write().unwrap();
|
||||||
|
|
||||||
let total_usages = cache_usage.len();
|
let total_usages = cache_usage.len();
|
||||||
let untracked_usages = max(total_usages as i64 - MAX_CACHE_USAGE_TRACK as i64, 0) as usize;
|
let untracked_usages = max(total_usages as i64 - MAX_CACHE_USAGE_TRACK as i64, 0) as usize;
|
||||||
@ -532,31 +532,31 @@ impl KeyDirectory {
|
|||||||
cache_usage.drain(..untracked_usages);
|
cache_usage.drain(..untracked_usages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.cache.borrow().len() <= MAX_CACHE_USAGE_TRACK { return; }
|
if self.cache.read().unwrap().len() <= MAX_CACHE_USAGE_TRACK { return; }
|
||||||
|
|
||||||
let uniqs: HashSet<&Uuid> = cache_usage.iter().collect();
|
let uniqs: HashSet<&Uuid> = cache_usage.iter().collect();
|
||||||
let removes:Vec<Uuid> = {
|
let removes:Vec<Uuid> = {
|
||||||
let cache = self.cache.borrow();
|
let cache = self.cache.read().unwrap();
|
||||||
cache.keys().cloned().filter(|key| !uniqs.contains(key)).collect()
|
cache.keys().cloned().filter(|key| !uniqs.contains(key)).collect()
|
||||||
};
|
};
|
||||||
if removes.is_empty() { return; }
|
if removes.is_empty() { return; }
|
||||||
let mut cache = self.cache.borrow_mut();
|
let mut cache = self.cache.write().unwrap();
|
||||||
for key in removes { cache.remove(&key); }
|
for key in removes { cache.remove(&key); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reports how many keys are currently cached.
|
/// Reports how many keys are currently cached.
|
||||||
pub fn cache_size(&self) -> usize {
|
pub fn cache_size(&self) -> usize {
|
||||||
self.cache.borrow().len()
|
self.cache.read().unwrap().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes key file from key directory
|
/// Removes key file from key directory
|
||||||
pub fn delete(&mut self, id: &Uuid) -> Result<(), ::std::io::Error> {
|
pub fn delete(&mut self, id: &Uuid) -> Result<(), ::std::io::Error> {
|
||||||
let path = self.key_path(id);
|
let path = self.key_path(id);
|
||||||
|
|
||||||
if !self.cache.borrow().contains_key(id) {
|
if !self.cache.read().unwrap().contains_key(id) {
|
||||||
return match fs::remove_file(&path) {
|
return match fs::remove_file(&path) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.cache.borrow_mut().remove(&id);
|
self.cache.write().unwrap().remove(&id);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Err(e) => Err(e)
|
Err(e) => Err(e)
|
||||||
|
@ -99,10 +99,20 @@ mod tests {
|
|||||||
use common::*;
|
use common::*;
|
||||||
use keys::store::SecretStore;
|
use keys::store::SecretStore;
|
||||||
|
|
||||||
|
fn test_path() -> &'static str {
|
||||||
|
match ::std::fs::metadata("res") {
|
||||||
|
Ok(_) => "res/geth_keystore",
|
||||||
|
Err(_) => "util/res/geth_keystore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_path_param(param_val: &'static str) -> String {
|
||||||
|
test_path().to_owned() + param_val
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_enumerate() {
|
fn can_enumerate() {
|
||||||
let keys = enumerate_geth_keys(Path::new("res/geth_keystore")).unwrap();
|
let keys = enumerate_geth_keys(Path::new(test_path())).unwrap();
|
||||||
assert_eq!(2, keys.len());
|
assert_eq!(2, keys.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +120,7 @@ mod tests {
|
|||||||
fn can_import() {
|
fn can_import() {
|
||||||
let temp = ::devtools::RandomTempPath::create_dir();
|
let temp = ::devtools::RandomTempPath::create_dir();
|
||||||
let mut secret_store = SecretStore::new_in(temp.as_path());
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
||||||
import_geth_key(&mut secret_store, Path::new("res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9")).unwrap();
|
import_geth_key(&mut secret_store, Path::new(&test_path_param("/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9"))).unwrap();
|
||||||
let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap());
|
let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap());
|
||||||
assert!(key.is_some());
|
assert!(key.is_some());
|
||||||
}
|
}
|
||||||
@ -119,7 +129,7 @@ mod tests {
|
|||||||
fn can_import_directory() {
|
fn can_import_directory() {
|
||||||
let temp = ::devtools::RandomTempPath::create_dir();
|
let temp = ::devtools::RandomTempPath::create_dir();
|
||||||
let mut secret_store = SecretStore::new_in(temp.as_path());
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
||||||
import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap();
|
import_geth_keys(&mut secret_store, Path::new(test_path())).unwrap();
|
||||||
|
|
||||||
let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap());
|
let key = secret_store.account(&Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap());
|
||||||
assert!(key.is_some());
|
assert!(key.is_some());
|
||||||
@ -134,7 +144,7 @@ mod tests {
|
|||||||
let temp = ::devtools::RandomTempPath::create_dir();
|
let temp = ::devtools::RandomTempPath::create_dir();
|
||||||
{
|
{
|
||||||
let mut secret_store = SecretStore::new_in(temp.as_path());
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
||||||
import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap();
|
import_geth_keys(&mut secret_store, Path::new(test_path())).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_directory = KeyDirectory::new(&temp.as_path());
|
let key_directory = KeyDirectory::new(&temp.as_path());
|
||||||
@ -156,7 +166,7 @@ mod tests {
|
|||||||
|
|
||||||
let temp = ::devtools::RandomTempPath::create_dir();
|
let temp = ::devtools::RandomTempPath::create_dir();
|
||||||
let mut secret_store = SecretStore::new_in(temp.as_path());
|
let mut secret_store = SecretStore::new_in(temp.as_path());
|
||||||
import_geth_keys(&mut secret_store, Path::new("res/geth_keystore")).unwrap();
|
import_geth_keys(&mut secret_store, Path::new(test_path())).unwrap();
|
||||||
|
|
||||||
let val = secret_store.get::<Bytes>(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap(), "123");
|
let val = secret_store.get::<Bytes>(&H128::from_str("62a0ad73556d496a8e1c0783d30d3ace").unwrap(), "123");
|
||||||
assert!(val.is_ok());
|
assert!(val.is_ok());
|
||||||
|
@ -22,6 +22,7 @@ use rcrypto::pbkdf2::*;
|
|||||||
use rcrypto::scrypt::*;
|
use rcrypto::scrypt::*;
|
||||||
use rcrypto::hmac::*;
|
use rcrypto::hmac::*;
|
||||||
use crypto;
|
use crypto;
|
||||||
|
use chrono::*;
|
||||||
|
|
||||||
const KEY_LENGTH: u32 = 32;
|
const KEY_LENGTH: u32 = 32;
|
||||||
const KEY_ITERATIONS: u32 = 10240;
|
const KEY_ITERATIONS: u32 = 10240;
|
||||||
@ -55,9 +56,26 @@ pub enum EncryptedHashMapError {
|
|||||||
InvalidValueFormat(FromBytesError),
|
InvalidValueFormat(FromBytesError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error retrieving value from encrypted hashmap
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SigningError {
|
||||||
|
/// Account passed does not exist
|
||||||
|
NoAccount,
|
||||||
|
/// Account passed is not unlocked
|
||||||
|
AccountNotUnlocked,
|
||||||
|
/// Invalid secret in store
|
||||||
|
InvalidSecret
|
||||||
|
}
|
||||||
|
|
||||||
/// Represent service for storing encrypted arbitrary data
|
/// Represent service for storing encrypted arbitrary data
|
||||||
pub struct SecretStore {
|
pub struct SecretStore {
|
||||||
directory: KeyDirectory
|
directory: KeyDirectory,
|
||||||
|
unlocks: RwLock<HashMap<Address, AccountUnlock>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AccountUnlock {
|
||||||
|
secret: H256,
|
||||||
|
expires: DateTime<UTC>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretStore {
|
impl SecretStore {
|
||||||
@ -72,7 +90,8 @@ impl SecretStore {
|
|||||||
/// new instance of Secret Store in specific directory
|
/// new instance of Secret Store in specific directory
|
||||||
pub fn new_in(path: &Path) -> SecretStore {
|
pub fn new_in(path: &Path) -> SecretStore {
|
||||||
SecretStore {
|
SecretStore {
|
||||||
directory: KeyDirectory::new(path)
|
directory: KeyDirectory::new(path),
|
||||||
|
unlocks: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +105,7 @@ impl SecretStore {
|
|||||||
import_path.push(".ethereum");
|
import_path.push(".ethereum");
|
||||||
import_path.push("keystore");
|
import_path.push("keystore");
|
||||||
if let Err(e) = geth_import::import_geth_keys(self, &import_path) {
|
if let Err(e) = geth_import::import_geth_keys(self, &import_path) {
|
||||||
warn!(target: "sstore", "Error retrieving geth keys: {:?}", e)
|
trace!(target: "sstore", "Geth key not imported: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,9 +139,57 @@ impl SecretStore {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn new_test(path: &::devtools::RandomTempPath) -> SecretStore {
|
fn new_test(path: &::devtools::RandomTempPath) -> SecretStore {
|
||||||
SecretStore {
|
SecretStore {
|
||||||
directory: KeyDirectory::new(path.as_path())
|
directory: KeyDirectory::new(path.as_path()),
|
||||||
|
unlocks: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unlocks account for use
|
||||||
|
pub fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
let secret_id = try!(self.account(&account).ok_or(EncryptedHashMapError::UnknownIdentifier));
|
||||||
|
let secret = try!(self.get(&secret_id, pass));
|
||||||
|
{
|
||||||
|
let mut write_lock = self.unlocks.write().unwrap();
|
||||||
|
let mut unlock = write_lock.entry(*account)
|
||||||
|
.or_insert_with(|| AccountUnlock { secret: secret, expires: UTC::now() });
|
||||||
|
unlock.secret = secret;
|
||||||
|
unlock.expires = UTC::now() + Duration::minutes(20);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new account
|
||||||
|
pub fn new_account(&mut self, pass: &str) -> Result<Address, ::std::io::Error> {
|
||||||
|
let secret = H256::random();
|
||||||
|
let key_id = H128::random();
|
||||||
|
self.insert(key_id.clone(), secret, pass);
|
||||||
|
|
||||||
|
let mut key_file = self.directory.get(&key_id).expect("the key was just inserted");
|
||||||
|
let address = Address::random();
|
||||||
|
key_file.account = Some(address);
|
||||||
|
try!(self.directory.save(key_file));
|
||||||
|
Ok(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs message with unlocked account
|
||||||
|
pub fn sign(&self, account: &Address, message: &H256) -> Result<crypto::Signature, SigningError> {
|
||||||
|
let read_lock = self.unlocks.read().unwrap();
|
||||||
|
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
||||||
|
match crypto::KeyPair::from_secret(unlock.secret) {
|
||||||
|
Ok(pair) => match pair.sign(message) {
|
||||||
|
Ok(signature) => Ok(signature),
|
||||||
|
Err(_) => Err(SigningError::InvalidSecret)
|
||||||
|
},
|
||||||
|
Err(_) => Err(SigningError::InvalidSecret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns secret for unlocked account
|
||||||
|
pub fn account_secret(&self, account: &Address) -> Result<crypto::Secret, SigningError> {
|
||||||
|
let read_lock = self.unlocks.read().unwrap();
|
||||||
|
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
||||||
|
Ok(unlock.secret as crypto::Secret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
|
fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
|
||||||
@ -369,6 +436,40 @@ mod tests {
|
|||||||
assert_eq!(4, sstore.directory.list().unwrap().len())
|
assert_eq!(4, sstore.directory.list().unwrap().len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_create_account() {
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let mut sstore = SecretStore::new_test(&temp);
|
||||||
|
sstore.new_account("123").unwrap();
|
||||||
|
assert_eq!(1, sstore.accounts().unwrap().len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_unlock_account() {
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let mut sstore = SecretStore::new_test(&temp);
|
||||||
|
let address = sstore.new_account("123").unwrap();
|
||||||
|
|
||||||
|
let secret = sstore.unlock_account(&address, "123");
|
||||||
|
assert!(secret.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_sign_data() {
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let address = {
|
||||||
|
let mut sstore = SecretStore::new_test(&temp);
|
||||||
|
sstore.new_account("334").unwrap()
|
||||||
|
};
|
||||||
|
let signature = {
|
||||||
|
let sstore = SecretStore::new_test(&temp);
|
||||||
|
sstore.unlock_account(&address, "334").unwrap();
|
||||||
|
sstore.sign(&address, &H256::random()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(signature != x!(0));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_import_account() {
|
fn can_import_account() {
|
||||||
use keys::directory::{KeyFileContent, KeyFileCrypto};
|
use keys::directory::{KeyFileContent, KeyFileCrypto};
|
||||||
|
@ -111,6 +111,7 @@ extern crate rustc_version;
|
|||||||
extern crate target_info;
|
extern crate target_info;
|
||||||
extern crate vergen;
|
extern crate vergen;
|
||||||
extern crate bigint;
|
extern crate bigint;
|
||||||
|
extern crate chrono;
|
||||||
|
|
||||||
pub mod standard;
|
pub mod standard;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use rlp::{Stream, RlpStream};
|
||||||
use target_info::Target;
|
use target_info::Target;
|
||||||
use rustc_version;
|
use rustc_version;
|
||||||
|
|
||||||
@ -69,5 +70,19 @@ pub fn contents(name: &str) -> Result<Bytes, UtilError> {
|
|||||||
|
|
||||||
/// Get the standard version string for this software.
|
/// Get the standard version string for this software.
|
||||||
pub fn version() -> String {
|
pub fn version() -> String {
|
||||||
format!("Parity//{}-{}-{}/{}-{}-{}/rustc{}", env!("CARGO_PKG_VERSION"), short_sha(), commit_date().replace("-", ""), Target::arch(), Target::os(), Target::env(), rustc_version::version())
|
format!("Parity/v{}-{}-{}/{}-{}-{}/rustc{}", env!("CARGO_PKG_VERSION"), short_sha(), commit_date().replace("-", ""), Target::arch(), Target::os(), Target::env(), rustc_version::version())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the standard version data for this software.
|
||||||
|
pub fn version_data() -> Bytes {
|
||||||
|
let mut s = RlpStream::new_list(4);
|
||||||
|
let v =
|
||||||
|
(u32::from_str(env!("CARGO_PKG_VERSION_MAJOR")).unwrap() << 16) +
|
||||||
|
(u32::from_str(env!("CARGO_PKG_VERSION_MINOR")).unwrap() << 8) +
|
||||||
|
u32::from_str(env!("CARGO_PKG_VERSION_PATCH")).unwrap();
|
||||||
|
s.append(&v);
|
||||||
|
s.append(&"Parity");
|
||||||
|
s.append(&format!("{}", rustc_version::version()));
|
||||||
|
s.append(&&Target::os()[0..2]);
|
||||||
|
s.out()
|
||||||
}
|
}
|
@ -190,25 +190,25 @@ impl Connection {
|
|||||||
|
|
||||||
/// Register this connection with the IO event loop.
|
/// Register this connection with the IO event loop.
|
||||||
pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn register_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection register; token={:?}", reg);
|
trace!(target: "network", "connection register; token={:?}", reg);
|
||||||
if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
|
if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()) {
|
||||||
debug!("Failed to register {:?}, {:?}", reg, e);
|
trace!(target: "network", "Failed to register {:?}, {:?}", reg, e);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update connection registration. Should be called at the end of the IO handler.
|
/// Update connection registration. Should be called at the end of the IO handler.
|
||||||
pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn update_socket<Host: Handler>(&self, reg: Token, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection reregister; token={:?}", reg);
|
trace!(target: "network", "connection reregister; token={:?}", reg);
|
||||||
event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()).or_else(|e| {
|
event_loop.reregister( &self.socket, reg, self.interest, PollOpt::edge() | PollOpt::oneshot()).or_else(|e| {
|
||||||
debug!("Failed to reregister {:?}, {:?}", reg, e);
|
trace!(target: "network", "Failed to reregister {:?}, {:?}", reg, e);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete connection registration. Should be called at the end of the IO handler.
|
/// Delete connection registration. Should be called at the end of the IO handler.
|
||||||
pub fn deregister_socket<Host: Handler>(&self, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
pub fn deregister_socket<Host: Handler>(&self, event_loop: &mut EventLoop<Host>) -> io::Result<()> {
|
||||||
trace!(target: "net", "connection deregister; token={:?}", self.token);
|
trace!(target: "network", "connection deregister; token={:?}", self.token);
|
||||||
event_loop.deregister(&self.socket).ok(); // ignore errors here
|
event_loop.deregister(&self.socket).ok(); // ignore errors here
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse, validate and confirm auth message
|
/// Parse, validate and confirm auth message
|
||||||
fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_auth(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
if data.len() != V4_AUTH_PACKET_SIZE {
|
if data.len() != V4_AUTH_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong auth packet size");
|
debug!(target:"net", "Wrong auth packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
@ -253,7 +253,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_auth_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
self.auth_cipher.extend_from_slice(data);
|
self.auth_cipher.extend_from_slice(data);
|
||||||
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&auth);
|
let rlp = UntrustedRlp::new(&auth);
|
||||||
@ -268,7 +268,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Parse and validate ack message
|
/// Parse and validate ack message
|
||||||
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||||
if data.len() != V4_ACK_PACKET_SIZE {
|
if data.len() != V4_ACK_PACKET_SIZE {
|
||||||
debug!(target:"net", "Wrong ack packet size");
|
debug!(target:"net", "Wrong ack packet size");
|
||||||
return Err(From::from(NetworkError::BadProtocol));
|
return Err(From::from(NetworkError::BadProtocol));
|
||||||
@ -296,7 +296,7 @@ impl Handshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
fn read_ack_eip8(&mut self, secret: &Secret, data: &[u8]) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Received EIP8 handshake auth from {:?}", self.connection.socket.peer_addr());
|
||||||
self.ack_cipher.extend_from_slice(data);
|
self.ack_cipher.extend_from_slice(data);
|
||||||
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
let ack = try!(ecies::decrypt(secret, &self.ack_cipher[0..2], &self.ack_cipher[2..]));
|
||||||
let rlp = UntrustedRlp::new(&ack);
|
let rlp = UntrustedRlp::new(&ack);
|
||||||
@ -309,7 +309,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends auth message
|
/// Sends auth message
|
||||||
fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> {
|
fn write_auth(&mut self, secret: &Secret, public: &Public) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending handshake auth to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -336,7 +336,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends ack message
|
/// Sends ack message
|
||||||
fn write_ack(&mut self) -> Result<(), UtilError> {
|
fn write_ack(&mut self) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending handshake ack to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending handshake ack to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
||||||
let len = data.len();
|
let len = data.len();
|
||||||
{
|
{
|
||||||
@ -355,7 +355,7 @@ impl Handshake {
|
|||||||
|
|
||||||
/// Sends EIP8 ack message
|
/// Sends EIP8 ack message
|
||||||
fn write_ack_eip8(&mut self) -> Result<(), UtilError> {
|
fn write_ack_eip8(&mut self) -> Result<(), UtilError> {
|
||||||
trace!(target:"net", "Sending EIP8 handshake ack to {:?}", self.connection.socket.peer_addr());
|
trace!(target:"network", "Sending EIP8 handshake ack to {:?}", self.connection.socket.peer_addr());
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(self.ecdhe.public());
|
rlp.append(self.ecdhe.public());
|
||||||
rlp.append(&self.nonce);
|
rlp.append(&self.nonce);
|
||||||
|
@ -170,29 +170,37 @@ pub struct NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'sta
|
|||||||
io: &'s IoContext<NetworkIoMessage<Message>>,
|
io: &'s IoContext<NetworkIoMessage<Message>>,
|
||||||
protocol: ProtocolId,
|
protocol: ProtocolId,
|
||||||
sessions: Arc<RwLock<Slab<SharedSession>>>,
|
sessions: Arc<RwLock<Slab<SharedSession>>>,
|
||||||
session: Option<StreamToken>,
|
session: Option<SharedSession>,
|
||||||
|
session_id: Option<StreamToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'static, {
|
impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone + 'static, {
|
||||||
/// Create a new network IO access point. Takes references to all the data that can be updated within the IO handler.
|
/// Create a new network IO access point. Takes references to all the data that can be updated within the IO handler.
|
||||||
fn new(io: &'s IoContext<NetworkIoMessage<Message>>,
|
fn new(io: &'s IoContext<NetworkIoMessage<Message>>,
|
||||||
protocol: ProtocolId,
|
protocol: ProtocolId,
|
||||||
session: Option<StreamToken>, sessions: Arc<RwLock<Slab<SharedSession>>>) -> NetworkContext<'s, Message> {
|
session: Option<SharedSession>, sessions: Arc<RwLock<Slab<SharedSession>>>) -> NetworkContext<'s, Message> {
|
||||||
|
let id = session.as_ref().map(|s| s.lock().unwrap().token());
|
||||||
NetworkContext {
|
NetworkContext {
|
||||||
io: io,
|
io: io,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
|
session_id: id,
|
||||||
session: session,
|
session: session,
|
||||||
sessions: sessions,
|
sessions: sessions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_session(&self, peer: PeerId) -> Option<SharedSession> {
|
||||||
|
match self.session_id {
|
||||||
|
Some(id) if id == peer => self.session.clone(),
|
||||||
|
_ => self.sessions.read().unwrap().get(peer).cloned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a packet over the network to another peer.
|
/// Send a packet over the network to another peer.
|
||||||
pub fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
pub fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
let session = { self.sessions.read().unwrap().get(peer).cloned() };
|
let session = self.resolve_session(peer);
|
||||||
if let Some(session) = session {
|
if let Some(session) = session {
|
||||||
session.lock().unwrap().deref_mut().send_packet(self.protocol, packet_id as u8, &data).unwrap_or_else(|e| {
|
try!(session.lock().unwrap().deref_mut().send_packet(self.protocol, packet_id as u8, &data));
|
||||||
warn!(target: "network", "Send error: {:?}", e);
|
|
||||||
}); //TODO: don't copy vector data
|
|
||||||
try!(self.io.update_registration(peer));
|
try!(self.io.update_registration(peer));
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "network", "Send: Peer no longer exist")
|
trace!(target: "network", "Send: Peer no longer exist")
|
||||||
@ -200,14 +208,10 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Respond to a current network message. Panics if no there is no packet in the context.
|
/// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing.
|
||||||
pub fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
pub fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), UtilError> {
|
||||||
match self.session {
|
assert!(self.session.is_some(), "Respond called without network context");
|
||||||
Some(session) => self.send(session, packet_id, data),
|
self.send(self.session_id.unwrap(), packet_id, data)
|
||||||
None => {
|
|
||||||
panic!("Respond: Session does not exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send an IO message
|
/// Send an IO message
|
||||||
@ -215,7 +219,6 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
self.io.message(NetworkIoMessage::User(msg));
|
self.io.message(NetworkIoMessage::User(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected.
|
/// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected.
|
||||||
pub fn disable_peer(&self, peer: PeerId) {
|
pub fn disable_peer(&self, peer: PeerId) {
|
||||||
//TODO: remove capability, disconnect if no capabilities left
|
//TODO: remove capability, disconnect if no capabilities left
|
||||||
@ -239,7 +242,7 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
|
|||||||
|
|
||||||
/// Returns peer identification string
|
/// Returns peer identification string
|
||||||
pub fn peer_info(&self, peer: PeerId) -> String {
|
pub fn peer_info(&self, peer: PeerId) -> String {
|
||||||
let session = { self.sessions.read().unwrap().get(peer).cloned() };
|
let session = self.resolve_session(peer);
|
||||||
if let Some(session) = session {
|
if let Some(session) = session {
|
||||||
return session.lock().unwrap().info.client_version.clone()
|
return session.lock().unwrap().info.client_version.clone()
|
||||||
}
|
}
|
||||||
@ -400,7 +403,8 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
// public_endpoint in host info contains local adderss at this point
|
// public_endpoint in host info contains local adderss at this point
|
||||||
let listen_address = self.info.read().unwrap().public_endpoint.address.clone();
|
let listen_address = self.info.read().unwrap().public_endpoint.address.clone();
|
||||||
let udp_port = self.info.read().unwrap().config.udp_port.unwrap_or(listen_address.port());
|
let udp_port = self.info.read().unwrap().config.udp_port.unwrap_or(listen_address.port());
|
||||||
let public_endpoint = match self.info.read().unwrap().config.public_address {
|
let public_address = self.info.read().unwrap().config.public_address.clone();
|
||||||
|
let public_endpoint = match public_address {
|
||||||
None => {
|
None => {
|
||||||
let public_address = select_public_address(listen_address.port());
|
let public_address = select_public_address(listen_address.port());
|
||||||
let local_endpoint = NodeEndpoint { address: public_address, udp_port: udp_port };
|
let local_endpoint = NodeEndpoint { address: public_address, udp_port: udp_port };
|
||||||
@ -623,7 +627,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None;
|
let mut packet_data: Option<(ProtocolId, PacketId, Vec<u8>)> = None;
|
||||||
let mut kill = false;
|
let mut kill = false;
|
||||||
let session = { self.sessions.read().unwrap().get(token).cloned() };
|
let session = { self.sessions.read().unwrap().get(token).cloned() };
|
||||||
if let Some(session) = session {
|
if let Some(session) = session.clone() {
|
||||||
let mut s = session.lock().unwrap();
|
let mut s = session.lock().unwrap();
|
||||||
match s.readable(io, &self.info.read().unwrap()) {
|
match s.readable(io, &self.info.read().unwrap()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -655,11 +659,11 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
}
|
}
|
||||||
for p in ready_data {
|
for p in ready_data {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.connected(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token);
|
h.connected(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token);
|
||||||
}
|
}
|
||||||
if let Some((p, packet_id, data)) = packet_data {
|
if let Some((p, packet_id, data)) = packet_data {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.read(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token, packet_id, &data[1..]);
|
h.read(&NetworkContext::new(io, p, session.clone(), self.sessions.clone()), &token, packet_id, &data[1..]);
|
||||||
}
|
}
|
||||||
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
|
io.update_registration(token).unwrap_or_else(|e| debug!(target: "network", "Token registration error: {:?}", e));
|
||||||
}
|
}
|
||||||
@ -717,6 +721,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
let mut to_disconnect: Vec<ProtocolId> = Vec::new();
|
let mut to_disconnect: Vec<ProtocolId> = Vec::new();
|
||||||
let mut failure_id = None;
|
let mut failure_id = None;
|
||||||
let mut deregister = false;
|
let mut deregister = false;
|
||||||
|
let mut expired_session = None;
|
||||||
match token {
|
match token {
|
||||||
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
|
FIRST_HANDSHAKE ... LAST_HANDSHAKE => {
|
||||||
let handshakes = self.handshakes.write().unwrap();
|
let handshakes = self.handshakes.write().unwrap();
|
||||||
@ -732,6 +737,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
FIRST_SESSION ... LAST_SESSION => {
|
FIRST_SESSION ... LAST_SESSION => {
|
||||||
let sessions = self.sessions.write().unwrap();
|
let sessions = self.sessions.write().unwrap();
|
||||||
if let Some(session) = sessions.get(token).cloned() {
|
if let Some(session) = sessions.get(token).cloned() {
|
||||||
|
expired_session = Some(session.clone());
|
||||||
let mut s = session.lock().unwrap();
|
let mut s = session.lock().unwrap();
|
||||||
if !s.expired() {
|
if !s.expired() {
|
||||||
if s.is_ready() {
|
if s.is_ready() {
|
||||||
@ -756,7 +762,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
}
|
}
|
||||||
for p in to_disconnect {
|
for p in to_disconnect {
|
||||||
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
let h = self.handlers.read().unwrap().get(p).unwrap().clone();
|
||||||
h.disconnected(&NetworkContext::new(io, p, Some(token), self.sessions.clone()), &token);
|
h.disconnected(&NetworkContext::new(io, p, expired_session.clone(), self.sessions.clone()), &token);
|
||||||
}
|
}
|
||||||
if deregister {
|
if deregister {
|
||||||
io.deregister_stream(token).expect("Error deregistering stream");
|
io.deregister_stream(token).expect("Error deregistering stream");
|
||||||
|
@ -213,6 +213,9 @@ impl Session {
|
|||||||
|
|
||||||
/// Send a protocol packet to peer.
|
/// Send a protocol packet to peer.
|
||||||
pub fn send_packet(&mut self, protocol: &str, packet_id: u8, data: &[u8]) -> Result<(), UtilError> {
|
pub fn send_packet(&mut self, protocol: &str, packet_id: u8, data: &[u8]) -> Result<(), UtilError> {
|
||||||
|
if self.expired() {
|
||||||
|
return Err(From::from(NetworkError::Expired));
|
||||||
|
}
|
||||||
let mut i = 0usize;
|
let mut i = 0usize;
|
||||||
while protocol != self.info.capabilities[i].protocol {
|
while protocol != self.info.capabilities[i].protocol {
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -351,15 +354,15 @@ impl Session {
|
|||||||
offset += caps[i].packet_count;
|
offset += caps[i].packet_count;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
trace!(target: "net", "Hello: {} v{} {} {:?}", client_version, protocol, id, caps);
|
trace!(target: "network", "Hello: {} v{} {} {:?}", client_version, protocol, id, caps);
|
||||||
self.info.client_version = client_version;
|
self.info.client_version = client_version;
|
||||||
self.info.capabilities = caps;
|
self.info.capabilities = caps;
|
||||||
if self.info.capabilities.is_empty() {
|
if self.info.capabilities.is_empty() {
|
||||||
trace!("No common capabilities with peer.");
|
trace!(target: "network", "No common capabilities with peer.");
|
||||||
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
||||||
}
|
}
|
||||||
if protocol != host.protocol_version {
|
if protocol != host.protocol_version {
|
||||||
trace!("Peer protocol version mismatch: {}", protocol);
|
trace!(target: "network", "Peer protocol version mismatch: {}", protocol);
|
||||||
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
return Err(From::from(self.disconnect(DisconnectReason::UselessPeer)));
|
||||||
}
|
}
|
||||||
self.had_hello = true;
|
self.had_hello = true;
|
||||||
|
@ -146,7 +146,7 @@ impl OverlayDB {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the refs and value of the given key.
|
/// Put the refs and value of the given key, possibly deleting it from the db.
|
||||||
fn put_payload(&self, key: &H256, payload: (Bytes, u32)) -> bool {
|
fn put_payload(&self, key: &H256, payload: (Bytes, u32)) -> bool {
|
||||||
if payload.1 > 0 {
|
if payload.1 > 0 {
|
||||||
let mut s = RlpStream::new_list(2);
|
let mut s = RlpStream::new_list(2);
|
||||||
|
@ -111,7 +111,7 @@ impl<Row, Col, Val> Table<Row, Col, Val>
|
|||||||
///
|
///
|
||||||
/// Returns previous value (if any)
|
/// Returns previous value (if any)
|
||||||
pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option<Val> {
|
pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option<Val> {
|
||||||
self.map.entry(row).or_insert_with(|| HashMap::new()).insert(col, val)
|
self.map.entry(row).or_insert_with(HashMap::new).insert(col, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user