Merge branch 'master' into constant-time-mac-compare
This commit is contained in:
commit
83690fdb90
@ -452,6 +452,7 @@ darwin:
|
||||
export COMMIT=$(git rev-parse HEAD)
|
||||
export PLATFORM=x86_64-apple-darwin
|
||||
rustup default stable
|
||||
cargo clean
|
||||
cargo build -j 8 --features final --release #$CARGOFLAGS
|
||||
cargo build -j 8 --release -p ethstore-cli #$CARGOFLAGS
|
||||
cargo build -j 8 --release -p ethkey-cli #$CARGOFLAGS
|
||||
@ -500,6 +501,7 @@ windows:
|
||||
- set RUST_BACKTRACE=1
|
||||
- set RUSTFLAGS=%RUSTFLAGS%
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo clean
|
||||
- cargo build --features final --release #%CARGOFLAGS%
|
||||
- cargo build --release -p ethstore-cli #%CARGOFLAGS%
|
||||
- cargo build --release -p ethkey-cli #%CARGOFLAGS%
|
||||
|
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -317,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "elastic-array"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -635,7 +635,7 @@ version = "1.7.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.1.3",
|
||||
@ -2130,7 +2130,7 @@ name = "rlp"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-bigint 0.1.3",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2951,7 +2951,7 @@ dependencies = [
|
||||
"checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8"
|
||||
"checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f"
|
||||
"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91"
|
||||
"checksum elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "561b1b1bb58e6d9212b75a28cca442f8a87cceb35cb1b6d6f39f5df5346a9160"
|
||||
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
||||
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
|
||||
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||
"checksum ethabi 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65f71b9ac0b0f8e6230d32dbf5acf7c2c61334af1148175d0a7e72b14c3d475e"
|
||||
|
@ -126,7 +126,7 @@ pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256>
|
||||
let start_num = start_number(cht_num) as usize;
|
||||
|
||||
for (i, (h, td)) in iterable.into_iter().take(SIZE as usize).enumerate() {
|
||||
v.push((key!(i + start_num).to_vec(), val!(h, td).to_vec()))
|
||||
v.push((key!(i + start_num).into_vec(), val!(h, td).into_vec()))
|
||||
}
|
||||
|
||||
if v.len() == SIZE as usize {
|
||||
|
@ -388,7 +388,7 @@ impl HeaderChain {
|
||||
None => {
|
||||
match self.db.get(self.col, &hash) {
|
||||
Ok(db_value) => {
|
||||
db_value.map(|x| x.to_vec()).map(encoded::Header::new)
|
||||
db_value.map(|x| x.into_vec()).map(encoded::Header::new)
|
||||
.and_then(|header| {
|
||||
cache.insert_block_header(hash.clone(), header.clone());
|
||||
Some(header)
|
||||
|
@ -137,7 +137,7 @@ impl Provider for TestProvider {
|
||||
|
||||
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
|
||||
Some(StorageResponse {
|
||||
proof: vec![::rlp::encode(&req.key_hash).to_vec()],
|
||||
proof: vec![::rlp::encode(&req.key_hash).into_vec()],
|
||||
value: req.key_hash | req.address_hash,
|
||||
})
|
||||
}
|
||||
|
@ -738,7 +738,7 @@ impl BlockReceipts {
|
||||
/// Check a response with receipts against the stored header.
|
||||
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt]) -> Result<Vec<Receipt>, Error> {
|
||||
let receipts_root = self.0.as_ref()?.receipts_root();
|
||||
let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).to_vec()));
|
||||
let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).into_vec()));
|
||||
|
||||
match receipts_root == found_root {
|
||||
true => {
|
||||
@ -902,7 +902,7 @@ mod tests {
|
||||
header.set_number(10_000);
|
||||
header.set_extra_data(b"test_header".to_vec());
|
||||
let hash = header.hash();
|
||||
let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec());
|
||||
let raw_header = encoded::Header::new(::rlp::encode(&header).into_vec());
|
||||
|
||||
let cache = Mutex::new(make_cache());
|
||||
assert!(HeaderByHash(hash.into()).check_response(&cache, &hash.into(), &[raw_header]).is_ok())
|
||||
@ -916,10 +916,10 @@ mod tests {
|
||||
let mut body_stream = RlpStream::new_list(2);
|
||||
body_stream.begin_list(0).begin_list(0);
|
||||
|
||||
let req = Body(encoded::Header::new(::rlp::encode(&header).to_vec()).into());
|
||||
let req = Body(encoded::Header::new(::rlp::encode(&header).into_vec()).into());
|
||||
|
||||
let cache = Mutex::new(make_cache());
|
||||
let response = encoded::Body::new(body_stream.drain().to_vec());
|
||||
let response = encoded::Body::new(body_stream.drain().into_vec());
|
||||
assert!(req.check_response(&cache, &response).is_ok())
|
||||
}
|
||||
|
||||
@ -934,12 +934,12 @@ mod tests {
|
||||
|
||||
let mut header = Header::new();
|
||||
let receipts_root = ::util::triehash::ordered_trie_root(
|
||||
receipts.iter().map(|x| ::rlp::encode(x).to_vec())
|
||||
receipts.iter().map(|x| ::rlp::encode(x).into_vec())
|
||||
);
|
||||
|
||||
header.set_receipts_root(receipts_root);
|
||||
|
||||
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec()).into());
|
||||
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).into_vec()).into());
|
||||
|
||||
let cache = Mutex::new(make_cache());
|
||||
assert!(req.check_response(&cache, &receipts).is_ok())
|
||||
@ -987,7 +987,7 @@ mod tests {
|
||||
header.set_state_root(root.clone());
|
||||
|
||||
let req = Account {
|
||||
header: encoded::Header::new(::rlp::encode(&header).to_vec()).into(),
|
||||
header: encoded::Header::new(::rlp::encode(&header).into_vec()).into(),
|
||||
address: addr,
|
||||
};
|
||||
|
||||
@ -1001,7 +1001,7 @@ mod tests {
|
||||
let code_hash = ::util::Hashable::sha3(&code);
|
||||
let header = Header::new();
|
||||
let req = Code {
|
||||
header: encoded::Header::new(::rlp::encode(&header).to_vec()).into(),
|
||||
header: encoded::Header::new(::rlp::encode(&header).into_vec()).into(),
|
||||
code_hash: code_hash.into(),
|
||||
};
|
||||
|
||||
|
@ -1673,7 +1673,7 @@ mod tests {
|
||||
let full_req = Request::Headers(req.clone());
|
||||
let res = HeadersResponse {
|
||||
headers: vec![
|
||||
::ethcore::encoded::Header::new(::rlp::encode(&Header::default()).to_vec())
|
||||
::ethcore::encoded::Header::new(::rlp::encode(&Header::default()).into_vec())
|
||||
]
|
||||
};
|
||||
let full_res = Response::Headers(res.clone());
|
||||
|
@ -28,7 +28,7 @@ const SECRETSTORE_ACL_STORAGE_ABI: &'static str = include_str!("res/secretstore_
|
||||
const VALIDATOR_SET_ABI: &'static str = include_str!("res/validator_set.json");
|
||||
const VALIDATOR_REPORT_ABI: &'static str = include_str!("res/validator_report.json");
|
||||
|
||||
const TEST_VALIDATOR_SET_ABI: &'static str = r#"[{"constant":true,"inputs":[],"name":"transitionNonce","outputs":[{"name":"n","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newValidators","type":"address[]"}],"name":"setValidators","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"vals","type":"address[]"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":true,"name":"_nonce","type":"uint256"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"ValidatorsChanged","type":"event"}]"#;
|
||||
const TEST_VALIDATOR_SET_ABI: &'static str = include_str!("res/test_validator_set.json");
|
||||
|
||||
fn build_file(name: &str, abi: &str, filename: &str) {
|
||||
let code = ::native_contract_generator::generate_module(name, abi).unwrap();
|
||||
|
@ -108,7 +108,7 @@ fn generate_functions(contract: &Contract) -> Result<String, Error> {
|
||||
/// Outputs: {abi_outputs:?}
|
||||
pub fn {snake_name}<F, U>(&self, call: F, {params}) -> BoxFuture<{output_type}, String>
|
||||
where
|
||||
F: Fn(util::Address, Vec<u8>) -> U,
|
||||
F: FnOnce(util::Address, Vec<u8>) -> U,
|
||||
U: IntoFuture<Item=Vec<u8>, Error=String>,
|
||||
U::Future: Send + 'static
|
||||
{{
|
||||
|
8
ethcore/native_contracts/res/test_validator_set.json
Normal file
8
ethcore/native_contracts/res/test_validator_set.json
Normal file
@ -0,0 +1,8 @@
|
||||
[
|
||||
{"constant":false,"inputs":[{"name":"_validators","type":"address[]"}],"name":"setValidators","outputs":[],"payable":false,"type":"function"},
|
||||
{"constant":false,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},
|
||||
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
|
||||
{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"_validators","type":"address[]"}],"payable":false,"type":"function"},
|
||||
{"constant":false,"inputs":[{"name":"","type":"address"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"},
|
||||
{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"}
|
||||
]
|
@ -1,5 +1,5 @@
|
||||
[
|
||||
{"constant":true,"inputs":[],"name":"transitionNonce","outputs":[{"name":"nonce","type":"uint256"}],"payable":false,"type":"function"},
|
||||
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
|
||||
{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"payable":false,"type":"function"},
|
||||
{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":true,"name":"_nonce","type":"uint256"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"ValidatorsChanged","type":"event"}
|
||||
{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"}
|
||||
]
|
||||
|
@ -11,7 +11,8 @@
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
|
||||
]
|
||||
}
|
||||
},
|
||||
"immediateTransitions": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8,7 +8,8 @@
|
||||
"startStep": 2,
|
||||
"validators": {
|
||||
"contract": "0x0000000000000000000000000000000000000005"
|
||||
}
|
||||
},
|
||||
"immediateTransitions": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -260,6 +260,7 @@ impl<'x> OpenBlock<'x> {
|
||||
author: Address,
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
is_epoch_begin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let number = parent.number() + 1;
|
||||
let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?;
|
||||
@ -279,7 +280,8 @@ impl<'x> OpenBlock<'x> {
|
||||
let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit);
|
||||
let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target);
|
||||
engine.populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target);
|
||||
engine.on_new_block(&mut r.block, last_hashes)?;
|
||||
engine.on_new_block(&mut r.block, last_hashes, is_epoch_begin)?;
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@ -380,11 +382,11 @@ impl<'x> OpenBlock<'x> {
|
||||
if let Err(e) = s.block.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
}
|
||||
s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().to_vec())));
|
||||
s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().into_vec())));
|
||||
let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
||||
s.block.header.set_uncles_hash(uncle_bytes.sha3());
|
||||
s.block.header.set_state_root(s.block.state.root().clone());
|
||||
s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
|
||||
s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().into_vec())));
|
||||
s.block.header.set_log_bloom(s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b})); //TODO: use |= operator
|
||||
s.block.header.set_gas_used(s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used));
|
||||
|
||||
@ -408,14 +410,14 @@ impl<'x> OpenBlock<'x> {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
}
|
||||
if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &SHA3_NULL_RLP {
|
||||
s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().to_vec())));
|
||||
s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().into_vec())));
|
||||
}
|
||||
let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
||||
if s.block.header.uncles_hash().is_zero() {
|
||||
s.block.header.set_uncles_hash(uncle_bytes.sha3());
|
||||
}
|
||||
if s.block.header.receipts_root().is_zero() || s.block.header.receipts_root() == &SHA3_NULL_RLP {
|
||||
s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
|
||||
s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes().into_vec())));
|
||||
}
|
||||
|
||||
s.block.header.set_state_root(s.block.state.root().clone());
|
||||
@ -430,7 +432,7 @@ impl<'x> OpenBlock<'x> {
|
||||
|
||||
#[cfg(test)]
|
||||
/// Return mutable block reference. To be used in tests only.
|
||||
pub fn block_mut (&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||
pub fn block_mut(&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||
}
|
||||
|
||||
impl<'x> IsBlock for OpenBlock<'x> {
|
||||
@ -508,7 +510,7 @@ impl LockedBlock {
|
||||
for receipt in &mut block.block.receipts {
|
||||
receipt.state_root = None;
|
||||
}
|
||||
block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
|
||||
block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().into_vec())));
|
||||
block
|
||||
}
|
||||
}
|
||||
@ -554,6 +556,7 @@ pub fn enact(
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
factories: Factories,
|
||||
is_epoch_begin: bool,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
@ -563,7 +566,19 @@ pub fn enact(
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])?;
|
||||
let mut b = OpenBlock::new(
|
||||
engine,
|
||||
factories,
|
||||
tracing,
|
||||
db,
|
||||
parent,
|
||||
last_hashes,
|
||||
Address::new(),
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![],
|
||||
is_epoch_begin,
|
||||
)?;
|
||||
|
||||
b.set_difficulty(*header.difficulty());
|
||||
b.set_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
@ -618,9 +633,22 @@ pub fn enact_verified(
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
factories: Factories,
|
||||
is_epoch_begin: bool,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let view = BlockView::new(&block.bytes);
|
||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||
|
||||
enact(
|
||||
&block.header,
|
||||
&block.transactions,
|
||||
&view.uncles(),
|
||||
engine,
|
||||
tracing,
|
||||
db,
|
||||
parent,
|
||||
last_hashes,
|
||||
factories,
|
||||
is_epoch_begin,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -653,7 +681,7 @@ mod tests {
|
||||
let header = block.header();
|
||||
let transactions: Result<Vec<_>, Error> = block.transactions().into_iter().map(SignedTransaction::new).collect();
|
||||
let transactions = transactions?;
|
||||
enact(&header, &transactions, &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||
enact(&header, &transactions, &block.uncles(), engine, tracing, db, parent, last_hashes, factories, false)
|
||||
}
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||
@ -678,7 +706,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let _ = b.seal(&*spec.engine, vec![]);
|
||||
}
|
||||
@ -692,7 +720,7 @@ mod tests {
|
||||
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap()
|
||||
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
@ -716,7 +744,7 @@ mod tests {
|
||||
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.set_extra_data(b"uncle1".to_vec());
|
||||
let mut uncle2_header = Header::new();
|
||||
|
@ -35,6 +35,7 @@ use blockchain::{CacheSize, ImportRoute, Config};
|
||||
use db::{self, Writable, Readable, CacheUpdatePolicy};
|
||||
use cache_manager::CacheManager;
|
||||
use encoded;
|
||||
use engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition};
|
||||
|
||||
const LOG_BLOOMS_LEVELS: usize = 3;
|
||||
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
|
||||
@ -145,6 +146,10 @@ pub trait BlockProvider {
|
||||
where F: Fn(&LogEntry) -> bool, Self: Sized;
|
||||
}
|
||||
|
||||
macro_rules! otry {
|
||||
($e:expr) => { match $e { Some(x) => x, None => return None } }
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
||||
enum CacheId {
|
||||
BlockHeader(H256),
|
||||
@ -261,7 +266,7 @@ impl BlockProvider for BlockChain {
|
||||
|
||||
let result = match opt {
|
||||
Some(b) => {
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).into_vec();
|
||||
let mut write = self.block_headers.write();
|
||||
write.insert(hash.clone(), bytes.clone());
|
||||
Some(encoded::Header::new(bytes))
|
||||
@ -297,7 +302,7 @@ impl BlockProvider for BlockChain {
|
||||
|
||||
let result = match opt {
|
||||
Some(b) => {
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).into_vec();
|
||||
let mut write = self.block_bodies.write();
|
||||
write.insert(hash.clone(), bytes.clone());
|
||||
Some(encoded::Body::new(bytes))
|
||||
@ -508,7 +513,7 @@ impl BlockChain {
|
||||
number: header.number(),
|
||||
total_difficulty: header.difficulty(),
|
||||
parent: header.parent_hash(),
|
||||
children: vec![]
|
||||
children: vec![],
|
||||
};
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
@ -531,7 +536,7 @@ impl BlockChain {
|
||||
let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner();
|
||||
let best_block_timestamp = BlockView::new(&best_block_rlp).header().timestamp();
|
||||
|
||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
|
||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.into_vec());
|
||||
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||
let best_ancient_number;
|
||||
if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() {
|
||||
@ -698,10 +703,6 @@ impl BlockChain {
|
||||
/// If the tree route verges into pruned or unknown blocks,
|
||||
/// `None` is returned.
|
||||
pub fn tree_route(&self, from: H256, to: H256) -> Option<TreeRoute> {
|
||||
macro_rules! otry {
|
||||
($e:expr) => { match $e { Some(x) => x, None => return None } }
|
||||
}
|
||||
|
||||
let mut from_branch = vec![];
|
||||
let mut to_branch = vec![];
|
||||
|
||||
@ -878,16 +879,60 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a specific epoch transition by epoch number and provided block hash.
|
||||
pub fn epoch_transition(&self, epoch_num: u64, block_hash: H256) -> Option<EpochTransition> {
|
||||
trace!(target: "blockchain", "Loading epoch {} transition at block {}",
|
||||
epoch_num, block_hash);
|
||||
/// Get a specific epoch transition by block number and provided block hash.
|
||||
pub fn epoch_transition(&self, block_num: u64, block_hash: H256) -> Option<EpochTransition> {
|
||||
trace!(target: "blockchain", "Loading epoch transition at block {}, {}",
|
||||
block_num, block_hash);
|
||||
|
||||
self.db.read(db::COL_EXTRA, &epoch_num).and_then(|transitions: EpochTransitions| {
|
||||
self.db.read(db::COL_EXTRA, &block_num).and_then(|transitions: EpochTransitions| {
|
||||
transitions.candidates.into_iter().find(|c| c.block_hash == block_hash)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the transition to the epoch the given parent hash is part of
|
||||
/// or transitions to.
|
||||
/// This will give the epoch that any children of this parent belong to.
|
||||
///
|
||||
/// The block corresponding the the parent hash must be stored already.
|
||||
pub fn epoch_transition_for(&self, parent_hash: H256) -> Option<EpochTransition> {
|
||||
// slow path: loop back block by block
|
||||
for hash in otry!(self.ancestry_iter(parent_hash)) {
|
||||
let details = otry!(self.block_details(&hash));
|
||||
|
||||
// look for transition in database.
|
||||
if let Some(transition) = self.epoch_transition(details.number, hash) {
|
||||
return Some(transition)
|
||||
}
|
||||
|
||||
// canonical hash -> fast breakout:
|
||||
// get the last epoch transition up to this block.
|
||||
//
|
||||
// if `block_hash` is canonical it will only return transitions up to
|
||||
// the parent.
|
||||
if otry!(self.block_hash(details.number)) == hash {
|
||||
return self.epoch_transitions()
|
||||
.map(|(_, t)| t)
|
||||
.take_while(|t| t.block_number <= details.number)
|
||||
.last()
|
||||
}
|
||||
}
|
||||
|
||||
// should never happen as the loop will encounter genesis before concluding.
|
||||
None
|
||||
}
|
||||
|
||||
/// Write a pending epoch transition by block hash.
|
||||
pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: PendingEpochTransition) {
|
||||
batch.write(db::COL_EXTRA, &hash, &t);
|
||||
}
|
||||
|
||||
/// Get a pending epoch transition by block hash.
|
||||
// TODO: implement removal safely: this can only be done upon finality of a block
|
||||
// that _uses_ the pending transition.
|
||||
pub fn get_pending_transition(&self, hash: H256) -> Option<PendingEpochTransition> {
|
||||
self.db.read(db::COL_EXTRA, &hash)
|
||||
}
|
||||
|
||||
/// Add a child to a given block. Assumes that the block hash is in
|
||||
/// the chain and the child's parent is this block.
|
||||
///
|
||||
@ -1165,12 +1210,12 @@ impl BlockChain {
|
||||
let mut parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
|
||||
parent_details.children.push(info.hash.clone());
|
||||
|
||||
// create current block details
|
||||
// create current block details.
|
||||
let details = BlockDetails {
|
||||
number: header.number(),
|
||||
total_difficulty: info.total_difficulty,
|
||||
parent: parent_hash.clone(),
|
||||
children: vec![]
|
||||
children: vec![],
|
||||
};
|
||||
|
||||
// write to batch
|
||||
@ -2201,7 +2246,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn epoch_transitions_iter() {
|
||||
use blockchain::extras::EpochTransition;
|
||||
use ::engines::EpochTransition;
|
||||
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
let mut finalizer = BlockFinalizer::default();
|
||||
@ -2223,7 +2268,6 @@ mod tests {
|
||||
block_hash: hash,
|
||||
block_number: i + 1,
|
||||
proof: vec![],
|
||||
state_proof: vec![],
|
||||
});
|
||||
bc.commit();
|
||||
}
|
||||
@ -2236,7 +2280,6 @@ mod tests {
|
||||
block_hash: hash,
|
||||
block_number: 1,
|
||||
proof: vec![],
|
||||
state_proof: vec![]
|
||||
});
|
||||
|
||||
db.write(batch).unwrap();
|
||||
@ -2252,4 +2295,85 @@ mod tests {
|
||||
assert_eq!(bc.best_block_number(), 5);
|
||||
assert_eq!(bc.epoch_transitions().map(|(i, _)| i).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_transition_for() {
|
||||
use ::engines::EpochTransition;
|
||||
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
let mut finalizer = BlockFinalizer::default();
|
||||
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||
|
||||
let db = new_db();
|
||||
|
||||
let bc = new_chain(&genesis, db.clone());
|
||||
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_epoch_transition(&mut batch, 0, EpochTransition {
|
||||
block_hash: bc.genesis_hash(),
|
||||
block_number: 0,
|
||||
proof: vec![],
|
||||
});
|
||||
db.write(batch).unwrap();
|
||||
|
||||
// set up a chain where we have a canonical chain of 10 blocks
|
||||
// and a non-canonical fork of 8 from genesis.
|
||||
let fork_hash = {
|
||||
let mut fork_chain = canon_chain.fork(1);
|
||||
let mut fork_finalizer = finalizer.fork();
|
||||
|
||||
for _ in 0..7 {
|
||||
let mut batch = db.transaction();
|
||||
let fork_block = fork_chain.generate(&mut fork_finalizer).unwrap();
|
||||
|
||||
bc.insert_block(&mut batch, &fork_block, vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 7);
|
||||
bc.chain_info().best_block_hash
|
||||
};
|
||||
|
||||
for _ in 0..10 {
|
||||
let mut batch = db.transaction();
|
||||
let canon_block = canon_chain.generate(&mut finalizer).unwrap();
|
||||
|
||||
bc.insert_block(&mut batch, &canon_block, vec![]);
|
||||
bc.commit();
|
||||
|
||||
db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(bc.best_block_number(), 10);
|
||||
|
||||
let mut batch = db.transaction();
|
||||
bc.insert_epoch_transition(&mut batch, 4, EpochTransition {
|
||||
block_hash: bc.block_hash(4).unwrap(),
|
||||
block_number: 4,
|
||||
proof: vec![],
|
||||
});
|
||||
db.write(batch).unwrap();
|
||||
|
||||
// blocks where the parent is one of the first 4 will be part of genesis epoch.
|
||||
for i in 0..4 {
|
||||
let hash = bc.block_hash(i).unwrap();
|
||||
assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 0);
|
||||
}
|
||||
|
||||
// blocks where the parent is the transition at 4 or after will be
|
||||
// part of that epoch.
|
||||
for i in 4..11 {
|
||||
let hash = bc.block_hash(i).unwrap();
|
||||
assert_eq!(bc.epoch_transition_for(hash).unwrap().block_number, 4);
|
||||
}
|
||||
|
||||
let fork_hashes = bc.ancestry_iter(fork_hash).unwrap().collect::<Vec<_>>();
|
||||
assert_eq!(fork_hashes.len(), 8);
|
||||
|
||||
// non-canonical fork blocks should all have genesis transition
|
||||
for fork_hash in fork_hashes {
|
||||
assert_eq!(bc.epoch_transition_for(fork_hash).unwrap().block_number, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,16 @@
|
||||
//! Blockchain DB extras.
|
||||
|
||||
use bloomchain;
|
||||
use util::*;
|
||||
use util::kvdb::PREFIX_LEN as DB_PREFIX_LEN;
|
||||
use rlp::*;
|
||||
use blooms::{GroupPosition, BloomGroup};
|
||||
use db::Key;
|
||||
use engines::epoch::{Transition as EpochTransition};
|
||||
use header::BlockNumber;
|
||||
use receipt::Receipt;
|
||||
use db::Key;
|
||||
use blooms::{GroupPosition, BloomGroup};
|
||||
|
||||
use rlp::*;
|
||||
use util::*;
|
||||
use util::kvdb::PREFIX_LEN as DB_PREFIX_LEN;
|
||||
|
||||
|
||||
/// Represents index of extra data in database
|
||||
#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)]
|
||||
@ -40,6 +43,8 @@ pub enum ExtrasIndex {
|
||||
BlockReceipts = 4,
|
||||
/// Epoch transition data index.
|
||||
EpochTransitions = 5,
|
||||
/// Pending epoch transition data index.
|
||||
PendingEpochTransition = 6,
|
||||
}
|
||||
|
||||
fn with_index(hash: &H256, i: ExtrasIndex) -> H264 {
|
||||
@ -137,6 +142,14 @@ impl Key<BlockReceipts> for H256 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Key<::engines::epoch::PendingTransition> for H256 {
|
||||
type Target = H264;
|
||||
|
||||
fn key(&self) -> H264 {
|
||||
with_index(self, ExtrasIndex::PendingEpochTransition)
|
||||
}
|
||||
}
|
||||
|
||||
/// length of epoch keys.
|
||||
pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16;
|
||||
|
||||
@ -296,41 +309,6 @@ impl Decodable for EpochTransitions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EpochTransition {
|
||||
pub block_hash: H256, // block hash at which the transition occurred.
|
||||
pub block_number: BlockNumber, // block number at which the tranition occurred.
|
||||
pub proof: Vec<u8>, // "transition/epoch" proof from the engine.
|
||||
pub state_proof: Vec<DBValue>, // state items necessary to regenerate proof.
|
||||
}
|
||||
|
||||
impl Encodable for EpochTransition {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4)
|
||||
.append(&self.block_hash)
|
||||
.append(&self.block_number)
|
||||
.append(&self.proof)
|
||||
.begin_list(self.state_proof.len());
|
||||
|
||||
for item in &self.state_proof {
|
||||
s.append(&&**item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for EpochTransition {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
Ok(EpochTransition {
|
||||
block_hash: rlp.val_at(0)?,
|
||||
block_number: rlp.val_at(1)?,
|
||||
proof: rlp.val_at(2)?,
|
||||
state_proof: rlp.at(3)?.iter().map(|x| {
|
||||
Ok(DBValue::from_slice(x.data()?))
|
||||
}).collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rlp::*;
|
||||
|
@ -67,6 +67,6 @@ impl WithTransaction for Block {
|
||||
impl CompleteBlock for Block {
|
||||
fn complete(mut self, parent_hash: H256) -> Bytes {
|
||||
self.header.set_parent_hash(parent_hash);
|
||||
encode(&self).to_vec()
|
||||
encode(&self).into_vec()
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,5 @@ pub mod generator;
|
||||
pub use self::blockchain::{BlockProvider, BlockChain};
|
||||
pub use self::cache::CacheSize;
|
||||
pub use self::config::Config;
|
||||
pub use self::extras::EpochTransition;
|
||||
pub use types::tree_route::TreeRoute;
|
||||
pub use self::import_route::ImportRoute;
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use engines::{Engine, EpochVerifier, EpochChange};
|
||||
use error::Error;
|
||||
use blockchain::BlockChain;
|
||||
use engines::{Engine, EpochVerifier};
|
||||
use header::Header;
|
||||
|
||||
use rand::Rng;
|
||||
@ -46,21 +46,21 @@ impl AncientVerifier {
|
||||
|
||||
/// Verify the next block header, randomly choosing whether to do heavy or light
|
||||
/// verification. If the block is the end of an epoch, updates the epoch verifier.
|
||||
pub fn verify<R: Rng, F: Fn(u64) -> Result<Box<EpochVerifier>, Error>>(
|
||||
pub fn verify<R: Rng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
header: &Header,
|
||||
block: &[u8],
|
||||
receipts: &[::receipt::Receipt],
|
||||
load_verifier: F,
|
||||
chain: &BlockChain,
|
||||
) -> Result<(), ::error::Error> {
|
||||
match rng.gen::<f32>() <= HEAVY_VERIFY_RATE {
|
||||
true => self.cur_verifier.read().verify_heavy(header)?,
|
||||
false => self.cur_verifier.read().verify_light(header)?,
|
||||
}
|
||||
|
||||
if let EpochChange::Yes(num) = self.engine.is_epoch_end(header, Some(block), Some(receipts)) {
|
||||
*self.cur_verifier.write() = load_verifier(num)?;
|
||||
// ancient import will only use transitions obtained from the snapshot.
|
||||
if let Some(transition) = chain.epoch_transition(header.number(), header.hash()) {
|
||||
let v = self.engine.epoch_verifier(&header, &transition.proof).known_confirmed()?;
|
||||
*self.cur_verifier.write() = v;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -32,7 +32,7 @@ use util::kvdb::*;
|
||||
// other
|
||||
use basic_types::Seal;
|
||||
use block::*;
|
||||
use blockchain::{BlockChain, BlockProvider, EpochTransition, TreeRoute, ImportRoute};
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use client::ancient_import::AncientVerifier;
|
||||
use client::Error as ClientError;
|
||||
@ -42,7 +42,7 @@ use client::{
|
||||
ChainNotify, PruningInfo, ProvingBlockChainClient,
|
||||
};
|
||||
use encoded;
|
||||
use engines::Engine;
|
||||
use engines::{Engine, EpochTransition};
|
||||
use env_info::EnvInfo;
|
||||
use env_info::LastHashes;
|
||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
||||
@ -256,9 +256,31 @@ impl Client {
|
||||
{
|
||||
let chain = client.chain.read();
|
||||
let gh = spec.genesis_header();
|
||||
if chain.epoch_transition(0, spec.genesis_header().hash()).is_none() {
|
||||
if chain.epoch_transition(0, gh.hash()).is_none() {
|
||||
trace!(target: "client", "No genesis transition found.");
|
||||
client.generate_epoch_proof(&gh, 0, &*chain);
|
||||
|
||||
let proof = client.with_proving_caller(
|
||||
BlockId::Number(0),
|
||||
|call| client.engine.genesis_epoch_data(&gh, call)
|
||||
);
|
||||
let proof = match proof {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => {
|
||||
warn!(target: "client", "Error generating genesis epoch data: {}. Snapshots generated may not be complete.", e);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
debug!(target: "client", "Obtained genesis transition proof: {:?}", proof);
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
chain.insert_epoch_transition(&mut batch, 0, EpochTransition {
|
||||
block_hash: gh.hash(),
|
||||
block_number: 0,
|
||||
proof: proof,
|
||||
});
|
||||
|
||||
client.db.read().write_buffered(batch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,7 +427,16 @@ impl Client {
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
|
||||
let enact_result = enact_verified(block,
|
||||
engine,
|
||||
self.tracedb.read().tracing_enabled(),
|
||||
db,
|
||||
&parent,
|
||||
last_hashes,
|
||||
self.factories.clone(),
|
||||
is_epoch_begin,
|
||||
);
|
||||
let mut locked_block = enact_result.map_err(|e| {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
})?;
|
||||
@ -555,18 +586,9 @@ impl Client {
|
||||
{
|
||||
// closure for verifying a block.
|
||||
let verify_with = |verifier: &AncientVerifier| -> Result<(), ::error::Error> {
|
||||
// verify the block, passing a closure used to load an epoch verifier
|
||||
// by number.
|
||||
verifier.verify(
|
||||
&mut *self.rng.lock(),
|
||||
&header,
|
||||
&block_bytes,
|
||||
&receipts,
|
||||
|epoch_num| chain.epoch_transition(epoch_num, hash)
|
||||
.ok_or(BlockError::UnknownEpochTransition(epoch_num))
|
||||
.map_err(Into::into)
|
||||
.and_then(|t| self.engine.epoch_verifier(&header, &t.proof))
|
||||
)
|
||||
// verify the block, passing the chain for updating the epoch
|
||||
// verifier.
|
||||
verifier.verify(&mut *self.rng.lock(), &header, &chain)
|
||||
};
|
||||
|
||||
// initialize the ancient block verifier if we don't have one already.
|
||||
@ -583,7 +605,8 @@ impl Client {
|
||||
.map(|(_, t)| t.proof)
|
||||
.expect("At least one epoch entry (genesis) always stored; qed");
|
||||
|
||||
let current_verifier = self.engine.epoch_verifier(&header, ¤t_epoch_data)?;
|
||||
let current_verifier = self.engine.epoch_verifier(&header, ¤t_epoch_data)
|
||||
.known_confirmed()?;
|
||||
let current_verifier = AncientVerifier::new(self.engine.clone(), current_verifier);
|
||||
|
||||
verify_with(¤t_verifier)?;
|
||||
@ -606,6 +629,7 @@ impl Client {
|
||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
||||
let number = block.header().number();
|
||||
let parent = block.header().parent_hash().clone();
|
||||
let header = block.header().clone(); // TODO: optimize and avoid copy.
|
||||
let chain = self.chain.read();
|
||||
|
||||
// Commit results
|
||||
@ -619,28 +643,13 @@ impl Client {
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
|
||||
// generate validation proof if the engine requires them.
|
||||
// TODO: make conditional?
|
||||
let entering_new_epoch = {
|
||||
use engines::EpochChange;
|
||||
match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) {
|
||||
EpochChange::Yes(e) => Some((block.header().clone(), e)),
|
||||
EpochChange::No => None,
|
||||
EpochChange::Unsure(_) => {
|
||||
warn!(target: "client", "Detected invalid engine implementation.");
|
||||
warn!(target: "client", "Engine claims to require more block data, but everything provided.");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
// TODO: Prove it with a test.
|
||||
let mut state = block.drain();
|
||||
|
||||
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
|
||||
let route = chain.insert_block(&mut batch, block_data, receipts);
|
||||
let route = chain.insert_block(&mut batch, block_data, receipts.clone());
|
||||
|
||||
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
||||
traces: traces.into(),
|
||||
@ -655,65 +664,102 @@ impl Client {
|
||||
// Final commit to the DB
|
||||
self.db.read().write_buffered(batch);
|
||||
chain.commit();
|
||||
|
||||
// check for epoch end. do this after writing first batch so we can prove
|
||||
// transactions on the block's state.
|
||||
// TODO: work these changes into the existing DBTransaction.
|
||||
self.check_epoch_end_signal(&header, block_data, &receipts, &chain);
|
||||
self.check_epoch_end(&header, &chain);
|
||||
|
||||
self.update_last_hashes(&parent, hash);
|
||||
|
||||
if let Err(e) = self.prune_ancient(state, &chain) {
|
||||
warn!("Failed to prune ancient state data: {}", e);
|
||||
}
|
||||
|
||||
if let Some((header, epoch)) = entering_new_epoch {
|
||||
self.generate_epoch_proof(&header, epoch, &chain);
|
||||
}
|
||||
|
||||
route
|
||||
}
|
||||
|
||||
// generate an epoch transition proof at the given block, and write it into the given blockchain.
|
||||
fn generate_epoch_proof(&self, header: &Header, epoch_number: u64, chain: &BlockChain) {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
// check for epoch end signal and write pending transition if it occurs.
|
||||
// state for the given block must be available.
|
||||
fn check_epoch_end_signal(&self, header: &Header, block: &[u8], receipts: &[Receipt], chain: &BlockChain) {
|
||||
use engines::EpochChange;
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
let hash = header.hash();
|
||||
debug!(target: "client", "Generating validation proof for epoch {} at block {}",
|
||||
epoch_number, hash);
|
||||
match self.engine.signals_epoch_end(header, Some(block), Some(&receipts)) {
|
||||
EpochChange::Yes(proof) => {
|
||||
use engines::epoch::PendingTransition;
|
||||
use engines::Proof;
|
||||
|
||||
// proof is two-part. state items read in lexicographical order,
|
||||
// and the secondary "proof" part.
|
||||
let read_values = RefCell::new(BTreeSet::new());
|
||||
let block_id = BlockId::Hash(hash.clone());
|
||||
let proof = {
|
||||
let call = |a, d| {
|
||||
let tx = self.contract_call_tx(block_id, a, d);
|
||||
let (result, items) = self.prove_transaction(tx, block_id)
|
||||
.ok_or_else(|| format!("Unable to make call to generate epoch proof."))?;
|
||||
let proof = match proof {
|
||||
Proof::Known(proof) => proof,
|
||||
Proof::WithState(with_state) =>
|
||||
match self.with_proving_caller(BlockId::Hash(hash), move |c| with_state(c)) {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => {
|
||||
warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e);
|
||||
warn!(target: "client", "Snapshots produced by this client may be incomplete");
|
||||
|
||||
read_values.borrow_mut().extend(items);
|
||||
Ok(result)
|
||||
Vec::new()
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.engine.epoch_proof(&header, &call)
|
||||
};
|
||||
debug!(target: "client", "Block {} signals epoch end.", hash);
|
||||
|
||||
// insert into database, using the generated proof.
|
||||
match proof {
|
||||
Ok(proof) => {
|
||||
chain.insert_epoch_transition(&mut batch, epoch_number, EpochTransition {
|
||||
block_hash: hash.clone(),
|
||||
block_number: header.number(),
|
||||
proof: proof,
|
||||
state_proof: read_values.into_inner().into_iter().collect(),
|
||||
});
|
||||
// write pending transition to DB.
|
||||
let mut batch = DBTransaction::new();
|
||||
|
||||
let pending = PendingTransition { proof: proof };
|
||||
chain.insert_pending_transition(&mut batch, hash, pending);
|
||||
|
||||
self.db.read().write_buffered(batch);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e);
|
||||
warn!(target: "client", "Snapshots generated by this node will be incomplete.");
|
||||
},
|
||||
EpochChange::No => {},
|
||||
EpochChange::Unsure(_) => {
|
||||
warn!(target: "client", "Detected invalid engine implementation.");
|
||||
warn!(target: "client", "Engine claims to require more block data, but everything provided.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for ending of epoch and write transition if it occurs.
|
||||
fn check_epoch_end<'a>(&self, header: &'a Header, chain: &BlockChain) {
|
||||
let is_epoch_end = self.engine.is_epoch_end(
|
||||
header,
|
||||
&(|hash| chain.block_header(&hash)),
|
||||
&(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch.
|
||||
);
|
||||
|
||||
if let Some(proof) = is_epoch_end {
|
||||
debug!(target: "client", "Epoch transition at block {}", header.hash());
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
chain.insert_epoch_transition(&mut batch, header.number(), EpochTransition {
|
||||
block_hash: header.hash(),
|
||||
block_number: header.number(),
|
||||
proof: proof,
|
||||
});
|
||||
self.db.read().write_buffered(batch);
|
||||
}
|
||||
}
|
||||
|
||||
// use a state-proving closure for the given block.
|
||||
fn with_proving_caller<F, T>(&self, id: BlockId, with_call: F) -> T
|
||||
where F: FnOnce(&::engines::Call) -> T
|
||||
{
|
||||
let call = |a, d| {
|
||||
let tx = self.contract_call_tx(id, a, d);
|
||||
let (result, items) = self.prove_transaction(tx, id)
|
||||
.ok_or_else(|| format!("Unable to make call. State unavailable?"))?;
|
||||
|
||||
let items = items.into_iter().map(|x| x.to_vec()).collect();
|
||||
Ok((result, items))
|
||||
};
|
||||
|
||||
with_call(&call)
|
||||
}
|
||||
|
||||
// prune ancient states until below the memory limit or only the minimum amount remain.
|
||||
fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ClientError> {
|
||||
let number = match state_db.journal_db().latest_era() {
|
||||
@ -1397,7 +1443,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).to_vec())
|
||||
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).into_vec())
|
||||
}
|
||||
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
@ -1622,17 +1668,21 @@ impl MiningBlockChainClient for Client {
|
||||
let engine = &*self.engine;
|
||||
let chain = self.chain.read();
|
||||
let h = chain.best_block_hash();
|
||||
let best_header = &chain.block_header(&h)
|
||||
.expect("h is best block hash: so its header must exist: qed");
|
||||
|
||||
let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some();
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine,
|
||||
self.factories.clone(),
|
||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||
self.state_db.lock().boxed_clone_canon(&h),
|
||||
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
||||
best_header,
|
||||
self.build_last_hashes(h.clone()),
|
||||
author,
|
||||
gas_range_target,
|
||||
extra_data,
|
||||
is_epoch_begin,
|
||||
).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed");
|
||||
|
||||
// Add uncles
|
||||
@ -1717,6 +1767,10 @@ impl EngineClient for Client {
|
||||
fn broadcast_consensus_message(&self, message: Bytes) {
|
||||
self.notify(|notify| notify.broadcast(message.clone()));
|
||||
}
|
||||
|
||||
fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> {
|
||||
self.chain.read().epoch_transition_for(parent_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvingBlockChainClient for Client {
|
||||
@ -1746,7 +1800,10 @@ impl ProvingBlockChainClient for Client {
|
||||
|
||||
match res {
|
||||
Err(ExecutionError::Internal(_)) => None,
|
||||
Err(_) => Some((Vec::new(), state.drop().1.extract_proof())),
|
||||
Err(e) => {
|
||||
trace!(target: "client", "Proved call failed: {}", e);
|
||||
Some((Vec::new(), state.drop().1.extract_proof()))
|
||||
}
|
||||
Ok(res) => Some((res.output, state.drop().1.extract_proof())),
|
||||
}
|
||||
}
|
||||
|
@ -372,7 +372,8 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
Arc::new(last_hashes),
|
||||
author,
|
||||
gas_range_target,
|
||||
extra_data
|
||||
extra_data,
|
||||
false,
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
||||
open_block.set_timestamp(*self.latest_block_timestamp.read());
|
||||
@ -786,4 +787,8 @@ impl EngineClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn broadcast_consensus_message(&self, _message: Bytes) {}
|
||||
|
||||
fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +311,13 @@ pub trait EngineClient: MiningBlockChainClient {
|
||||
|
||||
/// Broadcast a consensus message to the network.
|
||||
fn broadcast_consensus_message(&self, message: Bytes);
|
||||
|
||||
/// Get the transition to the epoch the given parent hash is part of
|
||||
/// or transitions to.
|
||||
/// This will give the epoch that any children of this parent belong to.
|
||||
///
|
||||
/// The block corresponding the the parent hash must be stored already.
|
||||
fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>;
|
||||
}
|
||||
|
||||
/// Extended client interface for providing proofs of the state.
|
||||
|
187
ethcore/src/engines/authority_round/finality.rs
Normal file
187
ethcore/src/engines/authority_round/finality.rs
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
//! Finality proof generation and checking.
|
||||
|
||||
use std::collections::{VecDeque};
|
||||
use std::collections::hash_map::{HashMap, Entry};
|
||||
|
||||
use util::{Address, H256};
|
||||
|
||||
use engines::validator_set::SimpleList;
|
||||
|
||||
/// Error indicating unknown validator.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct UnknownValidator;
|
||||
|
||||
/// Rolling finality checker for authority round consensus.
|
||||
/// Stores a chain of unfinalized hashes that can be pushed onto.
|
||||
pub struct RollingFinality {
|
||||
headers: VecDeque<(H256, Address)>,
|
||||
signers: SimpleList,
|
||||
sign_count: HashMap<Address, usize>,
|
||||
last_pushed: Option<H256>,
|
||||
}
|
||||
|
||||
impl RollingFinality {
|
||||
/// Create a blank finality checker under the given validator set.
|
||||
pub fn blank(signers: Vec<Address>) -> Self {
|
||||
RollingFinality {
|
||||
headers: VecDeque::new(),
|
||||
signers: SimpleList::new(signers),
|
||||
sign_count: HashMap::new(),
|
||||
last_pushed: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract unfinalized subchain from ancestry iterator.
|
||||
/// Clears the current subchain.
|
||||
///
|
||||
/// Fails if any provided signature isn't part of the signers set.
|
||||
pub fn build_ancestry_subchain<I>(&mut self, iterable: I) -> Result<(), UnknownValidator>
|
||||
where I: IntoIterator<Item=(H256, Address)>
|
||||
{
|
||||
self.clear();
|
||||
for (hash, signer) in iterable {
|
||||
if !self.signers.contains(&signer) { return Err(UnknownValidator) }
|
||||
if self.last_pushed.is_none() { self.last_pushed = Some(hash) }
|
||||
|
||||
// break when we've got our first finalized block.
|
||||
{
|
||||
let current_signed = self.sign_count.len();
|
||||
let would_be_finalized = (current_signed + 1) * 2 > self.signers.len();
|
||||
|
||||
let entry = self.sign_count.entry(signer);
|
||||
if let (true, &Entry::Vacant(_)) = (would_be_finalized, &entry) {
|
||||
break
|
||||
}
|
||||
|
||||
*entry.or_insert(0) += 1;
|
||||
}
|
||||
|
||||
self.headers.push_front((hash, signer));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear the finality status, but keeps the validator set.
|
||||
pub fn clear(&mut self) {
|
||||
self.headers.clear();
|
||||
self.sign_count.clear();
|
||||
self.last_pushed = None;
|
||||
}
|
||||
|
||||
/// Returns the last pushed hash.
|
||||
pub fn subchain_head(&self) -> Option<H256> {
|
||||
self.last_pushed
|
||||
}
|
||||
|
||||
/// Get an iterator over stored hashes in order.
|
||||
pub fn unfinalized_hashes(&self) -> Iter { Iter(self.headers.iter()) }
|
||||
|
||||
/// Get the validator set.
|
||||
pub fn validators(&self) -> &SimpleList { &self.signers }
|
||||
|
||||
/// Push a hash onto the rolling finality checker (implying `subchain_head` == head.parent)
|
||||
///
|
||||
/// Fails if `signer` isn't a member of the active validator set.
|
||||
/// Returns a list of all newly finalized headers.
|
||||
// TODO: optimize with smallvec.
|
||||
pub fn push_hash(&mut self, head: H256, signer: Address) -> Result<Vec<H256>, UnknownValidator> {
|
||||
if !self.signers.contains(&signer) { return Err(UnknownValidator) }
|
||||
|
||||
self.headers.push_back((head, signer));
|
||||
*self.sign_count.entry(signer).or_insert(0) += 1;
|
||||
|
||||
let mut newly_finalized = Vec::new();
|
||||
|
||||
while self.sign_count.len() * 2 > self.signers.len() {
|
||||
let (hash, signer) = self.headers.pop_front()
|
||||
.expect("headers length always greater than sign count length; qed");
|
||||
|
||||
newly_finalized.push(hash);
|
||||
|
||||
match self.sign_count.entry(signer) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
// decrement count for this signer and purge on zero.
|
||||
*entry.get_mut() -= 1;
|
||||
|
||||
if *entry.get() == 0 {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
Entry::Vacant(_) => panic!("all hashes in `header` should have an entry in `sign_count` for their signer; qed"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(newly_finalized)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a>(::std::collections::vec_deque::Iter<'a, (H256, Address)>);
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = H256;
|
||||
|
||||
fn next(&mut self) -> Option<H256> {
|
||||
self.0.next().map(|&(h, _)| h)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::{Address, H256};
|
||||
use super::RollingFinality;
|
||||
|
||||
#[test]
|
||||
fn rejects_unknown_signer() {
|
||||
let signers = (0..3).map(|_| Address::random()).collect();
|
||||
let mut finality = RollingFinality::blank(signers);
|
||||
assert!(finality.push_hash(H256::random(), Address::random()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finalize_multiple() {
|
||||
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
||||
|
||||
let mut finality = RollingFinality::blank(signers.clone());
|
||||
let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect();
|
||||
|
||||
// 3 / 6 signers is < 51% so no finality.
|
||||
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
|
||||
let i = i % 3;
|
||||
assert!(finality.push_hash(hash, signers[i]).unwrap().len() == 0);
|
||||
}
|
||||
|
||||
// after pushing a block signed by a fourth validator, the first four
|
||||
// blocks of the unverified chain become verified.
|
||||
assert_eq!(finality.push_hash(hashes[6], signers[4]).unwrap(),
|
||||
vec![hashes[0], hashes[1], hashes[2], hashes[3]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_ancestry() {
|
||||
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
||||
let hashes: Vec<_> = (0..12).map(|i| (H256::random(), signers[i % 6])).collect();
|
||||
|
||||
let mut finality = RollingFinality::blank(signers.clone());
|
||||
finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap();
|
||||
|
||||
assert_eq!(finality.unfinalized_hashes().count(), 3);
|
||||
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
|
||||
}
|
||||
}
|
@ -19,24 +19,32 @@
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::Weak;
|
||||
use std::time::{UNIX_EPOCH, Duration};
|
||||
use util::*;
|
||||
use ethkey::{verify_address, Signature};
|
||||
use rlp::{UntrustedRlp, encode};
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use spec::CommonParams;
|
||||
use engines::{Call, Engine, Seal, EngineError};
|
||||
use header::Header;
|
||||
use builtin::Builtin;
|
||||
use client::{Client, EngineClient};
|
||||
use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use error::{Error, TransactionError, BlockError};
|
||||
use ethjson;
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||
use builtin::Builtin;
|
||||
use transaction::UnverifiedTransaction;
|
||||
use client::{Client, EngineClient};
|
||||
use header::{Header, BlockNumber};
|
||||
use spec::CommonParams;
|
||||
use state::CleanupMode;
|
||||
use transaction::UnverifiedTransaction;
|
||||
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
|
||||
use self::finality::RollingFinality;
|
||||
|
||||
use ethkey::{verify_address, Signature};
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||
use itertools::{self, Itertools};
|
||||
use rlp::{UntrustedRlp, encode};
|
||||
use util::*;
|
||||
|
||||
mod finality;
|
||||
|
||||
/// `AuthorityRound` params.
|
||||
pub struct AuthorityRoundParams {
|
||||
/// Gas limit divisor.
|
||||
@ -57,6 +65,8 @@ pub struct AuthorityRoundParams {
|
||||
pub eip155_transition: u64,
|
||||
/// Monotonic step validation transition block.
|
||||
pub validate_step_transition: u64,
|
||||
/// Immediate transitions.
|
||||
pub immediate_transitions: bool,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
@ -71,6 +81,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
|
||||
immediate_transitions: p.immediate_transitions.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,6 +125,92 @@ impl Step {
|
||||
}
|
||||
}
|
||||
|
||||
struct EpochManager {
|
||||
epoch_transition_hash: H256,
|
||||
epoch_transition_number: BlockNumber,
|
||||
finality_checker: RollingFinality,
|
||||
force: bool,
|
||||
}
|
||||
|
||||
impl EpochManager {
|
||||
fn blank() -> Self {
|
||||
EpochManager {
|
||||
epoch_transition_hash: H256::default(),
|
||||
epoch_transition_number: 0,
|
||||
finality_checker: RollingFinality::blank(Vec::new()),
|
||||
force: true,
|
||||
}
|
||||
}
|
||||
|
||||
// zoom to epoch for given header. returns true if succeeded, false otherwise.
|
||||
fn zoom_to(&mut self, client: &EngineClient, engine: &Engine, validators: &ValidatorSet, header: &Header) -> bool {
|
||||
let last_was_parent = self.finality_checker.subchain_head() == Some(header.parent_hash().clone());
|
||||
|
||||
// early exit for current target == chain head, but only if the epochs are
|
||||
// the same.
|
||||
if last_was_parent && !self.force {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.force = false;
|
||||
debug!(target: "engine", "Zooming to epoch for block {}", header.hash());
|
||||
|
||||
// epoch_transition_for can be an expensive call, but in the absence of
|
||||
// forks it will only need to be called for the block directly after
|
||||
// epoch transition, in which case it will be O(1) and require a single
|
||||
// DB lookup.
|
||||
let last_transition = match client.epoch_transition_for(*header.parent_hash()) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
// this really should never happen unless the block passed
|
||||
// hasn't got a parent in the database.
|
||||
debug!(target: "engine", "No genesis transition found.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// extract other epoch set if it's not the same as the last.
|
||||
if last_transition.block_hash != self.epoch_transition_hash {
|
||||
let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof)
|
||||
.expect("proof produced by this engine; therefore it is valid; qed");
|
||||
|
||||
trace!(target: "engine", "extracting epoch set for epoch ({}, {}) signalled at #{}",
|
||||
last_transition.block_number, last_transition.block_hash, signal_number);
|
||||
|
||||
let first = signal_number == 0;
|
||||
let epoch_set = validators.epoch_set(
|
||||
first,
|
||||
engine,
|
||||
signal_number, // use signal number so multi-set first calculation is correct.
|
||||
set_proof,
|
||||
)
|
||||
.ok()
|
||||
.map(|(list, _)| list.into_inner())
|
||||
.expect("proof produced by this engine; therefore it is valid; qed");
|
||||
|
||||
self.finality_checker = RollingFinality::blank(epoch_set);
|
||||
}
|
||||
|
||||
self.epoch_transition_hash = last_transition.block_hash;
|
||||
self.epoch_transition_number = last_transition.block_number;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// note new epoch hash. this will force the next block to re-load
|
||||
// the epoch set
|
||||
// TODO: optimize and don't require re-loading after epoch change.
|
||||
fn note_new_epoch(&mut self) {
|
||||
self.force = true;
|
||||
}
|
||||
|
||||
/// Get validator set. Zoom to the correct epoch first.
|
||||
fn validators(&self) -> &SimpleList {
|
||||
self.finality_checker.validators()
|
||||
}
|
||||
}
|
||||
|
||||
/// Engine using `AuthorityRound` proof-of-authority BFT consensus.
|
||||
pub struct AuthorityRound {
|
||||
params: CommonParams,
|
||||
@ -130,22 +227,63 @@ pub struct AuthorityRound {
|
||||
validate_score_transition: u64,
|
||||
eip155_transition: u64,
|
||||
validate_step_transition: u64,
|
||||
epoch_manager: Mutex<EpochManager>,
|
||||
immediate_transitions: bool,
|
||||
}
|
||||
|
||||
// header-chain validator.
|
||||
struct EpochVerifier {
|
||||
epoch_number: u64,
|
||||
step: Arc<Step>,
|
||||
subchain_validators: SimpleList,
|
||||
}
|
||||
|
||||
impl super::EpochVerifier for EpochVerifier {
|
||||
fn epoch_number(&self) -> u64 { self.epoch_number.clone() }
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error> {
|
||||
// always check the seal since it's fast.
|
||||
// nothing heavier to do.
|
||||
verify_external(header, &self.subchain_validators, &*self.step)
|
||||
verify_external(header, &self.subchain_validators, &*self.step, |_| {})
|
||||
}
|
||||
|
||||
fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
|
||||
macro_rules! otry {
|
||||
($e: expr) => {
|
||||
match $e {
|
||||
Some(x) => x,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut finality_checker = RollingFinality::blank(self.subchain_validators.clone().into_inner());
|
||||
let mut finalized = Vec::new();
|
||||
|
||||
let headers: Vec<Header> = otry!(UntrustedRlp::new(proof).as_list().ok());
|
||||
|
||||
|
||||
for header in &headers {
|
||||
// ensure all headers have correct number of seal fields so we can `verify_external`
|
||||
// without panic.
|
||||
//
|
||||
// `verify_external` checks that signature is correct and author == signer.
|
||||
if header.seal().len() != 2 { return None }
|
||||
otry!(verify_external(header, &self.subchain_validators, &*self.step, |_| {}).ok());
|
||||
|
||||
let newly_finalized = otry!(finality_checker.push_hash(header.hash(), header.author().clone()).ok());
|
||||
finalized.extend(newly_finalized);
|
||||
}
|
||||
|
||||
if finalized.is_empty() { None } else { Some(finalized) }
|
||||
}
|
||||
}
|
||||
|
||||
// Report misbehavior
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum Report {
|
||||
// Malicious behavior
|
||||
Malicious(Address, BlockNumber, Bytes),
|
||||
// benign misbehavior
|
||||
Benign(Address, BlockNumber),
|
||||
}
|
||||
|
||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||
@ -156,13 +294,25 @@ fn header_signature(header: &Header) -> Result<Signature, ::rlp::DecoderError> {
|
||||
UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::<H520>().map(Into::into)
|
||||
}
|
||||
|
||||
fn verify_external(header: &Header, validators: &ValidatorSet, step: &Step) -> Result<(), Error> {
|
||||
fn step_proposer(validators: &ValidatorSet, bh: &H256, step: usize) -> Address {
|
||||
let proposer = validators.get(bh, step);
|
||||
trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer);
|
||||
proposer
|
||||
}
|
||||
|
||||
fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: usize, address: &Address) -> bool {
|
||||
step_proposer(validators, bh, step) == *address
|
||||
}
|
||||
|
||||
fn verify_external<F: Fn(Report)>(header: &Header, validators: &ValidatorSet, step: &Step, report: F)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
let header_step = header_step(header)?;
|
||||
|
||||
// Give one step slack if step is lagging, double vote is still not possible.
|
||||
if step.is_future(header_step) {
|
||||
trace!(target: "engine", "verify_block_unordered: block from the future");
|
||||
validators.report_benign(header.author(), header.number());
|
||||
report(Report::Benign(*header.author(), header.number()));
|
||||
Err(BlockError::InvalidSeal)?
|
||||
} else {
|
||||
let proposer_signature = header_signature(header)?;
|
||||
@ -179,6 +329,21 @@ fn verify_external(header: &Header, validators: &ValidatorSet, step: &Step) -> R
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: &[u8]) -> Vec<u8> {
|
||||
let mut stream = ::rlp::RlpStream::new_list(3);
|
||||
stream.append(&signal_number).append(&set_proof).append(&finality_proof);
|
||||
stream.out()
|
||||
}
|
||||
|
||||
fn destructure_proofs(combined: &[u8]) -> Result<(BlockNumber, &[u8], &[u8]), Error> {
|
||||
let rlp = UntrustedRlp::new(combined);
|
||||
Ok((
|
||||
rlp.at(0)?.as_val()?,
|
||||
rlp.at(1)?.data()?,
|
||||
rlp.at(2)?.data()?,
|
||||
))
|
||||
}
|
||||
|
||||
trait AsMillis {
|
||||
fn as_millis(&self) -> u64;
|
||||
}
|
||||
@ -214,7 +379,10 @@ impl AuthorityRound {
|
||||
validate_score_transition: our_params.validate_score_transition,
|
||||
eip155_transition: our_params.eip155_transition,
|
||||
validate_step_transition: our_params.validate_step_transition,
|
||||
epoch_manager: Mutex::new(EpochManager::blank()),
|
||||
immediate_transitions: our_params.immediate_transitions,
|
||||
});
|
||||
|
||||
// Do not initialize timeouts for tests.
|
||||
if should_timeout {
|
||||
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
|
||||
@ -222,14 +390,6 @@ impl AuthorityRound {
|
||||
}
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
fn step_proposer(&self, bh: &H256, step: usize) -> Address {
|
||||
self.validators.get(bh, step)
|
||||
}
|
||||
|
||||
fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool {
|
||||
self.step_proposer(bh, step) == *address
|
||||
}
|
||||
}
|
||||
|
||||
fn unix_now() -> Duration {
|
||||
@ -325,23 +485,81 @@ impl Engine for AuthorityRound {
|
||||
|
||||
let header = block.header();
|
||||
let step = self.step.load();
|
||||
if self.is_step_proposer(header.parent_hash(), step, header.author()) {
|
||||
|
||||
// fetch correct validator set for current epoch, taking into account
|
||||
// finality of previous transitions.
|
||||
let active_set;
|
||||
|
||||
let validators = if self.immediate_transitions {
|
||||
&*self.validators
|
||||
} else {
|
||||
let mut epoch_manager = self.epoch_manager.lock();
|
||||
let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
|
||||
Some(client) => client,
|
||||
None => {
|
||||
warn!(target: "engine", "Unable to generate seal: missing client ref.");
|
||||
return Seal::None;
|
||||
}
|
||||
};
|
||||
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) {
|
||||
debug!(target: "engine", "Unable to zoom to epoch.");
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
active_set = epoch_manager.validators().clone();
|
||||
&active_set as &_
|
||||
};
|
||||
|
||||
if is_step_proposer(validators, header.parent_hash(), step, header.author()) {
|
||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
|
||||
|
||||
// only issue the seal if we were the first to reach the compare_and_swap.
|
||||
if !self.proposed.compare_and_swap(false, true, AtomicOrdering::SeqCst) {
|
||||
return Seal::Regular(vec![encode(&step).to_vec(), encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
||||
return Seal::Regular(vec![encode(&step).into_vec(), encode(&(&H520::from(signature) as &[u8])).into_vec()]);
|
||||
}
|
||||
} else {
|
||||
warn!(target: "engine", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||
}
|
||||
} else {
|
||||
trace!(target: "engine", "generate_seal: Not a proposer for step {}.", step);
|
||||
trace!(target: "engine", "generate_seal: {} not a proposer for step {}.",
|
||||
header.author(), step);
|
||||
}
|
||||
Seal::None
|
||||
}
|
||||
|
||||
fn on_new_block(
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<::env_info::LastHashes>,
|
||||
epoch_begin: bool,
|
||||
) -> Result<(), Error> {
|
||||
let parent_hash = block.fields().header.parent_hash().clone();
|
||||
::engines::common::push_last_hash(block, last_hashes.clone(), self, &parent_hash)?;
|
||||
|
||||
if !epoch_begin { return Ok(()) }
|
||||
|
||||
// genesis is never a new block, but might as well check.
|
||||
let header = block.fields().header.clone();
|
||||
let first = header.number() == 0;
|
||||
|
||||
let mut call = |to, data| {
|
||||
let result = ::engines::common::execute_as_system(
|
||||
block,
|
||||
last_hashes.clone(),
|
||||
self,
|
||||
to,
|
||||
U256::max_value(), // unbounded gas? maybe make configurable.
|
||||
Some(data),
|
||||
);
|
||||
|
||||
result.map_err(|e| format!("{}", e))
|
||||
};
|
||||
|
||||
self.validators.on_epoch_begin(first, &header, &mut call)
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||
let fields = block.fields_mut();
|
||||
@ -386,19 +604,26 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
let parent_step = header_step(parent)?;
|
||||
|
||||
// Ensure header is from the step after parent.
|
||||
if step == parent_step
|
||||
|| (header.number() >= self.validate_step_transition && step <= parent_step) {
|
||||
trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step);
|
||||
self.validators.report_malicious(header.author(), header.number(), Default::default());
|
||||
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
}
|
||||
// Report skipped primaries.
|
||||
if step > parent_step + 1 {
|
||||
// TODO: use epochmanager to get correct validator set for reporting?
|
||||
// or just rely on the fact that in general these will be the same
|
||||
// and some reports might go missing?
|
||||
trace!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}",
|
||||
header.author(), step, parent_step);
|
||||
|
||||
for s in parent_step + 1..step {
|
||||
let skipped_primary = self.step_proposer(&parent.hash(), s);
|
||||
trace!(target: "engine", "Author {} did not build his block on top of the intermediate designated primary {}.", header.author(), skipped_primary);
|
||||
self.validators.report_benign(&skipped_primary, header.number());
|
||||
let skipped_primary = step_proposer(&*self.validators, &parent.hash(), s);
|
||||
self.validators.report_benign(&skipped_primary, header.number(), header.number());
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,30 +638,164 @@ impl Engine for AuthorityRound {
|
||||
|
||||
// Check the validators.
|
||||
fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
verify_external(header, &*self.validators, &*self.step)
|
||||
// fetch correct validator set for current epoch, taking into account
|
||||
// finality of previous transitions.
|
||||
let active_set;
|
||||
|
||||
let (validators, set_number) = if self.immediate_transitions {
|
||||
(&*self.validators, header.number())
|
||||
} else {
|
||||
// get correct validator set for epoch.
|
||||
let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
|
||||
Some(client) => client,
|
||||
None => {
|
||||
debug!(target: "engine", "Unable to verify sig: missing client ref.");
|
||||
return Err(EngineError::RequiresClient.into())
|
||||
}
|
||||
};
|
||||
|
||||
let mut epoch_manager = self.epoch_manager.lock();
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, header) {
|
||||
debug!(target: "engine", "Unable to zoom to epoch.");
|
||||
return Err(EngineError::RequiresClient.into())
|
||||
}
|
||||
|
||||
// the proofs we need just allow us to get the full validator set.
|
||||
fn epoch_proof(&self, header: &Header, caller: &Call) -> Result<Bytes, Error> {
|
||||
self.validators.epoch_proof(header, caller)
|
||||
.map_err(|e| EngineError::InsufficientProof(e).into())
|
||||
active_set = epoch_manager.validators().clone();
|
||||
(&active_set as &_, epoch_manager.epoch_transition_number)
|
||||
};
|
||||
|
||||
let report = |report| match report {
|
||||
Report::Benign(address, block_number) =>
|
||||
self.validators.report_benign(&address, set_number, block_number),
|
||||
Report::Malicious(address, block_number, proof) =>
|
||||
self.validators.report_malicious(&address, set_number, block_number, proof),
|
||||
};
|
||||
|
||||
// verify signature against fixed list, but reports should go to the
|
||||
// contract itself.
|
||||
verify_external(header, validators, &*self.step, report)
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||
self.validators.genesis_epoch_data(header, call)
|
||||
.map(|set_proof| combine_proofs(0, &set_proof, &[]))
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
{
|
||||
self.validators.is_epoch_end(header, block, receipts)
|
||||
if self.immediate_transitions { return super::EpochChange::No }
|
||||
|
||||
let first = header.number() == 0;
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
}
|
||||
|
||||
fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result<Box<super::EpochVerifier>, Error> {
|
||||
// extract a simple list from the proof.
|
||||
let (num, simple_list) = self.validators.epoch_set(header, proof)?;
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
chain_head: &Header,
|
||||
chain: &super::Headers,
|
||||
transition_store: &super::PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
// epochs only matter if we want to support light clients.
|
||||
if self.immediate_transitions { return None }
|
||||
|
||||
Ok(Box::new(EpochVerifier {
|
||||
epoch_number: num,
|
||||
let first = chain_head.number() == 0;
|
||||
|
||||
// apply immediate transitions.
|
||||
if let Some(change) = self.validators.is_epoch_end(first, chain_head) {
|
||||
return Some(change)
|
||||
}
|
||||
|
||||
let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
|
||||
Some(client) => client,
|
||||
None => {
|
||||
warn!(target: "engine", "Unable to check for epoch end: missing client ref.");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// find most recently finalized blocks, then check transition store for pending transitions.
|
||||
let mut epoch_manager = self.epoch_manager.lock();
|
||||
if !epoch_manager.zoom_to(&*client, self, &*self.validators, chain_head) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if epoch_manager.finality_checker.subchain_head() != Some(*chain_head.parent_hash()) {
|
||||
// build new finality checker from ancestry of chain head,
|
||||
// not including chain head itself yet.
|
||||
let mut hash = chain_head.parent_hash().clone();
|
||||
let epoch_transition_hash = epoch_manager.epoch_transition_hash;
|
||||
|
||||
// walk the chain within current epoch backwards.
|
||||
// author == ec_recover(sig) known since
|
||||
// the blocks are in the DB.
|
||||
let ancestry_iter = itertools::repeat_call(move || {
|
||||
chain(hash).and_then(|header| {
|
||||
if header.number() == 0 { return None }
|
||||
|
||||
let res = (hash, header.author().clone());
|
||||
hash = header.parent_hash().clone();
|
||||
Some(res)
|
||||
})
|
||||
})
|
||||
.while_some()
|
||||
.take_while(|&(h, _)| h != epoch_transition_hash);
|
||||
|
||||
if let Err(_) = epoch_manager.finality_checker.build_ancestry_subchain(ancestry_iter) {
|
||||
debug!(target: "engine", "inconsistent validator set within epoch");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if let Ok(finalized) = epoch_manager.finality_checker.push_hash(chain_head.hash(), *chain_head.author()) {
|
||||
let mut finalized = finalized.into_iter();
|
||||
while let Some(hash) = finalized.next() {
|
||||
if let Some(pending) = transition_store(hash) {
|
||||
let finality_proof = ::std::iter::once(hash)
|
||||
.chain(finalized)
|
||||
.chain(epoch_manager.finality_checker.unfinalized_hashes())
|
||||
.map(|hash| chain(hash)
|
||||
.expect("these headers fetched before when constructing finality checker; qed"))
|
||||
.collect::<Vec<Header>>();
|
||||
|
||||
// this gives us the block number for `hash`, assuming it's ancestry.
|
||||
let signal_number = chain_head.number()
|
||||
- finality_proof.len() as BlockNumber
|
||||
+ 1;
|
||||
let finality_proof = ::rlp::encode_list(&finality_proof);
|
||||
epoch_manager.note_new_epoch();
|
||||
|
||||
return Some(combine_proofs(signal_number, &pending.proof, &*finality_proof));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return ConstructedVerifier::Err(e),
|
||||
};
|
||||
|
||||
let first = signal_number == 0;
|
||||
match self.validators.epoch_set(first, self, signal_number, set_proof) {
|
||||
Ok((list, finalize)) => {
|
||||
let verifier = Box::new(EpochVerifier {
|
||||
step: self.step.clone(),
|
||||
subchain_validators: simple_list,
|
||||
}))
|
||||
subchain_validators: list,
|
||||
});
|
||||
|
||||
match finalize {
|
||||
Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, finality_proof, finalize),
|
||||
None => ConstructedVerifier::Trusted(verifier),
|
||||
}
|
||||
}
|
||||
Err(e) => ConstructedVerifier::Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
@ -465,8 +824,12 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
if self.immediate_transitions {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(::snapshot::PoaSnapshot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -517,7 +880,7 @@ mod tests {
|
||||
fn can_do_signature_verification_fail() {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![encode(&H520::default()).to_vec()]);
|
||||
header.set_seal(vec![encode(&H520::default()).into_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_external(&header, None);
|
||||
assert!(verify_result.is_err());
|
||||
@ -535,9 +898,9 @@ mod tests {
|
||||
let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b1 = b1.close_and_lock();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b2 = b2.close_and_lock();
|
||||
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
@ -560,7 +923,7 @@ mod tests {
|
||||
let tap = AccountProvider::transient_provider();
|
||||
let addr = tap.insert_account("0".sha3().into(), "0").unwrap();
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||
parent_header.set_seal(vec![encode(&0usize).into_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
@ -572,10 +935,10 @@ mod tests {
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&2usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_err());
|
||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
}
|
||||
@ -586,7 +949,7 @@ mod tests {
|
||||
let addr = tap.insert_account("0".sha3().into(), "0").unwrap();
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
|
||||
parent_header.set_seal(vec![encode(&0usize).into_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
@ -598,10 +961,10 @@ mod tests {
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&1usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert!(engine.verify_block_external(&header, None).is_err());
|
||||
}
|
||||
@ -612,7 +975,7 @@ mod tests {
|
||||
let addr = tap.insert_account("0".sha3().into(), "0").unwrap();
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&4usize).to_vec()]);
|
||||
parent_header.set_seal(vec![encode(&4usize).into_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
@ -624,9 +987,9 @@ mod tests {
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
header.set_seal(vec![encode(&3usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
header.set_seal(vec![encode(&3usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
|
||||
}
|
||||
|
||||
@ -643,16 +1006,17 @@ mod tests {
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
eip155_transition: 0,
|
||||
immediate_transitions: true,
|
||||
};
|
||||
let aura = AuthorityRound::new(Default::default(), params, Default::default()).unwrap();
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&1usize).to_vec()]);
|
||||
parent_header.set_seal(vec![encode(&1usize).into_vec()]);
|
||||
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_number(1);
|
||||
header.set_gas_limit(U256::from_str("222222").unwrap());
|
||||
header.set_seal(vec![encode(&3usize).to_vec()]);
|
||||
header.set_seal(vec![encode(&3usize).into_vec()]);
|
||||
|
||||
assert!(aura.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 1);
|
@ -23,7 +23,7 @@ use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, EngineError, Seal, Call, EpochChange};
|
||||
use engines::{Engine, Seal, Call, ConstructedVerifier, EngineError};
|
||||
use error::{BlockError, Error};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
@ -51,12 +51,10 @@ impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
|
||||
}
|
||||
|
||||
struct EpochVerifier {
|
||||
epoch_number: u64,
|
||||
list: SimpleList,
|
||||
}
|
||||
|
||||
impl super::EpochVerifier for EpochVerifier {
|
||||
fn epoch_number(&self) -> u64 { self.epoch_number.clone() }
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error> {
|
||||
verify_external(header, &self.list)
|
||||
}
|
||||
@ -141,7 +139,7 @@ impl Engine for BasicAuthority {
|
||||
if self.validators.contains(header.parent_hash(), author) {
|
||||
// account should be pernamently unlocked, otherwise sealing will fail
|
||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
||||
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).into_vec()]);
|
||||
} else {
|
||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
}
|
||||
@ -187,26 +185,54 @@ impl Engine for BasicAuthority {
|
||||
verify_external(header, &*self.validators)
|
||||
}
|
||||
|
||||
// the proofs we need just allow us to get the full validator set.
|
||||
fn epoch_proof(&self, header: &Header, caller: &Call) -> Result<Bytes, Error> {
|
||||
self.validators.epoch_proof(header, caller)
|
||||
.map_err(|e| EngineError::InsufficientProof(e).into())
|
||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||
self.validators.genesis_epoch_data(header, call)
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> EpochChange
|
||||
#[cfg(not(test))]
|
||||
fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
{
|
||||
self.validators.is_epoch_end(header, block, receipts)
|
||||
// don't bother signalling even though a contract might try.
|
||||
super::EpochChange::No
|
||||
}
|
||||
|
||||
fn epoch_verifier(&self, header: &Header, proof: &[u8]) -> Result<Box<super::EpochVerifier>, Error> {
|
||||
// extract a simple list from the proof.
|
||||
let (num, simple_list) = self.validators.epoch_set(header, proof)?;
|
||||
#[cfg(test)]
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange
|
||||
{
|
||||
// in test mode, always signal even though they don't be finalized.
|
||||
let first = header.number() == 0;
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
}
|
||||
|
||||
Ok(Box::new(EpochVerifier {
|
||||
epoch_number: num,
|
||||
list: simple_list,
|
||||
}))
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
chain_head: &Header,
|
||||
_chain: &super::Headers,
|
||||
_transition_store: &super::PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
let first = chain_head.number() == 0;
|
||||
|
||||
// finality never occurs so only apply immediate transitions.
|
||||
self.validators.is_epoch_end(first, chain_head)
|
||||
}
|
||||
|
||||
fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
let first = header.number() == 0;
|
||||
|
||||
match self.validators.epoch_set(first, self, header.number(), proof) {
|
||||
Ok((list, finalize)) => {
|
||||
let verifier = Box::new(EpochVerifier { list: list });
|
||||
|
||||
// our epoch verifier will ensure no unverified verifier is ever verified.
|
||||
match finalize {
|
||||
Some(finalize) => ConstructedVerifier::Unconfirmed(verifier, proof, finalize),
|
||||
None => ConstructedVerifier::Trusted(verifier),
|
||||
}
|
||||
}
|
||||
Err(e) => ConstructedVerifier::Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
@ -222,7 +248,7 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PoaSnapshot))
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,7 +301,7 @@ mod tests {
|
||||
fn can_do_signature_verification_fail() {
|
||||
let engine = new_test_authority().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
|
||||
assert!(verify_result.is_err());
|
||||
@ -292,7 +318,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
if let Seal::Regular(seal) = engine.generate_seal(b.block()) {
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
|
102
ethcore/src/engines/epoch.rs
Normal file
102
ethcore/src/engines/epoch.rs
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
//! Epoch verifiers and transitions.
|
||||
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
|
||||
use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp};
|
||||
use util::H256;
|
||||
|
||||
/// A full epoch transition.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Transition {
|
||||
/// Block hash at which the transition occurred.
|
||||
pub block_hash: H256,
|
||||
/// Block number at which the transition occurred.
|
||||
pub block_number: u64,
|
||||
/// "transition/epoch" proof from the engine combined with a finality proof.
|
||||
pub proof: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Encodable for Transition {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(3)
|
||||
.append(&self.block_hash)
|
||||
.append(&self.block_number)
|
||||
.append(&self.proof);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Transition {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
Ok(Transition {
|
||||
block_hash: rlp.val_at(0)?,
|
||||
block_number: rlp.val_at(1)?,
|
||||
proof: rlp.val_at(2)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An epoch transition pending a finality proof.
|
||||
/// Not all transitions need one.
|
||||
pub struct PendingTransition {
|
||||
/// "transition/epoch" proof from the engine.
|
||||
pub proof: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Encodable for PendingTransition {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.append(&self.proof);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for PendingTransition {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
Ok(PendingTransition {
|
||||
proof: rlp.as_val()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifier for all blocks within an epoch with self-contained state.
|
||||
///
|
||||
/// See docs on `Engine` relating to proving functions for more details.
|
||||
pub trait EpochVerifier: Send + Sync {
|
||||
/// Lightly verify the next block header.
|
||||
/// This may not be a header belonging to a different epoch.
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error>;
|
||||
|
||||
/// Perform potentially heavier checks on the next block header.
|
||||
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
|
||||
self.verify_light(header)
|
||||
}
|
||||
|
||||
/// Check a finality proof against this epoch verifier.
|
||||
/// Returns `Some(hashes)` if the proof proves finality of these hashes.
|
||||
/// Returns `None` if the proof doesn't prove anything.
|
||||
fn check_finality_proof(&self, _proof: &[u8]) -> Option<Vec<H256>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Special "no-op" verifier for stateless, epoch-less engines.
|
||||
pub struct NoOp;
|
||||
|
||||
impl EpochVerifier for NoOp {
|
||||
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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/>.
|
||||
|
||||
// Epoch verifiers.
|
||||
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
|
||||
/// Verifier for all blocks within an epoch with self-contained state.
|
||||
///
|
||||
/// See docs on `Engine` relating to proving functions for more details.
|
||||
pub trait EpochVerifier: Send + Sync {
|
||||
/// Get the epoch number.
|
||||
fn epoch_number(&self) -> u64;
|
||||
|
||||
/// Lightly verify the next block header.
|
||||
/// This may not be a header belonging to a different epoch.
|
||||
fn verify_light(&self, header: &Header) -> Result<(), Error>;
|
||||
|
||||
/// Perform potentially heavier checks on the next block header.
|
||||
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
|
||||
self.verify_light(header)
|
||||
}
|
||||
}
|
||||
|
||||
/// Special "no-op" verifier for stateless, epoch-less engines.
|
||||
pub struct NoOp;
|
||||
|
||||
impl EpochVerifier for NoOp {
|
||||
fn epoch_number(&self) -> u64 { 0 }
|
||||
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
}
|
@ -79,7 +79,7 @@ mod tests {
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
if let Seal::Regular(seal) = engine.generate_seal(b.block()) {
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
@ -93,7 +93,7 @@ mod tests {
|
||||
|
||||
assert!(engine.verify_block_basic(&header, None).is_ok());
|
||||
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
|
||||
header.set_seal(vec![::rlp::encode(&H520::default()).into_vec()]);
|
||||
|
||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
mod authority_round;
|
||||
mod basic_authority;
|
||||
mod epoch_verifier;
|
||||
mod instant_seal;
|
||||
mod null_engine;
|
||||
mod signer;
|
||||
@ -27,15 +26,19 @@ mod transition;
|
||||
mod validator_set;
|
||||
mod vote_collector;
|
||||
|
||||
pub mod epoch;
|
||||
|
||||
pub use self::authority_round::AuthorityRound;
|
||||
pub use self::basic_authority::BasicAuthority;
|
||||
pub use self::epoch_verifier::EpochVerifier;
|
||||
pub use self::epoch::{EpochVerifier, Transition as EpochTransition};
|
||||
pub use self::instant_seal::InstantSeal;
|
||||
pub use self::null_engine::NullEngine;
|
||||
pub use self::tendermint::Tendermint;
|
||||
|
||||
use std::sync::Weak;
|
||||
|
||||
use self::epoch::PendingTransition;
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
@ -72,6 +75,10 @@ pub enum EngineError {
|
||||
BadSealFieldSize(OutOfBounds<usize>),
|
||||
/// Validation proof insufficient.
|
||||
InsufficientProof(String),
|
||||
/// Failed system call.
|
||||
FailedSystemCall(String),
|
||||
/// Requires client ref, but none registered.
|
||||
RequiresClient,
|
||||
}
|
||||
|
||||
impl fmt::Display for EngineError {
|
||||
@ -84,6 +91,8 @@ impl fmt::Display for EngineError {
|
||||
UnexpectedMessage => "This Engine should not be fed messages.".into(),
|
||||
BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob),
|
||||
InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg),
|
||||
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
|
||||
RequiresClient => format!("Call requires client but none registered"),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Engine error ({})", msg))
|
||||
@ -102,17 +111,53 @@ pub enum Seal {
|
||||
}
|
||||
|
||||
/// Type alias for a function we can make calls through synchronously.
|
||||
pub type Call<'a> = Fn(Address, Bytes) -> Result<Bytes, String> + 'a;
|
||||
/// Returns the call result and state proof for each call.
|
||||
pub type Call<'a> = Fn(Address, Bytes) -> Result<(Bytes, Vec<Vec<u8>>), String> + 'a;
|
||||
|
||||
/// Type alias for a function we can get headers by hash through.
|
||||
pub type Headers<'a> = Fn(H256) -> Option<Header> + 'a;
|
||||
|
||||
/// Type alias for a function we can query pending transitions by block hash through.
|
||||
pub type PendingTransitionStore<'a> = Fn(H256) -> Option<PendingTransition> + 'a;
|
||||
|
||||
/// Proof generated on epoch change.
|
||||
pub enum Proof {
|
||||
/// Known proof (exctracted from signal)
|
||||
Known(Vec<u8>),
|
||||
/// Extract proof from caller.
|
||||
WithState(Box<Fn(&Call) -> Result<Vec<u8>, String>>),
|
||||
}
|
||||
|
||||
/// Generated epoch verifier.
|
||||
pub enum ConstructedVerifier<'a> {
|
||||
/// Fully trusted verifier.
|
||||
Trusted(Box<EpochVerifier>),
|
||||
/// Verifier unconfirmed. Check whether given finality proof finalizes given hash
|
||||
/// under previous epoch.
|
||||
Unconfirmed(Box<EpochVerifier>, &'a [u8], H256),
|
||||
/// Error constructing verifier.
|
||||
Err(Error),
|
||||
}
|
||||
|
||||
impl<'a> ConstructedVerifier<'a> {
|
||||
/// Convert to a result, indicating that any necessary confirmation has been done
|
||||
/// already.
|
||||
pub fn known_confirmed(self) -> Result<Box<EpochVerifier>, Error> {
|
||||
match self {
|
||||
ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v),
|
||||
ConstructedVerifier::Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Results of a query of whether an epoch change occurred at the given block.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EpochChange {
|
||||
/// Cannot determine until more data is passed.
|
||||
Unsure(Unsure),
|
||||
/// No epoch change.
|
||||
No,
|
||||
/// Validation proof required, and the new epoch number.
|
||||
Yes(u64),
|
||||
/// The epoch will change, with proof.
|
||||
Yes(Proof),
|
||||
}
|
||||
|
||||
/// More data required to determine if an epoch change occurred at a given block.
|
||||
@ -171,7 +216,13 @@ pub trait Engine : Sync + Send {
|
||||
}
|
||||
|
||||
/// Block transformation functions, before the transactions.
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>) -> Result<(), Error> {
|
||||
/// `epoch_begin` set to true if this block kicks off an epoch.
|
||||
fn on_new_block(
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
_epoch_begin: bool,
|
||||
) -> Result<(), Error> {
|
||||
let parent_hash = block.fields().header.parent_hash().clone();
|
||||
common::push_last_hash(block, last_hashes, self, &parent_hash)
|
||||
}
|
||||
@ -233,46 +284,44 @@ pub trait Engine : Sync + Send {
|
||||
self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None))
|
||||
}
|
||||
|
||||
/// Generate epoch change proof.
|
||||
///
|
||||
/// This will be used to generate proofs of epoch change as well as verify them.
|
||||
/// Must be called on blocks that have already passed basic verification.
|
||||
///
|
||||
/// Return the "epoch proof" generated.
|
||||
/// This must be usable to generate a `EpochVerifier` for verifying all blocks
|
||||
/// from the supplied header up to the next one where proof is required.
|
||||
///
|
||||
/// For example, for PoA chains the proof will be a validator set,
|
||||
/// and the corresponding `EpochVerifier` can be used to correctly validate
|
||||
/// all blocks produced under that `ValidatorSet`
|
||||
///
|
||||
/// It must be possible to generate an epoch proof for any block in an epoch,
|
||||
/// and it should always be equivalent to the proof of the transition block.
|
||||
fn epoch_proof(&self, _header: &Header, _caller: &Call)
|
||||
-> Result<Vec<u8>, Error>
|
||||
{
|
||||
Ok(Vec::new())
|
||||
}
|
||||
/// Genesis epoch data.
|
||||
fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result<Vec<u8>, String> { Ok(Vec::new()) }
|
||||
|
||||
/// Whether an epoch change occurred at the given header.
|
||||
/// Whether an epoch change is signalled at the given header but will require finality.
|
||||
/// If a change can be enacted immediately then return `No` from this function but
|
||||
/// `Yes` from `is_epoch_end`.
|
||||
///
|
||||
/// If the block or receipts are required, return `Unsure` and the function will be
|
||||
/// called again with them.
|
||||
/// Return `Yes` or `No` when the answer is definitively known.
|
||||
///
|
||||
/// Should not interact with state.
|
||||
fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>)
|
||||
fn signals_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[Receipt]>)
|
||||
-> EpochChange
|
||||
{
|
||||
EpochChange::No
|
||||
}
|
||||
|
||||
/// Create an epoch verifier from validation proof.
|
||||
/// Whether a block is the end of an epoch.
|
||||
///
|
||||
/// The proof should be one generated by `epoch_proof`.
|
||||
/// See docs of `epoch_proof` for description.
|
||||
fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result<Box<EpochVerifier>, Error> {
|
||||
Ok(Box::new(self::epoch_verifier::NoOp))
|
||||
/// This either means that an immediate transition occurs or a block signalling transition
|
||||
/// has reached finality. The `Headers` given are not guaranteed to return any blocks
|
||||
/// from any epoch other than the current.
|
||||
///
|
||||
/// Return optional transition proof.
|
||||
fn is_epoch_end(
|
||||
&self,
|
||||
_chain_head: &Header,
|
||||
_chain: &Headers,
|
||||
_transition_store: &PendingTransitionStore,
|
||||
) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create an epoch verifier from validation proof and a flag indicating
|
||||
/// whether finality is required.
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a> {
|
||||
ConstructedVerifier::Trusted(Box::new(self::epoch::NoOp))
|
||||
}
|
||||
|
||||
/// Populate a header's fields based on its parent's header.
|
||||
@ -329,7 +378,11 @@ pub trait Engine : Sync + Send {
|
||||
|
||||
/// Returns new contract address generation scheme at given block number.
|
||||
fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress {
|
||||
if number >= self.params().eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }
|
||||
if number >= self.params().eip86_transition {
|
||||
CreateContractAddress::FromCodeHash
|
||||
} else {
|
||||
CreateContractAddress::FromSenderAndNonce
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,13 +402,15 @@ pub mod common {
|
||||
use util::*;
|
||||
use super::Engine;
|
||||
|
||||
/// Push last known block hash to the state.
|
||||
pub fn push_last_hash<E: Engine + ?Sized>(block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>, engine: &E, hash: &H256) -> Result<(), Error> {
|
||||
if block.fields().header.number() == engine.params().eip210_transition {
|
||||
let state = block.fields_mut().state;
|
||||
state.init_code(&engine.params().eip210_contract_address, engine.params().eip210_contract_code.clone())?;
|
||||
}
|
||||
if block.fields().header.number() >= engine.params().eip210_transition {
|
||||
/// Execute a call as the system address.
|
||||
pub fn execute_as_system<E: Engine + ?Sized>(
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
engine: &E,
|
||||
contract_address: Address,
|
||||
gas: U256,
|
||||
data: Option<Bytes>,
|
||||
) -> Result<Bytes, Error> {
|
||||
let env_info = {
|
||||
let header = block.fields().header;
|
||||
EnvInfo {
|
||||
@ -365,30 +420,49 @@ pub mod common {
|
||||
difficulty: header.difficulty().clone(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: engine.params().eip210_contract_gas,
|
||||
gas_limit: gas,
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = block.fields_mut().state;
|
||||
let contract_address = engine.params().eip210_contract_address;
|
||||
let params = ActionParams {
|
||||
code_address: contract_address.clone(),
|
||||
address: contract_address.clone(),
|
||||
sender: SYSTEM_ADDRESS.clone(),
|
||||
origin: SYSTEM_ADDRESS.clone(),
|
||||
gas: engine.params().eip210_contract_gas,
|
||||
gas: gas,
|
||||
gas_price: 0.into(),
|
||||
value: ActionValue::Transfer(0.into()),
|
||||
code: state.code(&contract_address)?,
|
||||
code_hash: state.code_hash(&contract_address)?,
|
||||
data: Some(hash.to_vec()),
|
||||
data: data,
|
||||
call_type: CallType::Call,
|
||||
};
|
||||
let mut ex = Executive::new(&mut state, &env_info, engine);
|
||||
let mut substate = Substate::new();
|
||||
let mut output = [];
|
||||
if let Err(e) = ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer) {
|
||||
warn!("Encountered error on updating last hashes: {}", e);
|
||||
let mut output = Vec::new();
|
||||
if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) {
|
||||
warn!("Encountered error on making system call: {}", e);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Push last known block hash to the state.
|
||||
pub fn push_last_hash<E: Engine + ?Sized>(block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>, engine: &E, hash: &H256) -> Result<(), Error> {
|
||||
if block.fields().header.number() == engine.params().eip210_transition {
|
||||
let state = block.fields_mut().state;
|
||||
state.init_code(&engine.params().eip210_contract_address, engine.params().eip210_contract_code.clone())?;
|
||||
}
|
||||
if block.fields().header.number() >= engine.params().eip210_transition {
|
||||
let _ = execute_as_system(
|
||||
block,
|
||||
last_hashes,
|
||||
engine,
|
||||
engine.params().eip210_contract_address,
|
||||
engine.params().eip210_contract_gas,
|
||||
Some(hash.to_vec()),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ mod tests {
|
||||
},
|
||||
block_hash: Some("1".sha3())
|
||||
};
|
||||
let raw_rlp = ::rlp::encode(&message).to_vec();
|
||||
let raw_rlp = ::rlp::encode(&message).into_vec();
|
||||
let rlp = Rlp::new(&raw_rlp);
|
||||
assert_eq!(message, rlp.as_val());
|
||||
|
||||
@ -265,8 +265,8 @@ mod tests {
|
||||
fn proposal_message() {
|
||||
let mut header = Header::default();
|
||||
let seal = vec![
|
||||
::rlp::encode(&0u8).to_vec(),
|
||||
::rlp::encode(&H520::default()).to_vec(),
|
||||
::rlp::encode(&0u8).into_vec(),
|
||||
::rlp::encode(&H520::default()).into_vec(),
|
||||
Vec::new()
|
||||
];
|
||||
|
||||
|
@ -342,9 +342,9 @@ impl Tendermint {
|
||||
let precommits = self.votes.round_signatures(vote_step, &bh);
|
||||
trace!(target: "engine", "Collected seal: {:?}", precommits);
|
||||
let seal = vec![
|
||||
::rlp::encode(&vote_step.view).to_vec(),
|
||||
::rlp::encode(&vote_step.view).into_vec(),
|
||||
::rlp::NULL_RLP.to_vec(),
|
||||
::rlp::encode_list(&precommits).to_vec()
|
||||
::rlp::encode_list(&precommits).into_vec()
|
||||
];
|
||||
self.submit_seal(bh, seal);
|
||||
self.votes.throw_out_old(&vote_step);
|
||||
@ -446,8 +446,8 @@ impl Engine for Tendermint {
|
||||
*self.proposal.write() = bh;
|
||||
*self.proposal_parent.write() = header.parent_hash().clone();
|
||||
Seal::Proposal(vec![
|
||||
::rlp::encode(&view).to_vec(),
|
||||
::rlp::encode(&signature).to_vec(),
|
||||
::rlp::encode(&view).into_vec(),
|
||||
::rlp::encode(&signature).into_vec(),
|
||||
::rlp::EMPTY_LIST_RLP.to_vec()
|
||||
])
|
||||
} else {
|
||||
@ -466,7 +466,8 @@ impl Engine for Tendermint {
|
||||
}
|
||||
self.broadcast_message(rlp.as_raw().to_vec());
|
||||
if let Some(double) = self.votes.vote(message.clone(), &sender) {
|
||||
self.validators.report_malicious(&sender, message.vote_step.height as BlockNumber, ::rlp::encode(&double).to_vec());
|
||||
let height = message.vote_step.height as BlockNumber;
|
||||
self.validators.report_malicious(&sender, height, height, ::rlp::encode(&double).into_vec());
|
||||
return Err(EngineError::DoubleVote(sender).into());
|
||||
}
|
||||
trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender);
|
||||
@ -555,7 +556,7 @@ impl Engine for Tendermint {
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
self.validators.report_malicious(header.author(), header.number(), Default::default());
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into());
|
||||
}
|
||||
|
||||
@ -607,7 +608,7 @@ impl Engine for Tendermint {
|
||||
// Report the proposer if no proposal was received.
|
||||
let height = self.height.load(AtomicOrdering::SeqCst);
|
||||
let current_proposer = self.view_proposer(&*self.proposal_parent.read(), height, self.view.load(AtomicOrdering::SeqCst));
|
||||
self.validators.report_benign(¤t_proposer, height as BlockNumber);
|
||||
self.validators.report_benign(¤t_proposer, height as BlockNumber, height as BlockNumber);
|
||||
}
|
||||
Step::Prevote
|
||||
},
|
||||
@ -674,7 +675,7 @@ mod tests {
|
||||
let db = spec.ensure_db_good(db, &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close();
|
||||
if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) {
|
||||
(b, seal)
|
||||
@ -695,8 +696,8 @@ mod tests {
|
||||
let vote_info = message_info_rlp(&VoteStep::new(header.number() as Height, view, Step::Propose), Some(header.bare_hash()));
|
||||
let signature = tap.sign(*author, None, vote_info.sha3()).unwrap();
|
||||
vec![
|
||||
::rlp::encode(&view).to_vec(),
|
||||
::rlp::encode(&H520::from(signature)).to_vec(),
|
||||
::rlp::encode(&view).into_vec(),
|
||||
::rlp::encode(&H520::from(signature)).into_vec(),
|
||||
::rlp::EMPTY_LIST_RLP.to_vec()
|
||||
]
|
||||
}
|
||||
@ -812,7 +813,7 @@ mod tests {
|
||||
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
|
||||
|
||||
seal[1] = ::rlp::NULL_RLP.to_vec();
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone())]).to_vec();
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone())]).into_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
// One good signature is not enough.
|
||||
@ -824,7 +825,7 @@ mod tests {
|
||||
let voter = insert_and_unlock(&tap, "0");
|
||||
let signature0 = tap.sign(voter, None, vote_info.sha3()).unwrap();
|
||||
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec();
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
@ -832,7 +833,7 @@ mod tests {
|
||||
let bad_voter = insert_and_unlock(&tap, "101");
|
||||
let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap();
|
||||
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1), H520::from(bad_signature)]).to_vec();
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1), H520::from(bad_signature)]).into_vec();
|
||||
header.set_seal(seal);
|
||||
|
||||
// One good and one bad signature.
|
||||
|
@ -36,7 +36,7 @@ pub struct TransitionHandler<S: Sync + Send + Clone> {
|
||||
timeouts: Box<Timeouts<S>>,
|
||||
}
|
||||
|
||||
impl <S> TransitionHandler<S> where S: Sync + Send + Clone {
|
||||
impl<S> TransitionHandler<S> where S: Sync + Send + Clone {
|
||||
/// New step caller by timeouts.
|
||||
pub fn new(engine: Weak<Engine>, timeouts: Box<Timeouts<S>>) -> Self {
|
||||
TransitionHandler {
|
||||
@ -54,7 +54,7 @@ fn set_timeout<S: Sync + Send + Clone>(io: &IoContext<S>, timeout: Duration) {
|
||||
.unwrap_or_else(|e| warn!(target: "engine", "Failed to set consensus step timeout: {}.", e))
|
||||
}
|
||||
|
||||
impl <S> IoHandler<S> for TransitionHandler<S> where S: Sync + Send + Clone + 'static {
|
||||
impl<S> IoHandler<S> for TransitionHandler<S> where S: Sync + Send + Clone + 'static {
|
||||
fn initialize(&self, io: &IoContext<S>) {
|
||||
let initial = self.timeouts.initial();
|
||||
trace!(target: "engine", "Setting the initial timeout to {}.", initial);
|
||||
|
@ -24,10 +24,10 @@ use futures::Future;
|
||||
use native_contracts::ValidatorReport as Provider;
|
||||
|
||||
use client::{Client, BlockChainClient};
|
||||
use engines::Call;
|
||||
use engines::{Call, Engine};
|
||||
use header::{Header, BlockNumber};
|
||||
|
||||
use super::ValidatorSet;
|
||||
use super::{ValidatorSet, SimpleList, SystemCall};
|
||||
use super::safe_contract::ValidatorSafeContract;
|
||||
|
||||
/// A validator contract with reporting.
|
||||
@ -51,7 +51,7 @@ impl ValidatorContract {
|
||||
// could be `impl Trait`.
|
||||
// note: dispatches transactions to network as well as execute.
|
||||
// TODO [keorn]: Make more general.
|
||||
fn transact(&self) -> Box<Call> {
|
||||
fn transact(&self) -> Box<Fn(Address, Bytes) -> Result<Bytes, String>> {
|
||||
let client = self.client.read().clone();
|
||||
Box::new(move |a, d| client.as_ref()
|
||||
.and_then(Weak::upgrade)
|
||||
@ -66,18 +66,30 @@ impl ValidatorSet for ValidatorContract {
|
||||
self.validators.default_caller(id)
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
self.validators.is_epoch_end(header, block, receipts)
|
||||
fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> {
|
||||
self.validators.on_epoch_begin(first, header, call)
|
||||
}
|
||||
|
||||
fn epoch_proof(&self, header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
|
||||
self.validators.epoch_proof(header, caller)
|
||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||
self.validators.genesis_epoch_data(header, call)
|
||||
}
|
||||
|
||||
fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, super::SimpleList), ::error::Error> {
|
||||
self.validators.epoch_set(header, proof)
|
||||
fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option<Vec<u8>> {
|
||||
self.validators.is_epoch_end(first, chain_head)
|
||||
}
|
||||
|
||||
fn signals_epoch_end(
|
||||
&self,
|
||||
first: bool,
|
||||
header: &Header,
|
||||
block: Option<&[u8]>,
|
||||
receipts: Option<&[::receipt::Receipt]>,
|
||||
) -> ::engines::EpochChange {
|
||||
self.validators.signals_epoch_end(first, header, block, receipts)
|
||||
}
|
||||
|
||||
fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
|
||||
self.validators.epoch_set(first, engine, number, proof)
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
|
||||
@ -92,14 +104,14 @@ impl ValidatorSet for ValidatorContract {
|
||||
self.validators.count_with_caller(bh, caller)
|
||||
}
|
||||
|
||||
fn report_malicious(&self, address: &Address, block: BlockNumber, proof: Bytes) {
|
||||
fn report_malicious(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber, proof: Bytes) {
|
||||
match self.provider.report_malicious(&*self.transact(), *address, block.into(), proof).wait() {
|
||||
Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address),
|
||||
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
|
||||
}
|
||||
}
|
||||
|
||||
fn report_benign(&self, address: &Address, block: BlockNumber) {
|
||||
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
||||
match self.provider.report_benign(&*self.transact(), *address, block.into()).wait() {
|
||||
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
|
||||
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
|
||||
@ -149,7 +161,7 @@ mod tests {
|
||||
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
let mut header = Header::default();
|
||||
let seal = vec![encode(&5u8).to_vec(), encode(&(&H520::default() as &[u8])).to_vec()];
|
||||
let seal = vec![encode(&5u8).into_vec(), encode(&(&H520::default() as &[u8])).into_vec()];
|
||||
header.set_seal(seal);
|
||||
header.set_author(v1);
|
||||
header.set_number(2);
|
||||
|
@ -37,7 +37,10 @@ use self::contract::ValidatorContract;
|
||||
use self::safe_contract::ValidatorSafeContract;
|
||||
use self::multi::Multi;
|
||||
|
||||
use super::Call;
|
||||
use super::{Call, Engine};
|
||||
|
||||
/// A system-calling closure. Enacts calls on a block's state from the system address.
|
||||
pub type SystemCall<'a> = FnMut(Address, Bytes) -> Result<Bytes, String> + 'a;
|
||||
|
||||
/// Creates a validator set from spec.
|
||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
|
||||
@ -64,6 +67,7 @@ pub trait ValidatorSet: Send + Sync {
|
||||
let default = self.default_caller(BlockId::Hash(*parent));
|
||||
self.contains_with_caller(parent, address, &*default)
|
||||
}
|
||||
|
||||
/// Draws an validator nonce modulo number of validators.
|
||||
fn get(&self, parent: &H256, nonce: usize) -> Address {
|
||||
let default = self.default_caller(BlockId::Hash(*parent));
|
||||
@ -76,48 +80,66 @@ pub trait ValidatorSet: Send + Sync {
|
||||
self.count_with_caller(parent, &*default)
|
||||
}
|
||||
|
||||
/// Signalling that a new epoch has begun.
|
||||
///
|
||||
/// All calls here will be from the `SYSTEM_ADDRESS`: 2^160 - 2
|
||||
/// and will have an effect on the block's state.
|
||||
/// The caller provided here may not generate proofs.
|
||||
///
|
||||
/// `first` is true if this is the first block in the set.
|
||||
fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), ::error::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Extract genesis epoch data from the genesis state and header.
|
||||
fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result<Vec<u8>, String> { Ok(Vec::new()) }
|
||||
|
||||
/// Whether this block is the last one in its epoch.
|
||||
/// Usually indicates that the validator set changed at the given block.
|
||||
///
|
||||
/// Should not inspect state! This is used in situations where
|
||||
/// state is not generally available.
|
||||
/// Indicates that the validator set changed at the given block in a manner
|
||||
/// that doesn't require finality.
|
||||
///
|
||||
/// Return `Yes` or `No` indicating whether it changed at the given header,
|
||||
/// or `Unsure` indicating a need for more information.
|
||||
///
|
||||
/// If block or receipts are provided, do not return `Unsure` indicating
|
||||
/// need for them.
|
||||
fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> super::EpochChange;
|
||||
/// `first` is true if this is the first block in the set.
|
||||
fn is_epoch_end(&self, first: bool, chain_head: &Header) -> Option<Vec<u8>>;
|
||||
|
||||
/// Generate epoch proof.
|
||||
/// Must interact with state only through the given caller!
|
||||
/// Otherwise, generated proofs may be wrong.
|
||||
fn epoch_proof(&self, header: &Header, caller: &Call) -> Result<Vec<u8>, String>;
|
||||
/// Whether the given block signals the end of an epoch, but change won't take effect
|
||||
/// until finality.
|
||||
///
|
||||
/// Engine should set `first` only if the header is genesis. Multiplexing validator
|
||||
/// sets can set `first` to internal changes.
|
||||
fn signals_epoch_end(
|
||||
&self,
|
||||
first: bool,
|
||||
header: &Header,
|
||||
block: Option<&[u8]>,
|
||||
receipts: Option<&[::receipt::Receipt]>,
|
||||
) -> ::engines::EpochChange;
|
||||
|
||||
/// Recover the validator set for all
|
||||
/// Recover the validator set from the given proof, the block number, and
|
||||
/// whether this header is first in its set.
|
||||
///
|
||||
/// May fail if the given header doesn't kick off an epoch or
|
||||
/// the proof is invalid.
|
||||
///
|
||||
/// Returns the epoch number and proof.
|
||||
fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, SimpleList), ::error::Error>;
|
||||
/// Returns the set, along with a flag indicating whether finality of a specific
|
||||
/// hash should be proven.
|
||||
fn epoch_set(&self, first: bool, engine: &Engine, number: BlockNumber, proof: &[u8])
|
||||
-> Result<(SimpleList, Option<H256>), ::error::Error>;
|
||||
|
||||
/// Checks if a given address is a validator, with the given function
|
||||
/// for executing synchronous calls to contracts.
|
||||
fn contains_with_caller(&self, parent_block_hash: &H256, address: &Address, caller: &Call) -> bool;
|
||||
|
||||
/// Draws an validator nonce modulo number of validators.
|
||||
///
|
||||
fn get_with_caller(&self, parent_block_hash: &H256, nonce: usize, caller: &Call) -> Address;
|
||||
|
||||
/// Returns the current number of validators.
|
||||
fn count_with_caller(&self, parent_block_hash: &H256, caller: &Call) -> usize;
|
||||
|
||||
/// Notifies about malicious behaviour.
|
||||
fn report_malicious(&self, _validator: &Address, _block: BlockNumber, _proof: Bytes) {}
|
||||
fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber, _proof: Bytes) {}
|
||||
/// Notifies about benign misbehaviour.
|
||||
fn report_benign(&self, _validator: &Address, _block: BlockNumber) {}
|
||||
fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {}
|
||||
/// Allows blockchain state access.
|
||||
fn register_contract(&self, _client: Weak<Client>) {}
|
||||
}
|
||||
|
@ -18,12 +18,12 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Weak;
|
||||
use engines::{Call, EpochChange};
|
||||
use engines::{Call, Engine};
|
||||
use util::{Bytes, H256, Address, RwLock};
|
||||
use ids::BlockId;
|
||||
use header::{BlockNumber, Header};
|
||||
use client::{Client, BlockChainClient};
|
||||
use super::ValidatorSet;
|
||||
use super::{SystemCall, ValidatorSet};
|
||||
|
||||
type BlockNumberLookup = Box<Fn(BlockId) -> Result<BlockNumber, String> + Send + Sync + 'static>;
|
||||
|
||||
@ -72,49 +72,38 @@ impl ValidatorSet for Multi {
|
||||
.unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into())))
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> EpochChange
|
||||
fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> {
|
||||
let (set_block, set) = self.correct_set_by_number(header.number());
|
||||
let first = set_block == header.number();
|
||||
|
||||
set.on_epoch_begin(first, header, call)
|
||||
}
|
||||
|
||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||
self.correct_set_by_number(0).1.genesis_epoch_data(header, call)
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, _first: bool, chain_head: &Header) -> Option<Vec<u8>> {
|
||||
let (set_block, set) = self.correct_set_by_number(chain_head.number());
|
||||
let first = set_block == chain_head.number();
|
||||
|
||||
set.is_epoch_end(first, chain_head)
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, _first: bool, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
let (set_block, set) = self.correct_set_by_number(header.number());
|
||||
let (next_set_block, _) = self.correct_set_by_number(header.number() + 1);
|
||||
let first = set_block == header.number();
|
||||
|
||||
// multi-set transitions require epoch changes.
|
||||
if next_set_block != set_block {
|
||||
return EpochChange::Yes(next_set_block);
|
||||
set.signals_epoch_end(first, header, block, receipts)
|
||||
}
|
||||
|
||||
match set.is_epoch_end(header, block, receipts) {
|
||||
EpochChange::Yes(num) => EpochChange::Yes(set_block + num),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
fn epoch_set(&self, _first: bool, engine: &Engine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option<H256>), ::error::Error> {
|
||||
let (set_block, set) = self.correct_set_by_number(number);
|
||||
let first = set_block == number;
|
||||
|
||||
fn epoch_proof(&self, header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
|
||||
let (set_block, set) = self.correct_set_by_number(header.number());
|
||||
let (next_set_block, next_set) = self.correct_set_by_number(header.number() + 1);
|
||||
|
||||
if next_set_block != set_block {
|
||||
return next_set.epoch_proof(header, caller);
|
||||
}
|
||||
|
||||
set.epoch_proof(header, caller)
|
||||
}
|
||||
|
||||
fn epoch_set(&self, header: &Header, proof: &[u8]) -> Result<(u64, super::SimpleList), ::error::Error> {
|
||||
// "multi" epoch is the inner set's epoch plus the transition block to that set.
|
||||
// ensures epoch increases monotonically.
|
||||
let (set_block, set) = self.correct_set_by_number(header.number());
|
||||
let (next_set_block, next_set) = self.correct_set_by_number(header.number() + 1);
|
||||
|
||||
// this block kicks off a new validator set -- get the validator set
|
||||
// starting there.
|
||||
if next_set_block != set_block {
|
||||
let (inner_epoch, list) = next_set.epoch_set(header, proof)?;
|
||||
Ok((next_set_block + inner_epoch, list))
|
||||
} else {
|
||||
let (inner_epoch, list) = set.epoch_set(header, proof)?;
|
||||
Ok((set_block + inner_epoch, list))
|
||||
}
|
||||
set.epoch_set(first, engine, number, proof)
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, bh: &H256, address: &Address, caller: &Call) -> bool {
|
||||
@ -132,12 +121,12 @@ impl ValidatorSet for Multi {
|
||||
.map_or_else(usize::max_value, |set| set.count_with_caller(bh, caller))
|
||||
}
|
||||
|
||||
fn report_malicious(&self, validator: &Address, block: BlockNumber, proof: Bytes) {
|
||||
self.correct_set_by_number(block).1.report_malicious(validator, block, proof);
|
||||
fn report_malicious(&self, validator: &Address, set_block: BlockNumber, block: BlockNumber, proof: Bytes) {
|
||||
self.correct_set_by_number(set_block).1.report_malicious(validator, set_block, block, proof);
|
||||
}
|
||||
|
||||
fn report_benign(&self, validator: &Address, block: BlockNumber) {
|
||||
self.correct_set_by_number(block).1.report_benign(validator, block);
|
||||
fn report_benign(&self, validator: &Address, set_block: BlockNumber, block: BlockNumber) {
|
||||
self.correct_set_by_number(set_block).1.report_benign(validator, set_block, block);
|
||||
}
|
||||
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
@ -153,18 +142,24 @@ impl ValidatorSet for Multi {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use types::ids::BlockId;
|
||||
use spec::Spec;
|
||||
use account_provider::AccountProvider;
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use engines::EpochChange;
|
||||
use engines::validator_set::ValidatorSet;
|
||||
use ethkey::Secret;
|
||||
use header::Header;
|
||||
use miner::MinerService;
|
||||
use spec::Spec;
|
||||
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
|
||||
use types::ids::BlockId;
|
||||
use util::*;
|
||||
|
||||
use super::Multi;
|
||||
|
||||
#[test]
|
||||
fn uses_current_set() {
|
||||
::env_logger::init().unwrap();
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let s0: Secret = "0".sha3().into();
|
||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||
@ -205,4 +200,39 @@ mod tests {
|
||||
sync_client.flush_queue();
|
||||
assert_eq!(sync_client.chain_info().best_block_number, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_to_fixed_list_instant() {
|
||||
use super::super::SimpleList;
|
||||
|
||||
let mut map: BTreeMap<_, Box<ValidatorSet>> = BTreeMap::new();
|
||||
let list1: Vec<_> = (0..10).map(|_| Address::random()).collect();
|
||||
let list2 = {
|
||||
let mut list = list1.clone();
|
||||
list.push(Address::random());
|
||||
list
|
||||
};
|
||||
|
||||
map.insert(0, Box::new(SimpleList::new(list1)));
|
||||
map.insert(500, Box::new(SimpleList::new(list2)));
|
||||
|
||||
let multi = Multi::new(map);
|
||||
|
||||
let mut header = Header::new();
|
||||
header.set_number(499);
|
||||
|
||||
match multi.signals_epoch_end(false, &header, None, None) {
|
||||
EpochChange::No => {},
|
||||
_ => panic!("Expected no epoch signal change."),
|
||||
}
|
||||
assert!(multi.is_epoch_end(false, &header).is_none());
|
||||
|
||||
header.set_number(500);
|
||||
|
||||
match multi.signals_epoch_end(false, &header, None, None) {
|
||||
EpochChange::No => {},
|
||||
_ => panic!("Expected no epoch signal change."),
|
||||
}
|
||||
assert!(multi.is_epoch_end(false, &header).is_some());
|
||||
}
|
||||
}
|
||||
|
@ -22,21 +22,23 @@ use native_contracts::ValidatorSet as Provider;
|
||||
|
||||
use util::*;
|
||||
use util::cache::MemoryLruCache;
|
||||
use rlp::{UntrustedRlp, RlpStream};
|
||||
|
||||
use basic_types::LogBloom;
|
||||
use client::{Client, BlockChainClient};
|
||||
use engines::Call;
|
||||
use engines::{Call, Engine};
|
||||
use header::Header;
|
||||
use ids::BlockId;
|
||||
use log_entry::LogEntry;
|
||||
use receipt::Receipt;
|
||||
|
||||
use super::ValidatorSet;
|
||||
use super::{SystemCall, ValidatorSet};
|
||||
use super::simple_list::SimpleList;
|
||||
|
||||
const MEMOIZE_CAPACITY: usize = 500;
|
||||
|
||||
// TODO: ethabi should be able to generate this.
|
||||
const EVENT_NAME: &'static [u8] = &*b"ValidatorsChanged(bytes32,uint256,address[])";
|
||||
const EVENT_NAME: &'static [u8] = &*b"InitiateChange(bytes32,address[])";
|
||||
|
||||
lazy_static! {
|
||||
static ref EVENT_NAME_HASH: H256 = EVENT_NAME.sha3();
|
||||
@ -50,12 +52,66 @@ pub struct ValidatorSafeContract {
|
||||
client: RwLock<Option<Weak<Client>>>, // TODO [keorn]: remove
|
||||
}
|
||||
|
||||
fn encode_proof(nonce: U256, validators: &[Address]) -> Bytes {
|
||||
use rlp::RlpStream;
|
||||
|
||||
// first proof is just a state proof call of `getValidators` at header's state.
|
||||
fn encode_first_proof(header: &Header, state_items: &[Vec<u8>]) -> Bytes {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append(&nonce).append_list(validators);
|
||||
stream.drain().to_vec()
|
||||
stream.append(header).begin_list(state_items.len());
|
||||
for item in state_items {
|
||||
stream.append(item);
|
||||
}
|
||||
|
||||
stream.out()
|
||||
}
|
||||
|
||||
fn decode_first_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec<DBValue>), ::error::Error> {
|
||||
let header = rlp.val_at(0)?;
|
||||
let state_items = rlp.at(1)?.iter().map(|x| {
|
||||
let mut val = DBValue::new();
|
||||
val.append_slice(x.data()?);
|
||||
Ok(val)
|
||||
}).collect::<Result<_, ::error::Error>>()?;
|
||||
|
||||
Ok((header, state_items))
|
||||
}
|
||||
|
||||
// inter-contract proofs are a header and receipts.
|
||||
// checking will involve ensuring that the receipts match the header and
|
||||
// extracting the validator set from the receipts.
|
||||
fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append(header).append_list(receipts);
|
||||
stream.drain().into_vec()
|
||||
}
|
||||
|
||||
fn decode_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec<Receipt>), ::error::Error> {
|
||||
Ok((rlp.val_at(0)?, rlp.list_at(1)?))
|
||||
}
|
||||
|
||||
// given a provider and caller, generate proof. this will just be a state proof
|
||||
// of `getValidators`.
|
||||
fn prove_initial(provider: &Provider, header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
|
||||
use std::cell::RefCell;
|
||||
|
||||
let epoch_proof = RefCell::new(None);
|
||||
let res = {
|
||||
let caller = |a, d| {
|
||||
let (result, proof) = caller(a, d)?;
|
||||
*epoch_proof.borrow_mut() = Some(encode_first_proof(header, &proof));
|
||||
Ok(result)
|
||||
};
|
||||
|
||||
provider.get_validators(caller)
|
||||
.wait()
|
||||
};
|
||||
|
||||
res.map(|validators| {
|
||||
let proof = epoch_proof.into_inner().expect("epoch_proof always set after call; qed");
|
||||
|
||||
trace!(target: "engine", "obtained proof for initial set: {} validators, {} bytes",
|
||||
validators.len(), proof.len());
|
||||
|
||||
proof
|
||||
})
|
||||
}
|
||||
|
||||
impl ValidatorSafeContract {
|
||||
@ -70,6 +126,7 @@ impl ValidatorSafeContract {
|
||||
|
||||
/// Queries the state and gets the set of validators.
|
||||
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
|
||||
let caller = move |a, d| caller(a, d).map(|x| x.0);
|
||||
match self.provider.get_validators(caller).wait() {
|
||||
Ok(new) => {
|
||||
debug!(target: "engine", "Set of validators obtained: {:?}", new);
|
||||
@ -82,17 +139,6 @@ impl ValidatorSafeContract {
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries for the current validator set transition nonce.
|
||||
fn get_nonce(&self, caller: &Call) -> Option<::util::U256> {
|
||||
match self.provider.transition_nonce(caller).wait() {
|
||||
Ok(nonce) => Some(nonce),
|
||||
Err(s) => {
|
||||
debug!(target: "engine", "Unable to fetch transition nonce: {}", s);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Whether the header matches the expected bloom.
|
||||
//
|
||||
// The expected log should have 3 topics:
|
||||
@ -109,45 +155,31 @@ impl ValidatorSafeContract {
|
||||
//
|
||||
// The log data is an array of all new validator addresses.
|
||||
fn expected_bloom(&self, header: &Header) -> LogBloom {
|
||||
let topics = vec![*EVENT_NAME_HASH, *header.parent_hash()];
|
||||
|
||||
debug!(target: "engine", "Expected topics for header {}: {:?}",
|
||||
header.hash(), topics);
|
||||
|
||||
LogEntry {
|
||||
address: self.address,
|
||||
topics: vec![*EVENT_NAME_HASH, *header.parent_hash()],
|
||||
topics: topics,
|
||||
data: Vec::new(), // irrelevant for bloom.
|
||||
}.bloom()
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for ValidatorSafeContract {
|
||||
fn default_caller(&self, id: BlockId) -> Box<Call> {
|
||||
let client = self.client.read().clone();
|
||||
Box::new(move |addr, data| client.as_ref()
|
||||
.and_then(Weak::upgrade)
|
||||
.ok_or("No client!".into())
|
||||
.and_then(|c| c.call_contract(id, addr, data)))
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, header: &Header, _block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
let bloom = self.expected_bloom(header);
|
||||
let header_bloom = header.log_bloom();
|
||||
|
||||
if &bloom & header_bloom != bloom { return ::engines::EpochChange::No }
|
||||
|
||||
match receipts {
|
||||
None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts),
|
||||
Some(receipts) => {
|
||||
// check receipts for log event. bloom should be `expected_bloom` for the
|
||||
// header the receipts correspond to.
|
||||
fn extract_from_event(&self, bloom: LogBloom, header: &Header, receipts: &[Receipt]) -> Option<SimpleList> {
|
||||
let check_log = |log: &LogEntry| {
|
||||
log.address == self.address &&
|
||||
log.topics.len() == 3 &&
|
||||
log.topics.len() == 2 &&
|
||||
log.topics[0] == *EVENT_NAME_HASH &&
|
||||
log.topics[1] == *header.parent_hash()
|
||||
// don't have anything to compare nonce to yet.
|
||||
};
|
||||
|
||||
let event = Provider::contract(&self.provider)
|
||||
.event("ValidatorsChanged".into())
|
||||
.expect("Contract known ahead of time to have `ValidatorsChanged` event; qed");
|
||||
.event("InitiateChange".into())
|
||||
.expect("Contract known ahead of time to have `InitiateChange` event; qed");
|
||||
|
||||
// iterate in reverse because only the _last_ change in a given
|
||||
// block actually has any effect.
|
||||
@ -159,61 +191,180 @@ impl ValidatorSet for ValidatorSafeContract {
|
||||
.filter(move |l| check_log(l))
|
||||
.filter_map(|log| {
|
||||
let topics = log.topics.iter().map(|x| x.0.clone()).collect();
|
||||
match event.decode_log(topics, log.data.clone()) {
|
||||
Ok(decoded) => Some(decoded),
|
||||
Err(_) => None,
|
||||
}
|
||||
event.decode_log(topics, log.data.clone()).ok()
|
||||
});
|
||||
|
||||
match decoded_events.next() {
|
||||
None => ::engines::EpochChange::No,
|
||||
None => None,
|
||||
Some(matched_event) => {
|
||||
|
||||
// decode log manually until the native contract generator is
|
||||
// good enough to do it for us.
|
||||
let &(_, _, ref nonce_token) = &matched_event.params[1];
|
||||
let &(_, _, ref validators_token) = &matched_event.params[2];
|
||||
let &(_, _, ref validators_token) = &matched_event.params[1];
|
||||
|
||||
let nonce: Option<U256> = nonce_token.clone().to_uint()
|
||||
.map(H256).map(Into::into);
|
||||
let validators = validators_token.clone().to_array()
|
||||
.and_then(|a| a.into_iter()
|
||||
.map(|x| x.to_address().map(H160))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
);
|
||||
)
|
||||
.map(SimpleList::new);
|
||||
|
||||
match (nonce, validators) {
|
||||
(Some(nonce), Some(_)) => {
|
||||
let new_epoch = nonce.low_u64();
|
||||
::engines::EpochChange::Yes(new_epoch)
|
||||
}
|
||||
_ => {
|
||||
if validators.is_none() {
|
||||
debug!(target: "engine", "Successfully decoded log turned out to be bad.");
|
||||
::engines::EpochChange::No
|
||||
}
|
||||
|
||||
trace!(target: "engine", "decoded log. validators: {:?}", validators);
|
||||
|
||||
validators
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for ValidatorSafeContract {
|
||||
fn default_caller(&self, id: BlockId) -> Box<Call> {
|
||||
let client = self.client.read().clone();
|
||||
Box::new(move |addr, data| client.as_ref()
|
||||
.and_then(Weak::upgrade)
|
||||
.ok_or("No client!".into())
|
||||
.and_then(|c| c.call_contract(id, addr, data))
|
||||
.map(|out| (out, Vec::new()))) // generate no proofs in general
|
||||
}
|
||||
|
||||
fn on_epoch_begin(&self, first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> {
|
||||
if first { return Ok(()) } // only signalled changes need to be noted.
|
||||
|
||||
self.provider.finalize_change(caller)
|
||||
.wait()
|
||||
.map_err(::engines::EngineError::FailedSystemCall)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||
prove_initial(&self.provider, header, call)
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
||||
None // no immediate transitions to contract.
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, first: bool, header: &Header, _block: Option<&[u8]>, receipts: Option<&[Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
// transition to the first block of a contract requires finality but has no log event.
|
||||
if first {
|
||||
debug!(target: "engine", "signalling transition to fresh contract.");
|
||||
let (provider, header) = (self.provider.clone(), header.clone());
|
||||
let with_caller: Box<Fn(&Call) -> _> = Box::new(move |caller| prove_initial(&provider, &header, caller));
|
||||
return ::engines::EpochChange::Yes(::engines::Proof::WithState(with_caller))
|
||||
}
|
||||
|
||||
// otherwise, we're checking for logs.
|
||||
let bloom = self.expected_bloom(header);
|
||||
let header_bloom = header.log_bloom();
|
||||
|
||||
if &bloom & header_bloom != bloom { return ::engines::EpochChange::No }
|
||||
|
||||
trace!(target: "engine", "detected epoch change event bloom");
|
||||
|
||||
match receipts {
|
||||
None => ::engines::EpochChange::Unsure(::engines::Unsure::NeedsReceipts),
|
||||
Some(receipts) => match self.extract_from_event(bloom, header, receipts) {
|
||||
None => ::engines::EpochChange::No,
|
||||
Some(_) => {
|
||||
debug!(target: "engine", "signalling transition within contract");
|
||||
|
||||
let proof = encode_proof(&header, receipts);
|
||||
::engines::EpochChange::Yes(::engines::Proof::Known(proof))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// the proof we generate is an RLP list containing two parts.
|
||||
// (nonce, validators)
|
||||
fn epoch_proof(&self, _header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
|
||||
match (self.get_nonce(caller), self.get_list(caller)) {
|
||||
(Some(nonce), Some(list)) => Ok(encode_proof(nonce, &list.into_inner())),
|
||||
_ => Err("Caller insufficient to generate validator proof.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn epoch_set(&self, _header: &Header, proof: &[u8]) -> Result<(u64, SimpleList), ::error::Error> {
|
||||
use rlp::UntrustedRlp;
|
||||
fn epoch_set(&self, first: bool, engine: &Engine, _number: ::header::BlockNumber, proof: &[u8])
|
||||
-> Result<(SimpleList, Option<H256>), ::error::Error>
|
||||
{
|
||||
use transaction::{Action, Transaction};
|
||||
|
||||
let rlp = UntrustedRlp::new(proof);
|
||||
let nonce: u64 = rlp.val_at(0)?;
|
||||
let validators: Vec<Address> = rlp.list_at(1)?;
|
||||
|
||||
Ok((nonce, SimpleList::new(validators)))
|
||||
if first {
|
||||
trace!(target: "engine", "Recovering initial epoch set");
|
||||
|
||||
// TODO: match client contract_call_tx more cleanly without duplication.
|
||||
const PROVIDED_GAS: u64 = 50_000_000;
|
||||
|
||||
let (old_header, state_items) = decode_first_proof(&rlp)?;
|
||||
let old_hash = old_header.hash();
|
||||
|
||||
let env_info = ::env_info::EnvInfo {
|
||||
number: old_header.number(),
|
||||
author: *old_header.author(),
|
||||
difficulty: *old_header.difficulty(),
|
||||
gas_limit: PROVIDED_GAS.into(),
|
||||
timestamp: old_header.timestamp(),
|
||||
last_hashes: {
|
||||
// this will break if we don't inclue all 256 last hashes.
|
||||
let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect();
|
||||
last_hashes[255] = *old_header.parent_hash();
|
||||
Arc::new(last_hashes)
|
||||
},
|
||||
gas_used: 0.into(),
|
||||
};
|
||||
|
||||
// check state proof using given engine.
|
||||
let number = old_header.number();
|
||||
let addresses = self.provider.get_validators(move |a, d| {
|
||||
let from = Address::default();
|
||||
let tx = Transaction {
|
||||
nonce: engine.account_start_nonce(number),
|
||||
action: Action::Call(a),
|
||||
gas: PROVIDED_GAS.into(),
|
||||
gas_price: U256::default(),
|
||||
value: U256::default(),
|
||||
data: d,
|
||||
}.fake_sign(from);
|
||||
|
||||
let res = ::state::check_proof(
|
||||
&state_items,
|
||||
*old_header.state_root(),
|
||||
&tx,
|
||||
engine,
|
||||
&env_info,
|
||||
);
|
||||
|
||||
match res {
|
||||
::state::ProvedExecution::BadProof => Err("Bad proof".into()),
|
||||
::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)),
|
||||
::state::ProvedExecution::Complete(e) => Ok(e.output),
|
||||
}
|
||||
}).wait().map_err(::engines::EngineError::InsufficientProof)?;
|
||||
|
||||
trace!(target: "engine", "extracted epoch set at #{}: {} addresses",
|
||||
number, addresses.len());
|
||||
|
||||
Ok((SimpleList::new(addresses), Some(old_hash)))
|
||||
} else {
|
||||
let (old_header, receipts) = decode_proof(&rlp)?;
|
||||
|
||||
// ensure receipts match header.
|
||||
// TODO: optimize? these were just decoded.
|
||||
let found_root = ::util::triehash::ordered_trie_root(
|
||||
receipts.iter().map(::rlp::encode).map(|x| x.to_vec())
|
||||
);
|
||||
if found_root != *old_header.receipts_root() {
|
||||
return Err(::error::BlockError::InvalidReceiptsRoot(
|
||||
Mismatch { expected: *old_header.receipts_root(), found: found_root }
|
||||
).into());
|
||||
}
|
||||
|
||||
let bloom = self.expected_bloom(&old_header);
|
||||
|
||||
match self.extract_from_event(bloom, &old_header, &receipts) {
|
||||
Some(list) => Ok((list, Some(old_header.hash()))),
|
||||
None => Err(::engines::EngineError::InsufficientProof("No log event in proof.".into()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, block_hash: &H256, address: &Address, caller: &Call) -> bool {
|
||||
@ -371,6 +522,7 @@ mod tests {
|
||||
let last_hash = client.best_block_header().hash();
|
||||
let mut new_header = Header::default();
|
||||
new_header.set_parent_hash(last_hash);
|
||||
new_header.set_number(1); // so the validator set looks for a log.
|
||||
|
||||
// first, try without the parent hash.
|
||||
let mut event = LogEntry {
|
||||
@ -380,12 +532,35 @@ mod tests {
|
||||
};
|
||||
|
||||
new_header.set_log_bloom(event.bloom());
|
||||
assert_eq!(engine.is_epoch_end(&new_header, None, None), EpochChange::No);
|
||||
match engine.signals_epoch_end(&new_header, None, None) {
|
||||
EpochChange::No => {},
|
||||
_ => panic!("Expected bloom to be unrecognized."),
|
||||
};
|
||||
|
||||
// with the last hash, it should need the receipts.
|
||||
event.topics.push(last_hash);
|
||||
new_header.set_log_bloom(event.bloom());
|
||||
assert_eq!(engine.is_epoch_end(&new_header, None, None),
|
||||
EpochChange::Unsure(Unsure::NeedsReceipts));
|
||||
|
||||
match engine.signals_epoch_end(&new_header, None, None) {
|
||||
EpochChange::Unsure(Unsure::NeedsReceipts) => {},
|
||||
_ => panic!("Expected bloom to be recognized."),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initial_contract_is_signal() {
|
||||
use header::Header;
|
||||
use engines::{EpochChange, Proof};
|
||||
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
|
||||
let engine = client.engine().clone();
|
||||
|
||||
let mut new_header = Header::default();
|
||||
new_header.set_number(0); // so the validator set doesn't look for a log
|
||||
|
||||
match engine.signals_epoch_end(&new_header, None, None) {
|
||||
EpochChange::Yes(Proof::WithState(_)) => {},
|
||||
_ => panic!("Expected state to be required to prove initial signal"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use util::{H256, Address, HeapSizeOf};
|
||||
|
||||
use engines::Call;
|
||||
use header::Header;
|
||||
use engines::{Call, Engine};
|
||||
use header::{BlockNumber, Header};
|
||||
use super::ValidatorSet;
|
||||
|
||||
/// Validator set containing a known set of addresses.
|
||||
@ -42,6 +42,20 @@ impl SimpleList {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for SimpleList {
|
||||
type Target = [Address];
|
||||
|
||||
fn deref(&self) -> &[Address] { &self.validators }
|
||||
}
|
||||
|
||||
impl From<Vec<Address>> for SimpleList {
|
||||
fn from(validators: Vec<Address>) -> Self {
|
||||
SimpleList {
|
||||
validators: validators,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for SimpleList {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.validators.heap_size_of_children()
|
||||
@ -53,18 +67,21 @@ impl ValidatorSet for SimpleList {
|
||||
Box::new(|_, _| Err("Simple list doesn't require calls.".into()))
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>)
|
||||
fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
||||
match first {
|
||||
true => Some(Vec::new()), // allow transition to fixed list, and instantly
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
::engines::EpochChange::No
|
||||
}
|
||||
|
||||
fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result<Vec<u8>, String> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(u64, SimpleList), ::error::Error> {
|
||||
Ok((0, self.clone()))
|
||||
fn epoch_set(&self, _first: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
|
||||
Ok((self.clone(), None))
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, _bh: &H256, address: &Address, _: &Call) -> bool {
|
||||
@ -73,6 +90,11 @@ impl ValidatorSet for SimpleList {
|
||||
|
||||
fn get_with_caller(&self, _bh: &H256, nonce: usize, _: &Call) -> Address {
|
||||
let validator_n = self.validators.len();
|
||||
|
||||
if validator_n == 0 {
|
||||
panic!("Cannot operate with an empty validator set.");
|
||||
}
|
||||
|
||||
self.validators.get(nonce % validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
use util::{Arc, Bytes, H256, Address, HeapSizeOf};
|
||||
|
||||
use engines::Call;
|
||||
use engines::{Call, Engine};
|
||||
use header::{Header, BlockNumber};
|
||||
use super::{ValidatorSet, SimpleList};
|
||||
|
||||
@ -52,18 +52,16 @@ impl ValidatorSet for TestSet {
|
||||
Box::new(|_, _| Err("Test set doesn't require calls.".into()))
|
||||
}
|
||||
|
||||
fn is_epoch_end(&self, _header: &Header, _block: Option<&[u8]>, _receipts: Option<&[::receipt::Receipt]>)
|
||||
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> { None }
|
||||
|
||||
fn signals_epoch_end(&self, _: bool, _: &Header, _: Option<&[u8]>, _: Option<&[::receipt::Receipt]>)
|
||||
-> ::engines::EpochChange
|
||||
{
|
||||
::engines::EpochChange::No
|
||||
}
|
||||
|
||||
fn epoch_proof(&self, _header: &Header, _caller: &Call) -> Result<Vec<u8>, String> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn epoch_set(&self, _header: &Header, _: &[u8]) -> Result<(u64, SimpleList), ::error::Error> {
|
||||
Ok((0, self.validator.clone()))
|
||||
fn epoch_set(&self, _: bool, _: &Engine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option<H256>), ::error::Error> {
|
||||
Ok((self.validator.clone(), None))
|
||||
}
|
||||
|
||||
fn contains_with_caller(&self, bh: &H256, address: &Address, _: &Call) -> bool {
|
||||
@ -78,11 +76,11 @@ impl ValidatorSet for TestSet {
|
||||
1
|
||||
}
|
||||
|
||||
fn report_malicious(&self, _validator: &Address, block: BlockNumber, _proof: Bytes) {
|
||||
fn report_malicious(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber, _proof: Bytes) {
|
||||
self.last_malicious.store(block as usize, AtomicOrdering::SeqCst)
|
||||
}
|
||||
|
||||
fn report_benign(&self, _validator: &Address, block: BlockNumber) {
|
||||
fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
||||
self.last_benign.store(block as usize, AtomicOrdering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +166,6 @@ impl Ethash {
|
||||
// in the future, we might move the Ethash epoch
|
||||
// caching onto this mechanism as well.
|
||||
impl ::engines::EpochVerifier for Arc<Ethash> {
|
||||
fn epoch_number(&self) -> u64 { 0 }
|
||||
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
|
||||
self.verify_block_unordered(header, None)
|
||||
@ -256,7 +255,12 @@ impl Engine for Arc<Ethash> {
|
||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number(), header.difficulty(), header.gas_limit());
|
||||
}
|
||||
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock, last_hashes: Arc<LastHashes>) -> Result<(), Error> {
|
||||
fn on_new_block(
|
||||
&self,
|
||||
block: &mut ExecutedBlock,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
_begins_epoch: bool,
|
||||
) -> Result<(), Error> {
|
||||
let parent_hash = block.fields().header.parent_hash().clone();
|
||||
::engines::common::push_last_hash(block, last_hashes, self, &parent_hash)?;
|
||||
if block.fields().header.number() == self.ethash_params.dao_hardfork_transition {
|
||||
@ -400,8 +404,8 @@ impl Engine for Arc<Ethash> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn epoch_verifier(&self, _header: &Header, _proof: &[u8]) -> Result<Box<::engines::EpochVerifier>, Error> {
|
||||
Ok(Box::new(self.clone()))
|
||||
fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ::engines::ConstructedVerifier<'a> {
|
||||
::engines::ConstructedVerifier::Trusted(Box::new(self.clone()))
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
@ -536,7 +540,7 @@ impl Header {
|
||||
|
||||
/// Set the nonce and mix hash fields of the header.
|
||||
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
|
||||
self.set_seal(vec![rlp::encode(mix_hash).to_vec(), rlp::encode(nonce).to_vec()]);
|
||||
self.set_seal(vec![rlp::encode(mix_hash).into_vec(), rlp::encode(nonce).into_vec()]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,7 +563,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap());
|
||||
}
|
||||
@ -571,7 +575,7 @@ mod tests {
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let mut uncle = Header::new();
|
||||
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
|
||||
uncle.set_author(uncle_author);
|
||||
@ -618,7 +622,7 @@ mod tests {
|
||||
fn can_do_difficulty_verification_fail() {
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
|
||||
@ -633,7 +637,7 @@ mod tests {
|
||||
fn can_do_proof_of_work_verification_fail() {
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
|
||||
let verify_result = engine.verify_block_basic(&header, None);
|
||||
@ -663,7 +667,7 @@ mod tests {
|
||||
fn can_do_seal256_verification_fail() {
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
|
||||
match verify_result {
|
||||
@ -677,7 +681,7 @@ mod tests {
|
||||
fn can_do_proof_of_work_unordered_verification_fail() {
|
||||
let engine = new_morden().engine;
|
||||
let mut header: Header = Header::default();
|
||||
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
|
||||
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
|
||||
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
|
||||
|
||||
let verify_result = engine.verify_block_unordered(&header, None);
|
||||
|
@ -329,7 +329,7 @@ mod tests {
|
||||
let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap();
|
||||
|
||||
let header: Header = rlp::decode(&header_rlp);
|
||||
let encoded_header = rlp::encode(&header).to_vec();
|
||||
let encoded_header = rlp::encode(&header).into_vec();
|
||||
|
||||
assert_eq!(header_rlp, encoded_header);
|
||||
}
|
||||
|
@ -32,6 +32,6 @@ impl SimpleMigration for V8 {
|
||||
|
||||
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
self.0.tick();
|
||||
Some((key,UntrustedRlp::new(&value).compress(RlpType::Blocks).to_vec()))
|
||||
Some((key,UntrustedRlp::new(&value).compress(RlpType::Blocks).into_vec()))
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,7 @@ impl SimpleMigration for ToV6 {
|
||||
|
||||
fn version(&self) -> u32 { 6 }
|
||||
|
||||
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
|
||||
fn simple_migrate(&mut self, mut key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
//// at this version all extras keys are 33 bytes long.
|
||||
if key.len() == 33 {
|
||||
// block details key changes:
|
||||
@ -72,19 +71,21 @@ impl SimpleMigration for ToV6 {
|
||||
// - index is moved to the front
|
||||
// - index is changed 4 -> 3
|
||||
if key[32] == 4 {
|
||||
key.reverse();
|
||||
// i have no idea why it was reversed
|
||||
let reverse = key.into_iter().rev().collect::<Vec<_>>();
|
||||
let mut result = [0u8; 6];
|
||||
let reverse = key;
|
||||
let result = vec![
|
||||
// new extras index is 3
|
||||
result[0] = 3;
|
||||
3,
|
||||
// 9th (+ prefix) byte was the level. Now it's second.
|
||||
result[1] = reverse[9];
|
||||
result[2] = reverse[4];
|
||||
result[3] = reverse[3];
|
||||
result[4] = reverse[2];
|
||||
result[5] = reverse[1];
|
||||
reverse[9],
|
||||
reverse[4],
|
||||
reverse[3],
|
||||
reverse[2],
|
||||
reverse[1],
|
||||
];
|
||||
|
||||
return Some((result.to_vec(), value));
|
||||
return Some((result, value));
|
||||
}
|
||||
|
||||
// blocks receipts key changes:
|
||||
|
@ -153,7 +153,7 @@ impl OverlayRecentV7 {
|
||||
// and commit the altered entries.
|
||||
fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
|
||||
if let Some(val) = source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)? {
|
||||
batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest)?;
|
||||
batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().into_vec(), dest)?;
|
||||
|
||||
let mut era = decode::<u64>(&val);
|
||||
loop {
|
||||
|
@ -104,7 +104,7 @@ impl Migration for ToV10 {
|
||||
let mut batch = Batch::new(config, col);
|
||||
for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) {
|
||||
self.progress.tick();
|
||||
batch.insert(key.to_vec(), value.to_vec(), dest)?;
|
||||
batch.insert(key.into_vec(), value.into_vec(), dest)?;
|
||||
}
|
||||
batch.commit(dest)?;
|
||||
|
||||
|
@ -51,7 +51,6 @@ impl ToV9 {
|
||||
}
|
||||
|
||||
impl Migration for ToV9 {
|
||||
|
||||
fn columns(&self) -> Option<u32> { Some(5) }
|
||||
|
||||
fn version(&self) -> u32 { 9 }
|
||||
@ -63,17 +62,17 @@ impl Migration for ToV9 {
|
||||
self.progress.tick();
|
||||
match self.extract {
|
||||
Extract::Header => {
|
||||
batch.insert(key.to_vec(), Rlp::new(&value).at(0).as_raw().to_vec(), dest)?
|
||||
batch.insert(key.into_vec(), Rlp::new(&value).at(0).as_raw().to_vec(), dest)?
|
||||
},
|
||||
Extract::Body => {
|
||||
let mut body = RlpStream::new_list(2);
|
||||
let block_rlp = Rlp::new(&value);
|
||||
body.append_raw(block_rlp.at(1).as_raw(), 1);
|
||||
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
||||
batch.insert(key.to_vec(), body.out(), dest)?
|
||||
batch.insert(key.into_vec(), body.out(), dest)?
|
||||
},
|
||||
Extract::All => {
|
||||
batch.insert(key.to_vec(), value.to_vec(), dest)?
|
||||
batch.insert(key.into_vec(), value.into_vec(), dest)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ impl JobDispatcher for StratumJobDispatcher {
|
||||
);
|
||||
|
||||
self.with_core_void(|client, miner| {
|
||||
let seal = vec![encode(&payload.mix_hash).to_vec(), encode(&payload.nonce).to_vec()];
|
||||
let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()];
|
||||
if let Err(e) = miner.submit_seal(&*client, payload.pow_hash, seal) {
|
||||
warn!(target: "stratum", "submit_seal error: {:?}", e);
|
||||
};
|
||||
|
@ -121,7 +121,7 @@ pub fn to_fat_rlps(account_hash: &H256, acc: &BasicAccount, acct_db: &AccountDB,
|
||||
let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2));
|
||||
chunks.push(stream.out());
|
||||
target_chunk_size = max_chunk_size;
|
||||
leftover = Some(pair.to_vec());
|
||||
leftover = Some(pair.into_vec());
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -190,7 +190,7 @@ mod tests {
|
||||
|
||||
let receipts_root = b.header.receipts_root().clone();
|
||||
b.header.set_transactions_root(::util::triehash::ordered_trie_root(
|
||||
b.transactions.iter().map(::rlp::encode).map(|out| out.to_vec())
|
||||
b.transactions.iter().map(::rlp::encode).map(|out| out.into_vec())
|
||||
));
|
||||
|
||||
let encoded = encode_block(&b);
|
||||
|
@ -24,34 +24,28 @@ use super::{SnapshotComponents, Rebuilder, ChunkSink};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use blockchain::{BlockChain, BlockProvider, EpochTransition};
|
||||
use engines::{Engine, EpochVerifier};
|
||||
use env_info::EnvInfo;
|
||||
use blockchain::{BlockChain, BlockProvider};
|
||||
use engines::{Engine, EpochVerifier, EpochTransition};
|
||||
use ids::BlockId;
|
||||
use header::Header;
|
||||
use receipt::Receipt;
|
||||
use snapshot::{Error, ManifestData};
|
||||
use state_db::StateDB;
|
||||
|
||||
use itertools::{Position, Itertools};
|
||||
use rlp::{RlpStream, UntrustedRlp};
|
||||
use util::{Address, Bytes, H256, KeyValueDB, DBValue};
|
||||
use util::{Bytes, H256, KeyValueDB};
|
||||
|
||||
/// Snapshot creation and restoration for PoA chains.
|
||||
/// Chunk format:
|
||||
///
|
||||
/// [FLAG, [header, epoch_number, epoch data, state proof, last hashes], ...]
|
||||
/// [FLAG, [header, epoch data], ...]
|
||||
/// - Header data at which transition occurred,
|
||||
/// - epoch data (usually list of validators)
|
||||
/// - state items required to check epoch data
|
||||
/// - last 256 hashes before the transition; required for checking state changes.
|
||||
/// - epoch data (usually list of validators and proof of change)
|
||||
///
|
||||
/// FLAG is a bool: true for last chunk, false otherwise.
|
||||
///
|
||||
/// The last item of the last chunk will be a list containing data for the warp target block:
|
||||
/// [header, transactions, uncles, receipts, last_hashes, parent_td].
|
||||
/// If this block is not a transition block, the epoch data should be the same as that
|
||||
/// for the last transition.
|
||||
/// [header, transactions, uncles, receipts, parent_td].
|
||||
pub struct PoaSnapshot;
|
||||
|
||||
impl SnapshotComponents for PoaSnapshot {
|
||||
@ -68,35 +62,23 @@ impl SnapshotComponents for PoaSnapshot {
|
||||
let mut pending_size = 0;
|
||||
let mut rlps = Vec::new();
|
||||
|
||||
// TODO: this will become irrelevant after recent block hashes are moved into
|
||||
// the state. can we optimize it out in that case?
|
||||
let make_last_hashes = |parent_hash| chain.ancestry_iter(parent_hash)
|
||||
.into_iter()
|
||||
.flat_map(|inner| inner)
|
||||
.take(255)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (epoch_number, transition) in chain.epoch_transitions()
|
||||
for (_, transition) in chain.epoch_transitions()
|
||||
.take_while(|&(_, ref t)| t.block_number <= number)
|
||||
{
|
||||
// this can happen when our starting block is non-canonical.
|
||||
if transition.block_number == number && transition.block_hash != block_at {
|
||||
break
|
||||
}
|
||||
|
||||
let header = chain.block_header_data(&transition.block_hash)
|
||||
.ok_or(Error::BlockNotFound(transition.block_hash))?;
|
||||
|
||||
let last_hashes: Vec<_> = make_last_hashes(header.parent_hash());
|
||||
|
||||
let entry = {
|
||||
let mut entry_stream = RlpStream::new_list(5);
|
||||
let mut entry_stream = RlpStream::new_list(2);
|
||||
entry_stream
|
||||
.append_raw(&header.into_inner(), 1)
|
||||
.append(&epoch_number)
|
||||
.append(&transition.proof);
|
||||
|
||||
entry_stream.begin_list(transition.state_proof.len());
|
||||
for item in transition.state_proof {
|
||||
entry_stream.append(&&*item);
|
||||
}
|
||||
|
||||
entry_stream.append_list(&last_hashes);
|
||||
entry_stream.out()
|
||||
};
|
||||
|
||||
@ -121,16 +103,13 @@ impl SnapshotComponents for PoaSnapshot {
|
||||
.map(|d| d.total_difficulty)
|
||||
.ok_or(Error::BlockNotFound(block_at))?;
|
||||
|
||||
let last_hashes = make_last_hashes(*block.header.parent_hash());
|
||||
|
||||
rlps.push({
|
||||
let mut stream = RlpStream::new_list(6);
|
||||
let mut stream = RlpStream::new_list(5);
|
||||
stream
|
||||
.append(&block.header)
|
||||
.append_list(&block.transactions)
|
||||
.append_list(&block.uncles)
|
||||
.append(&receipts)
|
||||
.append_list(&last_hashes)
|
||||
.append(&parent_td);
|
||||
stream.out()
|
||||
});
|
||||
@ -153,7 +132,7 @@ impl SnapshotComponents for PoaSnapshot {
|
||||
db: db,
|
||||
had_genesis: false,
|
||||
unverified_firsts: Vec::new(),
|
||||
last_proofs: Vec::new(),
|
||||
last_epochs: Vec::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
@ -178,7 +157,7 @@ fn write_chunk(last: bool, chunk_data: &mut Vec<Bytes>, sink: &mut ChunkSink) ->
|
||||
// transition header is verifiable from the epoch data of the one prior.
|
||||
struct ChunkRebuilder {
|
||||
manifest: ManifestData,
|
||||
warp_target: Option<(Header, Vec<H256>)>,
|
||||
warp_target: Option<Header>,
|
||||
chain: BlockChain,
|
||||
db: Arc<KeyValueDB>,
|
||||
had_genesis: bool,
|
||||
@ -186,52 +165,16 @@ struct ChunkRebuilder {
|
||||
// sorted vectors of unverified first blocks in a chunk
|
||||
// and epoch data from last blocks in chunks.
|
||||
// verification for these will be done at the end.
|
||||
unverified_firsts: Vec<(u64, Header)>,
|
||||
last_proofs: Vec<(u64, Header, Bytes)>,
|
||||
unverified_firsts: Vec<(Header, Bytes, H256)>,
|
||||
last_epochs: Vec<(Header, Box<EpochVerifier>)>,
|
||||
}
|
||||
|
||||
// verified data.
|
||||
struct Verified {
|
||||
epoch_number: u64,
|
||||
epoch_transition: EpochTransition,
|
||||
header: Header,
|
||||
}
|
||||
|
||||
// make a transaction and env info.
|
||||
// TODO: hardcoded 50M to match constants in client.
|
||||
// would be nice to extract magic numbers, or better yet
|
||||
// off-chain transaction execution, into its own module.
|
||||
fn make_tx_and_env(
|
||||
engine: &Engine,
|
||||
addr: Address,
|
||||
data: Bytes,
|
||||
header: &Header,
|
||||
last_hashes: Arc<Vec<H256>>,
|
||||
) -> (::transaction::SignedTransaction, EnvInfo) {
|
||||
use transaction::{Action, Transaction};
|
||||
|
||||
let transaction = Transaction {
|
||||
nonce: engine.account_start_nonce(header.number()),
|
||||
action: Action::Call(addr),
|
||||
gas: 50_000_000.into(),
|
||||
gas_price: 0.into(),
|
||||
value: 0.into(),
|
||||
data: data,
|
||||
}.fake_sign(Default::default());
|
||||
|
||||
let env = EnvInfo {
|
||||
number: header.number(),
|
||||
author: *header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: *header.difficulty(),
|
||||
gas_limit: 50_000_000.into(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: 0.into(),
|
||||
};
|
||||
|
||||
(transaction, env)
|
||||
}
|
||||
|
||||
impl ChunkRebuilder {
|
||||
fn verify_transition(
|
||||
&mut self,
|
||||
@ -239,69 +182,49 @@ impl ChunkRebuilder {
|
||||
transition_rlp: UntrustedRlp,
|
||||
engine: &Engine,
|
||||
) -> Result<Verified, ::error::Error> {
|
||||
use engines::ConstructedVerifier;
|
||||
|
||||
// decode.
|
||||
let header: Header = transition_rlp.val_at(0)?;
|
||||
let epoch_number: u64 = transition_rlp.val_at(1)?;
|
||||
let epoch_data: Bytes = transition_rlp.val_at(2)?;
|
||||
let state_proof: Vec<DBValue> = transition_rlp.at(3)?
|
||||
.iter()
|
||||
.map(|x| Ok(DBValue::from_slice(x.data()?)))
|
||||
.collect::<Result<_, ::rlp::DecoderError>>()?;
|
||||
let last_hashes: Vec<H256> = transition_rlp.list_at(4)?;
|
||||
let last_hashes = Arc::new(last_hashes);
|
||||
let epoch_data: Bytes = transition_rlp.val_at(1)?;
|
||||
|
||||
trace!(target: "snapshot", "verifying transition to epoch {}", epoch_number);
|
||||
trace!(target: "snapshot", "verifying transition to epoch at block {}", header.number());
|
||||
|
||||
// check current transition against validators of last epoch.
|
||||
if let Some(verifier) = last_verifier.as_ref() {
|
||||
verifier.verify_heavy(&header)?;
|
||||
}
|
||||
|
||||
let new_verifier = match engine.epoch_verifier(&header, &epoch_data) {
|
||||
ConstructedVerifier::Trusted(v) => v,
|
||||
ConstructedVerifier::Unconfirmed(v, finality_proof, hash) => {
|
||||
match *last_verifier {
|
||||
Some(ref last) =>
|
||||
if last.check_finality_proof(finality_proof).map_or(true, |hashes| !hashes.contains(&hash))
|
||||
{
|
||||
// check the provided state proof actually leads to the
|
||||
// given epoch data.
|
||||
let caller = |addr, data| {
|
||||
use state::{check_proof, ProvedExecution};
|
||||
return Err(Error::BadEpochProof(header.number()).into());
|
||||
},
|
||||
None if header.number() != 0 => {
|
||||
// genesis never requires additional validation.
|
||||
|
||||
let (transaction, env_info) = make_tx_and_env(
|
||||
engine,
|
||||
addr,
|
||||
data,
|
||||
&header,
|
||||
last_hashes.clone(),
|
||||
);
|
||||
let idx = self.unverified_firsts
|
||||
.binary_search_by_key(&header.number(), |&(ref h, _, _)| h.number())
|
||||
.unwrap_or_else(|x| x);
|
||||
|
||||
let result = check_proof(
|
||||
&state_proof,
|
||||
header.state_root().clone(),
|
||||
&transaction,
|
||||
engine,
|
||||
&env_info,
|
||||
);
|
||||
|
||||
match result {
|
||||
ProvedExecution::Complete(executed) => Ok(executed.output),
|
||||
_ => Err("Bad state proof".into()),
|
||||
let entry = (header.clone(), finality_proof.to_owned(), hash);
|
||||
self.unverified_firsts.insert(idx, entry);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
ConstructedVerifier::Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let extracted_proof = engine.epoch_proof(&header, &caller)
|
||||
.map_err(|_| Error::BadEpochProof(epoch_number))?;
|
||||
|
||||
if extracted_proof != epoch_data {
|
||||
return Err(Error::BadEpochProof(epoch_number).into());
|
||||
}
|
||||
}
|
||||
|
||||
// create new epoch verifier.
|
||||
*last_verifier = Some(engine.epoch_verifier(&header, &epoch_data)?);
|
||||
*last_verifier = Some(new_verifier);
|
||||
|
||||
Ok(Verified {
|
||||
epoch_number: epoch_number,
|
||||
epoch_transition: EpochTransition {
|
||||
block_hash: header.hash(),
|
||||
block_number: header.number(),
|
||||
state_proof: state_proof,
|
||||
proof: epoch_data,
|
||||
},
|
||||
header: header,
|
||||
@ -327,6 +250,10 @@ impl Rebuilder for ChunkRebuilder {
|
||||
num_items - 1
|
||||
};
|
||||
|
||||
if num_transitions == 0 && !is_last_chunk {
|
||||
return Err(Error::WrongChunkFormat("Found non-last chunk without any data.".into()).into());
|
||||
}
|
||||
|
||||
let mut last_verifier = None;
|
||||
let mut last_number = None;
|
||||
for transition_rlp in rlp.iter().skip(1).take(num_transitions).with_position() {
|
||||
@ -356,41 +283,33 @@ impl Rebuilder for ChunkRebuilder {
|
||||
if is_first {
|
||||
// make sure the genesis transition was included,
|
||||
// but it doesn't need verification later.
|
||||
if verified.epoch_number == 0 && verified.header.number() == 0 {
|
||||
if verified.header.number() == 0 {
|
||||
if verified.header.hash() != self.chain.genesis_hash() {
|
||||
return Err(Error::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into());
|
||||
}
|
||||
|
||||
self.had_genesis = true;
|
||||
} else {
|
||||
let idx = self.unverified_firsts
|
||||
.binary_search_by_key(&verified.epoch_number, |&(a, _)| a)
|
||||
.unwrap_or_else(|x| x);
|
||||
|
||||
let entry = (verified.epoch_number, verified.header.clone());
|
||||
self.unverified_firsts.insert(idx, entry);
|
||||
}
|
||||
}
|
||||
if is_last {
|
||||
let idx = self.last_proofs
|
||||
.binary_search_by_key(&verified.epoch_number, |&(a, _, _)| a)
|
||||
let idx = self.last_epochs
|
||||
.binary_search_by_key(&verified.header.number(), |&(ref h, _)| h.number())
|
||||
.unwrap_or_else(|x| x);
|
||||
|
||||
let entry = (
|
||||
verified.epoch_number,
|
||||
verified.header.clone(),
|
||||
verified.epoch_transition.proof.clone()
|
||||
last_verifier.take().expect("last_verifier always set after verify_transition; qed"),
|
||||
);
|
||||
self.last_proofs.insert(idx, entry);
|
||||
self.last_epochs.insert(idx, entry);
|
||||
}
|
||||
|
||||
// write epoch transition into database.
|
||||
let mut batch = self.db.transaction();
|
||||
self.chain.insert_epoch_transition(&mut batch, verified.epoch_number,
|
||||
self.chain.insert_epoch_transition(&mut batch, verified.header.number(),
|
||||
verified.epoch_transition);
|
||||
self.db.write_buffered(batch);
|
||||
|
||||
trace!(target: "snapshot", "Verified epoch transition for epoch {}", verified.epoch_number);
|
||||
trace!(target: "snapshot", "Verified epoch transition for epoch at block {}", verified.header.number());
|
||||
}
|
||||
|
||||
if is_last_chunk {
|
||||
@ -413,84 +332,57 @@ impl Rebuilder for ChunkRebuilder {
|
||||
}
|
||||
}
|
||||
|
||||
let last_hashes: Vec<H256> = last_rlp.list_at(4)?;
|
||||
let parent_td: ::util::U256 = last_rlp.val_at(5)?;
|
||||
let parent_td: ::util::U256 = last_rlp.val_at(4)?;
|
||||
|
||||
let mut batch = self.db.transaction();
|
||||
self.chain.insert_unordered_block(&mut batch, &block_data, receipts, Some(parent_td), true, false);
|
||||
self.db.write_buffered(batch);
|
||||
|
||||
self.warp_target = Some((block.header, last_hashes));
|
||||
self.warp_target = Some(block.header);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finalize(&mut self, db: StateDB, engine: &Engine) -> Result<(), ::error::Error> {
|
||||
use state::State;
|
||||
|
||||
fn finalize(&mut self, _engine: &Engine) -> Result<(), ::error::Error> {
|
||||
if !self.had_genesis {
|
||||
return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into());
|
||||
}
|
||||
|
||||
let (target_header, target_last_hashes) = match self.warp_target.take() {
|
||||
let target_header = match self.warp_target.take() {
|
||||
Some(x) => x,
|
||||
None => return Err(Error::WrongChunkFormat("Warp target block not included.".into()).into()),
|
||||
};
|
||||
|
||||
// we store the last data even for the last chunk for easier verification
|
||||
// of warp target, but we don't store genesis transition data.
|
||||
// other than that, there should be a one-to-one correspondence of
|
||||
// chunk ends to chunk beginnings.
|
||||
if self.last_proofs.len() != self.unverified_firsts.len() + 1 {
|
||||
return Err(Error::WrongChunkFormat("More than one 'last' chunk".into()).into());
|
||||
}
|
||||
|
||||
// verify the first entries of chunks we couldn't before.
|
||||
let lasts_iter = self.last_proofs.iter().map(|&(_, ref hdr, ref proof)| (hdr, &proof[..]));
|
||||
let firsts_iter = self.unverified_firsts.iter().map(|&(_, ref hdr)| hdr);
|
||||
|
||||
for ((last_hdr, last_proof), first_hdr) in lasts_iter.zip(firsts_iter) {
|
||||
let verifier = engine.epoch_verifier(&last_hdr, &last_proof)?;
|
||||
verifier.verify_heavy(&first_hdr)?;
|
||||
// we store all last verifiers, but not all firsts.
|
||||
// match each unverified first epoch with a last epoch verifier.
|
||||
let mut lasts_reversed = self.last_epochs.iter().rev();
|
||||
for &(ref header, ref finality_proof, hash) in self.unverified_firsts.iter().rev() {
|
||||
let mut found = false;
|
||||
while let Some(&(ref last_header, ref last_verifier)) = lasts_reversed.next() {
|
||||
if last_header.number() < header.number() {
|
||||
if last_verifier.check_finality_proof(&finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) {
|
||||
return Err(Error::BadEpochProof(header.number()).into());
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// verify that the validator set of the warp target is the same as that of the
|
||||
// most recent transition. if the warp target was a transition itself,
|
||||
// `last_data` will still be correct
|
||||
let &(_, _, ref last_data) = self.last_proofs.last()
|
||||
.expect("last_proofs known to have at least one element by the check above; qed");
|
||||
if !found {
|
||||
return Err(Error::WrongChunkFormat("Inconsistent chunk ordering.".into()).into());
|
||||
}
|
||||
}
|
||||
|
||||
let target_last_hashes = Arc::new(target_last_hashes);
|
||||
let caller = |addr, data| {
|
||||
use executive::{Executive, TransactOptions};
|
||||
// verify that the warp target verifies correctly the
|
||||
// most recent epoch. if the warp target was a transition itself,
|
||||
// it's already verified and doesn't need any more verification.
|
||||
let &(ref header, ref last_epoch) = self.last_epochs.last()
|
||||
.expect("last_epochs known to have at least one element by the check above; qed");
|
||||
|
||||
let factories = ::factory::Factories::default();
|
||||
let mut state = State::from_existing(
|
||||
db.boxed_clone(),
|
||||
self.manifest.state_root.clone(),
|
||||
engine.account_start_nonce(target_header.number()),
|
||||
factories,
|
||||
).map_err(|e| format!("State root mismatch: {}", e))?;
|
||||
|
||||
let (tx, env_info) = make_tx_and_env(
|
||||
engine,
|
||||
addr,
|
||||
data,
|
||||
&target_header,
|
||||
target_last_hashes.clone(),
|
||||
);
|
||||
|
||||
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
||||
Executive::new(&mut state, &env_info, engine)
|
||||
.transact_virtual(&tx, options)
|
||||
.map(|e| e.output)
|
||||
.map_err(|e| format!("Error executing: {}", e))
|
||||
};
|
||||
|
||||
let data = engine.epoch_proof(&target_header, &caller)?;
|
||||
if &data[..] != &last_data[..] {
|
||||
return Err(Error::WrongChunkFormat("Warp target has different epoch data than epoch transition.".into()).into())
|
||||
if header != &target_header {
|
||||
last_epoch.verify_heavy(&target_header)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -93,7 +93,5 @@ pub trait Rebuilder: Send {
|
||||
///
|
||||
/// This should apply the necessary "glue" between chunks,
|
||||
/// and verify against the restored state.
|
||||
///
|
||||
/// The database passed contains the state for the warp target block.
|
||||
fn finalize(&mut self, db: ::state_db::StateDB, engine: &Engine) -> Result<(), ::error::Error>;
|
||||
fn finalize(&mut self, engine: &Engine) -> Result<(), ::error::Error>;
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ impl Rebuilder for PowRebuilder {
|
||||
}
|
||||
|
||||
/// Glue together any disconnected chunks and check that the chain is complete.
|
||||
fn finalize(&mut self, _: ::state_db::StateDB, _: &Engine) -> Result<(), ::error::Error> {
|
||||
fn finalize(&mut self, _: &Engine) -> Result<(), ::error::Error> {
|
||||
let mut batch = self.db.transaction();
|
||||
|
||||
for (first_num, first_hash) in self.disconnected.drain(..) {
|
||||
@ -299,12 +299,12 @@ impl Rebuilder for PowRebuilder {
|
||||
}
|
||||
|
||||
let genesis_hash = self.chain.genesis_hash();
|
||||
self.chain.insert_epoch_transition(&mut batch, 0, ::blockchain::EpochTransition {
|
||||
self.chain.insert_epoch_transition(&mut batch, 0, ::engines::EpochTransition {
|
||||
block_number: 0,
|
||||
block_hash: genesis_hash,
|
||||
proof: vec![],
|
||||
state_proof: vec![],
|
||||
});
|
||||
|
||||
self.db.write_buffered(batch);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ fn rebuild_accounts(
|
||||
}
|
||||
}
|
||||
|
||||
::rlp::encode(&acc).to_vec()
|
||||
::rlp::encode(&acc).into_vec()
|
||||
};
|
||||
|
||||
*out = (hash, thin_rlp);
|
||||
|
@ -164,11 +164,10 @@ impl Restoration {
|
||||
}
|
||||
|
||||
// check for missing code.
|
||||
let db = self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?;
|
||||
let db = ::state_db::StateDB::new(db, 0);
|
||||
self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?;
|
||||
|
||||
// connect out-of-order chunks and verify chain integrity.
|
||||
self.secondary.finalize(db, engine)?;
|
||||
self.secondary.finalize(engine)?;
|
||||
|
||||
if let Some(writer) = self.writer {
|
||||
writer.finish(self.manifest)?;
|
||||
|
@ -26,7 +26,6 @@ use client::{BlockChainClient, Client};
|
||||
use engines::Engine;
|
||||
use snapshot::{StateRebuilder};
|
||||
use snapshot::io::{SnapshotReader, PackedWriter, PackedReader};
|
||||
use state_db::StateDB;
|
||||
|
||||
use devtools::{RandomTempPath, GuardedTempResult};
|
||||
use rand::Rng;
|
||||
@ -78,7 +77,7 @@ impl StateProducer {
|
||||
let mut account: BasicAccount = ::rlp::decode(&*account_data);
|
||||
let acct_db = AccountDBMut::from_hash(db, *address_hash);
|
||||
fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed);
|
||||
*account_data = DBValue::from_vec(::rlp::encode(&account).to_vec());
|
||||
*account_data = DBValue::from_vec(::rlp::encode(&account).into_vec());
|
||||
}
|
||||
|
||||
// sweep again to alter account trie.
|
||||
@ -195,8 +194,7 @@ pub fn restore(
|
||||
secondary.feed(&snappy_buffer[..len], engine, &flag)?;
|
||||
}
|
||||
|
||||
let jdb = state.finalize(manifest.block_number, manifest.block_hash)?;
|
||||
let state_db = StateDB::new(jdb, 0);
|
||||
|
||||
secondary.finalize(state_db, engine)
|
||||
trace!(target: "snapshot", "finalizing");
|
||||
state.finalize(manifest.block_number, manifest.block_hash)?;
|
||||
secondary.finalize(engine)
|
||||
}
|
||||
|
@ -21,11 +21,9 @@ use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use account_provider::AccountProvider;
|
||||
use client::{Client, BlockChainClient, MiningBlockChainClient};
|
||||
use client::{Client, BlockChainClient};
|
||||
use ethkey::Secret;
|
||||
use engines::Seal;
|
||||
use futures::Future;
|
||||
use miner::MinerService;
|
||||
use native_contracts::test_contracts::ValidatorSet;
|
||||
use snapshot::tests::helpers as snapshot_helpers;
|
||||
use spec::Spec;
|
||||
@ -37,7 +35,7 @@ use util::kvdb;
|
||||
|
||||
const PASS: &'static str = "";
|
||||
const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes activated.
|
||||
const TRANSITION_BLOCK_2: usize = 6; // block at which the second contract activates.
|
||||
const TRANSITION_BLOCK_2: usize = 10; // block at which the second contract activates.
|
||||
|
||||
macro_rules! secret {
|
||||
($e: expr) => { Secret::from_slice(&$e.sha3()) }
|
||||
@ -54,10 +52,10 @@ lazy_static! {
|
||||
}
|
||||
|
||||
|
||||
/// Contract code used here: https://gist.github.com/rphmeier/2de14fd365a969e3a9e10d77eb9a1e37
|
||||
/// Contract code used here: https://gist.github.com/anonymous/2a43783647e0f0dfcc359bd6fd81d6d9
|
||||
/// Account with secrets "1".sha3() is initially the validator.
|
||||
/// Transitions to the contract at block 2, initially same validator set.
|
||||
/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine the current validators using `getValidators`.
|
||||
/// Create a new Spec with AuthorityRound which uses a contract at address 5 to determine the current validators using `getValidators`.
|
||||
/// `native_contracts::test_contracts::ValidatorSet` provides a native wrapper for the ABi.
|
||||
fn spec_fixed_to_contract() -> Spec {
|
||||
let data = include_bytes!("test_validator_contract.json");
|
||||
@ -101,45 +99,41 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
|
||||
{
|
||||
// push a block with given number, signed by one of the signers, with given transactions.
|
||||
let push_block = |signers: &[Address], n, txs: Vec<SignedTransaction>| {
|
||||
use block::IsBlock;
|
||||
use miner::MinerService;
|
||||
|
||||
let idx = n as usize % signers.len();
|
||||
trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}",
|
||||
n, txs.len(), signers[idx]);
|
||||
|
||||
client.miner().set_author(signers[idx]);
|
||||
client.miner().import_external_transactions(&*client,
|
||||
txs.into_iter().map(Into::into).collect());
|
||||
|
||||
let engine = client.engine();
|
||||
let idx = n as usize % signers.len();
|
||||
engine.set_signer(accounts.clone(), signers[idx], PASS.to_owned());
|
||||
engine.step();
|
||||
|
||||
trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}", n, txs.len(), signers[idx]);
|
||||
|
||||
let mut open_block = client.prepare_open_block(signers[idx], (5_000_000.into(), 5_000_000.into()), Vec::new());
|
||||
for tx in txs {
|
||||
open_block.push_transaction(tx, None).unwrap();
|
||||
}
|
||||
let block = open_block.close_and_lock();
|
||||
let seal = match engine.generate_seal(block.block()) {
|
||||
Seal::Regular(seal) => seal,
|
||||
_ => panic!("Unable to generate seal for dummy chain block #{}", n),
|
||||
};
|
||||
let block = block.seal(&*engine, seal).unwrap();
|
||||
|
||||
client.import_sealed_block(block).unwrap();
|
||||
assert_eq!(client.chain_info().best_block_number, n);
|
||||
};
|
||||
|
||||
// execution callback for native contract: push transaction to be sealed.
|
||||
let nonce = RefCell::new(client.engine().account_start_nonce(0));
|
||||
let exec = |addr, data| {
|
||||
|
||||
// create useless transactions vector so we don't have to dig in
|
||||
// and force sealing.
|
||||
let make_useless_transactions = || {
|
||||
let mut nonce = nonce.borrow_mut();
|
||||
let transaction = Transaction {
|
||||
nonce: *nonce,
|
||||
gas_price: 0.into(),
|
||||
gas: 1_000_000.into(),
|
||||
action: Action::Call(addr),
|
||||
value: 0.into(),
|
||||
data: data,
|
||||
gas_price: 1.into(),
|
||||
gas: 21_000.into(),
|
||||
action: Action::Call(Address::new()),
|
||||
value: 1.into(),
|
||||
data: Vec::new(),
|
||||
}.sign(&*RICH_SECRET, client.signing_network_id());
|
||||
|
||||
client.miner().import_own_transaction(&*client, transaction.into()).unwrap();
|
||||
|
||||
*nonce = *nonce + 1.into();
|
||||
Ok(Vec::new())
|
||||
vec![transaction]
|
||||
};
|
||||
|
||||
let contract_1 = ValidatorSet::new(*CONTRACT_ADDR_1);
|
||||
@ -156,8 +150,12 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
|
||||
panic!("Bad test: issued epoch change before transition to contract.");
|
||||
}
|
||||
|
||||
if (num as u64) < client.chain_info().best_block_number {
|
||||
panic!("Bad test: issued epoch change before previous transition finalized.");
|
||||
}
|
||||
|
||||
for number in client.chain_info().best_block_number + 1 .. num as u64 {
|
||||
push_block(&cur_signers, number, vec![]);
|
||||
push_block(&cur_signers, number, make_useless_transactions());
|
||||
}
|
||||
|
||||
let pending = if manual {
|
||||
@ -167,22 +165,48 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
|
||||
false => &contract_1,
|
||||
};
|
||||
|
||||
contract.set_validators(&exec, new_set.clone()).wait().unwrap();
|
||||
client.ready_transactions()
|
||||
.into_iter()
|
||||
.map(|x| x.transaction)
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
let mut pending = Vec::new();
|
||||
{
|
||||
let mut exec = |addr, data| {
|
||||
let mut nonce = nonce.borrow_mut();
|
||||
let transaction = Transaction {
|
||||
nonce: *nonce,
|
||||
gas_price: 0.into(),
|
||||
gas: 1_000_000.into(),
|
||||
action: Action::Call(addr),
|
||||
value: 0.into(),
|
||||
data: data,
|
||||
}.sign(&*RICH_SECRET, client.signing_network_id());
|
||||
|
||||
pending.push(transaction);
|
||||
|
||||
*nonce = *nonce + 1.into();
|
||||
Ok(Vec::new())
|
||||
};
|
||||
|
||||
contract.set_validators(&mut exec, new_set.clone()).wait().unwrap();
|
||||
}
|
||||
|
||||
pending
|
||||
} else {
|
||||
make_useless_transactions()
|
||||
};
|
||||
|
||||
// push transition block.
|
||||
push_block(&cur_signers, num as u64, pending);
|
||||
|
||||
// push blocks to finalize transition
|
||||
for finalization_count in 1.. {
|
||||
if finalization_count * 2 > cur_signers.len() { break }
|
||||
push_block(&cur_signers, (num + finalization_count) as u64, make_useless_transactions());
|
||||
}
|
||||
|
||||
cur_signers = new_set;
|
||||
}
|
||||
|
||||
// make blocks beyond.
|
||||
for number in (client.chain_info().best_block_number..).take(blocks_beyond) {
|
||||
push_block(&cur_signers, number + 1, vec![]);
|
||||
push_block(&cur_signers, number + 1, make_useless_transactions());
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +214,7 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_to_contract() {
|
||||
fn fixed_to_contract_only() {
|
||||
let (provider, addrs) = make_accounts(&[
|
||||
RICH_SECRET.clone(),
|
||||
secret!("foo"),
|
||||
@ -204,17 +228,21 @@ fn fixed_to_contract() {
|
||||
|
||||
assert!(provider.has_account(*RICH_ADDR).unwrap());
|
||||
|
||||
let client = make_chain(provider, 1, vec![
|
||||
let client = make_chain(provider, 3, vec![
|
||||
Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]),
|
||||
Transition::Manual(4, vec![addrs[0], addrs[1], addrs[4], addrs[6]]),
|
||||
Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]),
|
||||
]);
|
||||
|
||||
assert_eq!(client.chain_info().best_block_number, 5);
|
||||
// 6, 7, 8 prove finality for transition at 6.
|
||||
// 3 beyond gets us to 11.
|
||||
assert_eq!(client.chain_info().best_block_number, 11);
|
||||
let reader = snapshot_helpers::snap(&*client);
|
||||
|
||||
let new_db = kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0));
|
||||
let spec = spec_fixed_to_contract();
|
||||
|
||||
// ensure fresh engine's step matches.
|
||||
for _ in 0..11 { spec.engine.step() }
|
||||
snapshot_helpers::restore(Arc::new(new_db), &*spec.engine, &**reader, &spec.genesis_block()).unwrap();
|
||||
}
|
||||
|
||||
@ -233,17 +261,18 @@ fn fixed_to_contract_to_contract() {
|
||||
|
||||
assert!(provider.has_account(*RICH_ADDR).unwrap());
|
||||
|
||||
let client = make_chain(provider, 2, vec![
|
||||
let client = make_chain(provider, 3, vec![
|
||||
Transition::Manual(3, vec![addrs[2], addrs[3], addrs[5], addrs[7]]),
|
||||
Transition::Manual(4, vec![addrs[0], addrs[1], addrs[4], addrs[6]]),
|
||||
Transition::Implicit(5, vec![addrs[0]]),
|
||||
Transition::Manual(8, vec![addrs[2], addrs[4], addrs[6], addrs[7]]),
|
||||
Transition::Manual(6, vec![addrs[0], addrs[1], addrs[4], addrs[6]]),
|
||||
Transition::Implicit(10, vec![addrs[0]]),
|
||||
Transition::Manual(13, vec![addrs[2], addrs[4], addrs[6], addrs[7]]),
|
||||
]);
|
||||
|
||||
assert_eq!(client.chain_info().best_block_number, 10);
|
||||
assert_eq!(client.chain_info().best_block_number, 16);
|
||||
let reader = snapshot_helpers::snap(&*client);
|
||||
let new_db = kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0));
|
||||
let spec = spec_fixed_to_contract();
|
||||
|
||||
for _ in 0..16 { spec.engine.step() }
|
||||
snapshot_helpers::restore(Arc::new(new_db), &*spec.engine, &**reader, &spec.genesis_block()).unwrap();
|
||||
}
|
||||
|
@ -23,10 +23,8 @@ use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||
use blockchain::BlockChain;
|
||||
use snapshot::{chunk_secondary, Error as SnapshotError, Progress, SnapshotComponents};
|
||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||
use state_db::StateDB;
|
||||
|
||||
use util::{Mutex, snappy};
|
||||
use util::journaldb::{self, Algorithm};
|
||||
use util::kvdb::{self, KeyValueDB, DBTransaction};
|
||||
|
||||
use std::sync::Arc;
|
||||
@ -83,7 +81,6 @@ fn chunk_and_restore(amount: u64) {
|
||||
// restore it.
|
||||
let new_db = Arc::new(kvdb::in_memory(::db::NUM_COLUMNS.unwrap_or(0)));
|
||||
let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone());
|
||||
let new_state = StateDB::new(journaldb::new(new_db.clone(), Algorithm::Archive, None), 0);
|
||||
let mut rebuilder = SNAPSHOT_MODE.rebuilder(new_chain, new_db.clone(), &manifest).unwrap();
|
||||
|
||||
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
|
||||
@ -94,7 +91,7 @@ fn chunk_and_restore(amount: u64) {
|
||||
rebuilder.feed(&chunk, engine.as_ref(), &flag).unwrap();
|
||||
}
|
||||
|
||||
rebuilder.finalize(new_state, engine.as_ref()).unwrap();
|
||||
rebuilder.finalize(engine.as_ref()).unwrap();
|
||||
drop(rebuilder);
|
||||
|
||||
// and test it.
|
||||
|
@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "TestValidatorContract",
|
||||
"engine": {
|
||||
"basicAuthority": {
|
||||
"authorityRound": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"durationLimit": "0x0d",
|
||||
"stepDuration": 1,
|
||||
"startStep": 0,
|
||||
"validators": {
|
||||
"multi": {
|
||||
"0": { "list": ["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"] },
|
||||
"2": { "contract": "0x0000000000000000000000000000000000000005" },
|
||||
"6": { "contract": "0x0000000000000000000000000000000000000006" }
|
||||
"10": { "contract": "0x0000000000000000000000000000000000000006" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,7 +24,10 @@
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"generic": "0xc180"
|
||||
"authorityRound": {
|
||||
"step": "0x0",
|
||||
"signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
@ -39,11 +43,11 @@
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000005": {
|
||||
"balance": "1",
|
||||
"constructor": "6060604052604060405190810160405280737d577a597b2742b498cb5cf0c26cdcd726d39e6e73ffffffffffffffffffffffffffffffffffffffff1681526020017382a978b3f5962a5b0957d9ee9eef472ee55b42f173ffffffffffffffffffffffffffffffffffffffff16815250600290600261007e929190610096565b50341561008757fe5b5b60006001819055505b610163565b82805482825590600052602060002090810192821561010f579160200282015b8281111561010e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906100b6565b5b50905061011c9190610120565b5090565b61016091905b8082111561015c57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101610126565b5090565b90565b61045d806101726000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063303e98e5146100675780639300c9261461008d578063b7ab4db5146100e4578063bfc708a014610159578063fd6e1b501461018f575bfe5b341561006f57fe5b6100776101c5565b6040518082815260200191505060405180910390f35b341561009557fe5b6100e26004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506101d0565b005b34156100ec57fe5b6100f46102b3565b6040518080602001828103825283818151815260200191508051906020019060200280838360008314610146575b80518252602083111561014657602082019150602081019050602083039250610122565b5050509050019250505060405180910390f35b341561016157fe5b61018d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610348565b005b341561019757fe5b6101c3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061034c565b005b600060015490505b90565b600081600290805190602001906101e8929190610350565b50600143034090506000546000191681600019161415156102ae578060008160001916905550600160016000828254019250508190555060015481600019167f47e91f47ccfdcb578564e1af55da55c5e5d33403372fe68e4fed3dfd385764a184604051808060200182810382528381815181526020019150805190602001906020028083836000831461029b575b80518252602083111561029b57602082019150602081019050602083039250610277565b5050509050019250505060405180910390a35b5b5050565b6102bb6103da565b600280548060200260200160405190810160405280929190818152602001828054801561033d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116102f3575b505050505090505b90565b5b50565b5b50565b8280548282559060005260206000209081019282156103c9579160200282015b828111156103c85782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610370565b5b5090506103d691906103ee565b5090565b602060405190810160405280600081525090565b61042e91905b8082111561042a57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016103f4565b5090565b905600a165627a7a723058205c9ed1e1da2b93682907ac47377a662b21a5f9d89c4b21be40b098bdb00254360029"
|
||||
"constructor": "6060604052602060405190810160405280737d577a597b2742b498cb5cf0c26cdcd726d39e6e73ffffffffffffffffffffffffffffffffffffffff16815250600090600161004e92919061005c565b50341561005757fe5b610129565b8280548282559060005260206000209081019282156100d5579160200282015b828111156100d45782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019061007c565b5b5090506100e291906100e6565b5090565b61012691905b8082111561012257600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016100ec565b5090565b90565b61056f806101386000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806375286211146100675780639300c92614610079578063b7ab4db5146100d0578063c476dd4014610145578063d69f13bb146101c7575bfe5b341561006f57fe5b610077610206565b005b341561008157fe5b6100ce600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050610275565b005b34156100d857fe5b6100e061031f565b6040518080602001828103825283818151815260200191508051906020019060200280838360008314610132575b8051825260208311156101325760208201915060208101905060208303925061010e565b5050509050019250505060405180910390f35b341561014d57fe5b6101c5600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b4565b005b34156101cf57fe5b610204600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506103ba565b005b73fffffffffffffffffffffffffffffffffffffffe3373ffffffffffffffffffffffffffffffffffffffff1614151561023f5760006000fd5b600060018054905014151561027257600160009080546102609291906103bf565b5060006001816102709190610411565b505b5b565b806001908051906020019061028b92919061043d565b506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89826040518080602001828103825283818151815260200191508051906020019060200280838360008314610309575b805182526020831115610309576020820191506020810190506020830392506102e5565b5050509050019250505060405180910390a25b50565b6103276104c7565b60008054806020026020016040519081016040528092919081815260200182805480156103a957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161035f575b505050505090505b90565b5b505050565b5b5050565b8280548282559060005260206000209081019282156104005760005260206000209182015b828111156103ff5782548255916001019190600101906103e4565b5b50905061040d91906104db565b5090565b81548183558181151161043857818360005260206000209182019101610437919061051e565b5b505050565b8280548282559060005260206000209081019282156104b6579160200282015b828111156104b55782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019061045d565b5b5090506104c391906104db565b5090565b602060405190810160405280600081525090565b61051b91905b8082111561051757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016104e1565b5090565b90565b61054091905b8082111561053c576000816000905550600101610524565b5090565b905600a165627a7a7230582041ce7e5c820bc89b1a330a3233c4f3013e77433ecba368fa234adf758d87fe1d0029"
|
||||
},
|
||||
"0000000000000000000000000000000000000006": {
|
||||
"balance": "1",
|
||||
"constructor": "6060604052602060405190810160405280737d577a597b2742b498cb5cf0c26cdcd726d39e6e73ffffffffffffffffffffffffffffffffffffffff16815250600290600161004e929190610066565b50341561005757fe5b5b60006001819055505b610133565b8280548282559060005260206000209081019282156100df579160200282015b828111156100de5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610086565b5b5090506100ec91906100f0565b5090565b61013091905b8082111561012c57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016100f6565b5090565b90565b61045d806101426000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063303e98e5146100675780639300c9261461008d578063b7ab4db5146100e4578063bfc708a014610159578063fd6e1b501461018f575bfe5b341561006f57fe5b6100776101c5565b6040518082815260200191505060405180910390f35b341561009557fe5b6100e26004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919050506101d0565b005b34156100ec57fe5b6100f46102b3565b6040518080602001828103825283818151815260200191508051906020019060200280838360008314610146575b80518252602083111561014657602082019150602081019050602083039250610122565b5050509050019250505060405180910390f35b341561016157fe5b61018d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610348565b005b341561019757fe5b6101c3600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061034c565b005b600060015490505b90565b600081600290805190602001906101e8929190610350565b50600143034090506000546000191681600019161415156102ae578060008160001916905550600160016000828254019250508190555060015481600019167f47e91f47ccfdcb578564e1af55da55c5e5d33403372fe68e4fed3dfd385764a184604051808060200182810382528381815181526020019150805190602001906020028083836000831461029b575b80518252602083111561029b57602082019150602081019050602083039250610277565b5050509050019250505060405180910390a35b5b5050565b6102bb6103da565b600280548060200260200160405190810160405280929190818152602001828054801561033d57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116102f3575b505050505090505b90565b5b50565b5b50565b8280548282559060005260206000209081019282156103c9579160200282015b828111156103c85782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610370565b5b5090506103d691906103ee565b5090565b602060405190810160405280600081525090565b61042e91905b8082111561042a57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016103f4565b5090565b905600a165627a7a723058203070810251dcb89c9838d957eb3dbeef357bef0902e0245e3dc3849b6143c3960029"
|
||||
"constructor": "6060604052602060405190810160405280737d577a597b2742b498cb5cf0c26cdcd726d39e6e73ffffffffffffffffffffffffffffffffffffffff16815250600090600161004e92919061005c565b50341561005757fe5b610129565b8280548282559060005260206000209081019282156100d5579160200282015b828111156100d45782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019061007c565b5b5090506100e291906100e6565b5090565b61012691905b8082111561012257600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016100ec565b5090565b90565b61056f806101386000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806375286211146100675780639300c92614610079578063b7ab4db5146100d0578063c476dd4014610145578063d69f13bb146101c7575bfe5b341561006f57fe5b610077610206565b005b341561008157fe5b6100ce600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091905050610275565b005b34156100d857fe5b6100e061031f565b6040518080602001828103825283818151815260200191508051906020019060200280838360008314610132575b8051825260208311156101325760208201915060208101905060208303925061010e565b5050509050019250505060405180910390f35b341561014d57fe5b6101c5600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506103b4565b005b34156101cf57fe5b610204600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506103ba565b005b73fffffffffffffffffffffffffffffffffffffffe3373ffffffffffffffffffffffffffffffffffffffff1614151561023f5760006000fd5b600060018054905014151561027257600160009080546102609291906103bf565b5060006001816102709190610411565b505b5b565b806001908051906020019061028b92919061043d565b506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89826040518080602001828103825283818151815260200191508051906020019060200280838360008314610309575b805182526020831115610309576020820191506020810190506020830392506102e5565b5050509050019250505060405180910390a25b50565b6103276104c7565b60008054806020026020016040519081016040528092919081815260200182805480156103a957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161035f575b505050505090505b90565b5b505050565b5b5050565b8280548282559060005260206000209081019282156104005760005260206000209182015b828111156103ff5782548255916001019190600101906103e4565b5b50905061040d91906104db565b5090565b81548183558181151161043857818360005260206000209182019101610437919061051e565b5b505050565b8280548282559060005260206000209081019282156104b6579160200282015b828111156104b55782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019061045d565b5b5090506104c391906104db565b5090565b602060405190810160405280600081525090565b61051b91905b8082111561051757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016104e1565b5090565b90565b61054091905b8082111561053c576000816000905550600101610524565b5090565b905600a165627a7a7230582041ce7e5c820bc89b1a330a3233c4f3013e77433ecba368fa234adf758d87fe1d0029"
|
||||
},
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
|
@ -257,7 +257,7 @@ impl Account {
|
||||
match db.get(&self.code_hash) {
|
||||
Some(x) => {
|
||||
self.code_size = Some(x.len());
|
||||
self.code_cache = Arc::new(x.to_vec());
|
||||
self.code_cache = Arc::new(x.into_vec());
|
||||
Some(self.code_cache.clone())
|
||||
},
|
||||
_ => {
|
||||
@ -476,10 +476,10 @@ mod tests {
|
||||
fn account_compress() {
|
||||
let raw = Account::new_basic(2.into(), 4.into()).rlp();
|
||||
let rlp = UntrustedRlp::new(&raw);
|
||||
let compact_vec = rlp.compress(RlpType::Snapshot).to_vec();
|
||||
let compact_vec = rlp.compress(RlpType::Snapshot).into_vec();
|
||||
assert!(raw.len() > compact_vec.len());
|
||||
let again_raw = UntrustedRlp::new(&compact_vec).decompress(RlpType::Snapshot);
|
||||
assert_eq!(raw, again_raw.to_vec());
|
||||
assert_eq!(raw, again_raw.into_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1826,14 +1826,14 @@ mod tests {
|
||||
let mut state = get_temp_state();
|
||||
state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}).unwrap();
|
||||
state.init_code(&a, vec![1, 2, 3]).unwrap();
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new([1u8, 2, 3].to_vec())));
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new([1u8, 2, 3].to_vec())));
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
|
||||
state.drop()
|
||||
};
|
||||
|
||||
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new([1u8, 2, 3].to_vec())));
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -133,7 +133,7 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransa
|
||||
rlp.append(header);
|
||||
rlp.begin_list(transactions.len());
|
||||
for t in transactions {
|
||||
rlp.append_raw(&rlp::encode(t).to_vec(), 1);
|
||||
rlp.append_raw(&rlp::encode(t).into_vec(), 1);
|
||||
}
|
||||
rlp.append_list(&uncles);
|
||||
rlp.out()
|
||||
@ -193,7 +193,8 @@ pub fn generate_dummy_client_with_spec_accounts_and_data<F>(get_test_spec: F, ac
|
||||
Arc::new(last_hashes.clone()),
|
||||
author.clone(),
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![]
|
||||
vec![],
|
||||
false,
|
||||
).unwrap();
|
||||
b.set_difficulty(U256::from(0x20000));
|
||||
rolling_timestamp += 10;
|
||||
|
@ -475,7 +475,7 @@ mod tests {
|
||||
let mut uncles_rlp = RlpStream::new();
|
||||
uncles_rlp.append_list(&good_uncles);
|
||||
let good_uncles_hash = uncles_rlp.as_raw().sha3();
|
||||
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::<UnverifiedTransaction>(t).to_vec()));
|
||||
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| ::rlp::encode::<UnverifiedTransaction>(t).into_vec()));
|
||||
|
||||
let mut parent = good.clone();
|
||||
parent.set_number(9);
|
||||
|
@ -104,7 +104,7 @@ impl Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: iv,
|
||||
}),
|
||||
ciphertext: (*ciphertext).to_vec(),
|
||||
ciphertext: ciphertext.into_vec(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
dklen: crypto::KEY_LENGTH as u32,
|
||||
salt: salt,
|
||||
|
@ -122,7 +122,7 @@ impl trace::VMTracer for Informant {
|
||||
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized {
|
||||
let mut vm = Informant::default();
|
||||
vm.depth = self.depth + 1;
|
||||
vm.code = code.to_vec();
|
||||
vm.code = code.into_vec();
|
||||
vm.gas_used = self.gas_used;
|
||||
vm
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ mod tests {
|
||||
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
|
||||
assert_eq!(b"foobar".to_vec(), transport);
|
||||
assert_eq!(b"foobar".into_vec(), transport);
|
||||
assert_eq!(6, progress);
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ mod tests {
|
||||
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
|
||||
assert_eq!(b"bar".to_vec(), transport);
|
||||
assert_eq!(b"bar".into_vec(), transport);
|
||||
assert_eq!(6, progress);
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ impl IpfsHandler {
|
||||
fn block_list(&self, hash: H256) -> Result<Out> {
|
||||
let uncles = self.client().find_uncles(&hash).ok_or(Error::BlockNotFound)?;
|
||||
|
||||
Ok(Out::OctetStream(rlp::encode_list(&uncles).to_vec()))
|
||||
Ok(Out::OctetStream(rlp::encode_list(&uncles).into_vec()))
|
||||
}
|
||||
|
||||
/// Get transaction by hash and return as raw binary.
|
||||
@ -87,7 +87,7 @@ impl IpfsHandler {
|
||||
let tx_id = TransactionId::Hash(hash);
|
||||
let tx = self.client().transaction(tx_id).ok_or(Error::TransactionNotFound)?;
|
||||
|
||||
Ok(Out::OctetStream(rlp::encode(&*tx).to_vec()))
|
||||
Ok(Out::OctetStream(rlp::encode(&*tx).into_vec()))
|
||||
}
|
||||
|
||||
/// Get state trie node by hash and return as raw binary.
|
||||
|
@ -49,6 +49,9 @@ pub struct AuthorityRoundParams {
|
||||
/// Block from which monotonic steps start.
|
||||
#[serde(rename="validateStepTransition")]
|
||||
pub validate_step_transition: Option<Uint>,
|
||||
/// Whether transitions should be immediate.
|
||||
#[serde(rename="immediateTransitions")]
|
||||
pub immediate_transitions: Option<bool>,
|
||||
}
|
||||
|
||||
/// Authority engine deserialization.
|
||||
@ -92,5 +95,6 @@ mod tests {
|
||||
assert!(deserialized.params.registrar.is_none());
|
||||
assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24))));
|
||||
assert_eq!(deserialized.params.eip155_transition, Some(Uint(U256::from(0x42))));
|
||||
assert_eq!(deserialized.params.immediate_transitions, None);
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ impl TransactionEntry {
|
||||
impl From<PendingTransaction> for TransactionEntry {
|
||||
fn from(pending: PendingTransaction) -> Self {
|
||||
TransactionEntry {
|
||||
rlp_bytes: ::rlp::encode(&pending.transaction).to_vec(),
|
||||
rlp_bytes: ::rlp::encode(&pending.transaction).into_vec(),
|
||||
condition: pending.condition.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
@ -227,8 +227,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
||||
}
|
||||
|
||||
// start on_demand service.
|
||||
let account_start_nonce = service.client().engine().account_start_nonce(0);
|
||||
let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone(), account_start_nonce));
|
||||
let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone()));
|
||||
|
||||
// set network path.
|
||||
net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned());
|
||||
|
@ -36,6 +36,6 @@ pub fn sign_call<B: MiningBlockChainClient, M: MinerService>(
|
||||
gas: request.gas.unwrap_or(50_000_000.into()),
|
||||
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(&**client, &**miner)),
|
||||
value: request.value.unwrap_or(0.into()),
|
||||
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
|
||||
data: request.data.unwrap_or_default(),
|
||||
}.fake_sign(from))
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ impl LightFetch {
|
||||
let action = req.to.map_or(Action::Create, Action::Call);
|
||||
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
||||
let value = req.value.unwrap_or_else(U256::zero);
|
||||
let data = req.data.map_or_else(Vec::new, |d| d.to_vec());
|
||||
let data = req.data.unwrap_or_default();
|
||||
|
||||
future::done(match nonce {
|
||||
Some(n) => Ok(EthTransaction {
|
||||
|
@ -607,7 +607,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
||||
let mix_hash: H256 = mix_hash.into();
|
||||
trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
||||
|
||||
let seal = vec![rlp::encode(&mix_hash).to_vec(), rlp::encode(&nonce).to_vec()];
|
||||
let seal = vec![rlp::encode(&mix_hash).into_vec(), rlp::encode(&nonce).into_vec()];
|
||||
Ok(self.miner.submit_seal(&*self.client, pow_hash, seal).is_ok())
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ impl EthClient {
|
||||
true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(|t| Transaction::from_localized(t, eip86_transition)).collect()),
|
||||
_ => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()),
|
||||
},
|
||||
extra_data: Bytes::new(header.extra_data().to_vec()),
|
||||
extra_data: Bytes::new(header.extra_data().clone()),
|
||||
},
|
||||
extra_info: extra_info
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
.and_then(|(pending_tx, dispatcher)| {
|
||||
let network_id = pending_tx.network_id();
|
||||
trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}",
|
||||
::rlp::encode(&*pending_tx).to_vec().pretty(), network_id);
|
||||
::rlp::encode(&*pending_tx).into_vec().pretty(), network_id);
|
||||
|
||||
dispatcher.dispatch_transaction(pending_tx).map(Into::into)
|
||||
})
|
||||
|
@ -963,7 +963,7 @@ fn rpc_eth_send_raw_transaction() {
|
||||
let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let rlp = rlp::encode(&t).to_vec().to_hex();
|
||||
let rlp = rlp::encode(&t).into_vec().to_hex();
|
||||
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
|
@ -195,7 +195,7 @@ impl Transaction {
|
||||
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()),
|
||||
Action::Call(_) => None,
|
||||
},
|
||||
raw: ::rlp::encode(&t.signed).to_vec().into(),
|
||||
raw: ::rlp::encode(&t.signed).into_vec().into(),
|
||||
public_key: t.recover_public().ok().map(Into::into),
|
||||
network_id: t.network_id(),
|
||||
standard_v: t.standard_v().into(),
|
||||
@ -229,7 +229,7 @@ impl Transaction {
|
||||
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()),
|
||||
Action::Call(_) => None,
|
||||
},
|
||||
raw: ::rlp::encode(&t).to_vec().into(),
|
||||
raw: ::rlp::encode(&t).into_vec().into(),
|
||||
public_key: t.public_key().map(Into::into),
|
||||
network_id: t.network_id(),
|
||||
standard_v: t.standard_v().into(),
|
||||
|
@ -93,7 +93,7 @@ impl KeyStorage for PersistentKeyStorage {
|
||||
self.db.get(None, document)
|
||||
.map_err(Error::Database)?
|
||||
.ok_or(Error::DocumentNotFound)
|
||||
.map(|key| key.to_vec())
|
||||
.map(|key| key.into_vec())
|
||||
.and_then(|key| serde_json::from_slice::<SerializableDocumentKeyShare>(&key).map_err(|e| Error::Database(e.to_string())))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ mod tests {
|
||||
let mut core = Core::new().expect("Tokio Core should be created with no errors");
|
||||
let mut buffer = vec![0u8; 2048];
|
||||
|
||||
let mut data_vec = data.as_bytes().to_vec();
|
||||
let mut data_vec = data.as_bytes().into_vec();
|
||||
data_vec.extend(b"\n");
|
||||
|
||||
let stream = TcpStream::connect(addr, &core.handle())
|
||||
@ -353,7 +353,7 @@ mod tests {
|
||||
io::read(stream, &mut buffer)
|
||||
})
|
||||
.and_then(|(_, read_buf, len)| {
|
||||
future::ok(read_buf[0..len].to_vec())
|
||||
future::ok(read_buf[0..len].into_vec())
|
||||
});
|
||||
let result = core.run(stream).expect("Core should run with no errors");
|
||||
|
||||
@ -454,7 +454,7 @@ mod tests {
|
||||
let mut auth_request =
|
||||
r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"#
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
.into_vec();
|
||||
auth_request.extend(b"\n");
|
||||
|
||||
let mut core = Core::new().expect("Tokio Core should be created with no errors");
|
||||
@ -487,7 +487,7 @@ mod tests {
|
||||
})
|
||||
.and_then(|(_, read_buf, len)| {
|
||||
trace!(target: "stratum", "Received work from server");
|
||||
future::ok(read_buf[0..len].to_vec())
|
||||
future::ok(read_buf[0..len].into_vec())
|
||||
});
|
||||
let response = String::from_utf8(
|
||||
core.run(stream).expect("Core should run with no errors")
|
||||
|
@ -427,7 +427,7 @@ struct TxRelay(Arc<BlockChainClient>);
|
||||
impl LightHandler for TxRelay {
|
||||
fn on_transactions(&self, ctx: &EventContext, relay: &[::ethcore::transaction::UnverifiedTransaction]) {
|
||||
trace!(target: "pip", "Relaying {} transactions from peer {}", relay.len(), ctx.peer());
|
||||
self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx).to_vec()).collect(), ctx.peer())
|
||||
self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx).into_vec()).collect(), ctx.peer())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ mod tests {
|
||||
|
||||
parent_hash = Some(header.hash());
|
||||
|
||||
encoded::Header::new(::rlp::encode(&header).to_vec())
|
||||
encoded::Header::new(::rlp::encode(&header).into_vec())
|
||||
}).collect();
|
||||
|
||||
assert!(verify(&headers, &request).is_ok());
|
||||
@ -203,7 +203,7 @@ mod tests {
|
||||
|
||||
parent_hash = Some(header.hash());
|
||||
|
||||
encoded::Header::new(::rlp::encode(&header).to_vec())
|
||||
encoded::Header::new(::rlp::encode(&header).into_vec())
|
||||
}).collect();
|
||||
|
||||
assert!(verify(&headers, &request).is_ok());
|
||||
@ -229,7 +229,7 @@ mod tests {
|
||||
|
||||
parent_hash = Some(header.hash());
|
||||
|
||||
encoded::Header::new(::rlp::encode(&header).to_vec())
|
||||
encoded::Header::new(::rlp::encode(&header).into_vec())
|
||||
}).collect();
|
||||
|
||||
assert_eq!(verify(&headers, &request), Err(BasicError::TooManyHeaders(20, 25)));
|
||||
@ -248,7 +248,7 @@ mod tests {
|
||||
let mut header = Header::default();
|
||||
header.set_number(x);
|
||||
|
||||
encoded::Header::new(::rlp::encode(&header).to_vec())
|
||||
encoded::Header::new(::rlp::encode(&header).into_vec())
|
||||
}).collect();
|
||||
|
||||
assert_eq!(verify(&headers, &request), Err(BasicError::WrongSkip(5, Some(2))));
|
||||
|
@ -16,7 +16,7 @@ time = "0.1.34"
|
||||
rocksdb = { git = "https://github.com/paritytech/rust-rocksdb" }
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
rust-crypto = "0.2.34"
|
||||
elastic-array = "0.8"
|
||||
elastic-array = "0.9"
|
||||
rlp = { path = "rlp" }
|
||||
heapsize = "0.4"
|
||||
itertools = "0.5"
|
||||
|
@ -45,14 +45,14 @@ fn random_bytes(min_count: usize, diff_count: usize, seed: &mut H256) -> Vec<u8>
|
||||
assert!(min_count + diff_count <= 32);
|
||||
*seed = seed.sha3();
|
||||
let r = min_count + (seed[31] as usize % (diff_count + 1));
|
||||
seed[0..r].to_vec()
|
||||
seed[0..r].into_vec()
|
||||
}
|
||||
|
||||
fn random_value(seed: &mut H256) -> Bytes {
|
||||
*seed = seed.sha3();
|
||||
match seed[0] % 2 {
|
||||
1 => vec![seed[31];1],
|
||||
_ => seed.to_vec(),
|
||||
_ => seed.into_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ version = "0.2.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
elastic-array = "0.8"
|
||||
elastic-array = "0.9"
|
||||
ethcore-bigint = { path = "../bigint" }
|
||||
lazy_static = "0.2"
|
||||
rustc-serialize = "0.3"
|
||||
|
File diff suppressed because one or more lines are too long
@ -98,7 +98,7 @@ pub fn decode_list<T>(bytes: &[u8]) -> Vec<T> where T: Decodable {
|
||||
///
|
||||
/// fn main () {
|
||||
/// let animal = "cat";
|
||||
/// let out = rlp::encode(&animal).to_vec();
|
||||
/// let out = rlp::encode(&animal).into_vec();
|
||||
/// assert_eq!(out, vec![0x83, b'c', b'a', b't']);
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -264,8 +264,8 @@ impl RlpStream {
|
||||
/// panic! if stream is not finished.
|
||||
pub fn out(self) -> Vec<u8> {
|
||||
match self.is_finished() {
|
||||
//true => self.encoder.out().to_vec(),
|
||||
true => self.buffer.to_vec(),
|
||||
//true => self.encoder.out().into_vec(),
|
||||
true => self.buffer.into_vec(),
|
||||
false => panic!()
|
||||
}
|
||||
}
|
||||
|
@ -242,8 +242,8 @@ impl JournalDB for OverlayRecentDB {
|
||||
fn state(&self, key: &H256) -> Option<Bytes> {
|
||||
let journal_overlay = self.journal_overlay.read();
|
||||
let key = to_short_key(key);
|
||||
journal_overlay.backing_overlay.get(&key).map(|v| v.to_vec())
|
||||
.or_else(|| journal_overlay.pending_overlay.get(&key).map(|d| d.clone().to_vec()))
|
||||
journal_overlay.backing_overlay.get(&key).map(|v| v.into_vec())
|
||||
.or_else(|| journal_overlay.pending_overlay.get(&key).map(|d| d.clone().into_vec()))
|
||||
.or_else(|| self.backing.get_by_prefix(self.column, &key[0..DB_PREFIX_LEN]).map(|b| b.into_vec()))
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ impl JournalDB for OverlayRecentDB {
|
||||
batch.put_vec(self.column, &k.drain(), r.out());
|
||||
if journal_overlay.latest_era.map_or(true, |e| now > e) {
|
||||
trace!(target: "journaldb", "Set latest era to {}", now);
|
||||
batch.put_vec(self.column, &LATEST_ERA_KEY, encode(&now).to_vec());
|
||||
batch.put_vec(self.column, &LATEST_ERA_KEY, encode(&now).into_vec());
|
||||
journal_overlay.latest_era = Some(now);
|
||||
}
|
||||
|
||||
|
@ -216,7 +216,7 @@ impl KeyValueDB for InMemory {
|
||||
Some(map) =>
|
||||
map.iter()
|
||||
.find(|&(ref k ,_)| k.starts_with(prefix))
|
||||
.map(|(_, v)| (&**v).to_vec().into_boxed_slice())
|
||||
.map(|(_, v)| v.to_vec().into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,7 +227,7 @@ impl KeyValueDB for InMemory {
|
||||
match op {
|
||||
DBOp::Insert { col, key, value } => {
|
||||
if let Some(mut col) = columns.get_mut(&col) {
|
||||
col.insert(key.to_vec(), value);
|
||||
col.insert(key.into_vec(), value);
|
||||
}
|
||||
},
|
||||
DBOp::InsertCompressed { col, key, value } => {
|
||||
@ -235,7 +235,7 @@ impl KeyValueDB for InMemory {
|
||||
let compressed = UntrustedRlp::new(&value).compress(RlpType::Blocks);
|
||||
let mut value = DBValue::new();
|
||||
value.append_slice(&compressed);
|
||||
col.insert(key.to_vec(), value);
|
||||
col.insert(key.into_vec(), value);
|
||||
}
|
||||
},
|
||||
DBOp::Delete { col, key } => {
|
||||
@ -253,7 +253,7 @@ impl KeyValueDB for InMemory {
|
||||
Some(map) => Box::new( // TODO: worth optimizing at all?
|
||||
map.clone()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_boxed_slice(), v.to_vec().into_boxed_slice()))
|
||||
.map(|(k, v)| (k.into_boxed_slice(), v.into_vec().into_boxed_slice()))
|
||||
),
|
||||
None => Box::new(None.into_iter()),
|
||||
}
|
||||
@ -267,7 +267,7 @@ impl KeyValueDB for InMemory {
|
||||
map.clone()
|
||||
.into_iter()
|
||||
.skip_while(move |&(ref k, _)| !k.starts_with(prefix))
|
||||
.map(|(k, v)| (k.into_boxed_slice(), v.to_vec().into_boxed_slice()))
|
||||
.map(|(k, v)| (k.into_boxed_slice(), v.into_vec().into_boxed_slice()))
|
||||
),
|
||||
None => Box::new(None.into_iter()),
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ impl<T: SimpleMigration> Migration for T {
|
||||
};
|
||||
|
||||
for (key, value) in iter {
|
||||
if let Some((key, value)) = self.simple_migrate(key.to_vec(), value.to_vec()) {
|
||||
if let Some((key, value)) = self.simple_migrate(key.into_vec(), value.into_vec()) {
|
||||
batch.insert(key, value, dest)?;
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ impl Migration for AddsColumn {
|
||||
let mut batch = Batch::new(config, col);
|
||||
|
||||
for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) {
|
||||
batch.insert(key.to_vec(), value.to_vec(), dest)?;
|
||||
batch.insert(key.into_vec(), value.into_vec(), dest)?;
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,7 +95,7 @@ impl<'db> Iterator for FatDBIterator<'db> {
|
||||
.map(|res|
|
||||
res.map(|(hash, value)| {
|
||||
let aux_hash = hash.sha3();
|
||||
(self.trie.db().get(&aux_hash).expect("Missing fatdb hash").to_vec(), value)
|
||||
(self.trie.db().get(&aux_hash).expect("Missing fatdb hash").into_vec(), value)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ impl StandardMap {
|
||||
let v = match self.value_mode {
|
||||
ValueMode::Mirror => k.clone(),
|
||||
ValueMode::Random => Self::random_value(seed),
|
||||
ValueMode::Index => encode(&index).to_vec(),
|
||||
ValueMode::Index => encode(&index).into_vec(),
|
||||
};
|
||||
d.push((k, v))
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ fn iterator() {
|
||||
}
|
||||
|
||||
let t = TrieDB::new(&memdb, &root).unwrap();
|
||||
assert_eq!(d.iter().map(|i| i.clone().to_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>());
|
||||
assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::<Vec<_>>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::<Vec<_>>());
|
||||
assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
|
@ -1026,14 +1026,14 @@ mod tests {
|
||||
let mut memdb = MemoryDB::new();
|
||||
let mut root = H256::new();
|
||||
let mut t1 = TrieDBMut::new(&mut memdb, &mut root);
|
||||
t1.insert(&[0x01, 0x23], &big_value.to_vec()).unwrap();
|
||||
t1.insert(&[0x01, 0x34], &big_value.to_vec()).unwrap();
|
||||
t1.insert(&[0x01, 0x23], big_value).unwrap();
|
||||
t1.insert(&[0x01, 0x34], big_value).unwrap();
|
||||
let mut memdb2 = MemoryDB::new();
|
||||
let mut root2 = H256::new();
|
||||
let mut t2 = TrieDBMut::new(&mut memdb2, &mut root2);
|
||||
t2.insert(&[0x01], &big_value.to_vec()).unwrap();
|
||||
t2.insert(&[0x01, 0x23], &big_value.to_vec()).unwrap();
|
||||
t2.insert(&[0x01, 0x34], &big_value.to_vec()).unwrap();
|
||||
t2.insert(&[0x01], big_value).unwrap();
|
||||
t2.insert(&[0x01, 0x23], big_value).unwrap();
|
||||
t2.insert(&[0x01, 0x34], big_value).unwrap();
|
||||
t2.remove(&[0x01]).unwrap();
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ pub fn ordered_trie_root<I>(input: I) -> H256
|
||||
// optimize it later
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, vec)| (rlp::encode(&i).to_vec(), vec))
|
||||
.map(|(i, vec)| (rlp::encode(&i).into_vec(), vec))
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
// then move them to a vector
|
||||
.into_iter()
|
||||
|
Loading…
Reference in New Issue
Block a user