Merge branch 'master' into ui-2
This commit is contained in:
commit
97c67bc259
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -692,7 +692,6 @@ name = "ethkey"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||||
"ethcore-bigint 0.1.3",
|
"ethcore-bigint 0.1.3",
|
||||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -703,10 +702,18 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethstore"
|
name = "ethkey-cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethkey 0.2.0",
|
||||||
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethstore"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
"ethcore-bigint 0.1.3",
|
"ethcore-bigint 0.1.3",
|
||||||
"ethcrypto 0.1.0",
|
"ethcrypto 0.1.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
@ -727,6 +734,15 @@ dependencies = [
|
|||||||
"tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethstore-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethstore 0.1.0",
|
||||||
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethsync"
|
name = "ethsync"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -1569,7 +1585,6 @@ dependencies = [
|
|||||||
"ethcore-util 1.7.0",
|
"ethcore-util 1.7.0",
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
"ethsync 1.7.0",
|
"ethsync 1.7.0",
|
||||||
"evmbin 0.1.0",
|
|
||||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1803,7 +1818,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/paritytech/js-precompiled.git#bebd4fc6ab48574cc976f3570357359f6e831a71"
|
source = "git+https://github.com/paritytech/js-precompiled.git#259741a8ff42f0d4cd0a2c60cc7fab4f0b55aa4f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -40,7 +40,6 @@ ethcore-light = { path = "ethcore/light" }
|
|||||||
ethcore-logger = { path = "logger" }
|
ethcore-logger = { path = "logger" }
|
||||||
ethcore-stratum = { path = "stratum" }
|
ethcore-stratum = { path = "stratum" }
|
||||||
ethkey = { path = "ethkey" }
|
ethkey = { path = "ethkey" }
|
||||||
evmbin = { path = "evmbin" }
|
|
||||||
rlp = { path = "util/rlp" }
|
rlp = { path = "util/rlp" }
|
||||||
rpc-cli = { path = "rpc_cli" }
|
rpc-cli = { path = "rpc_cli" }
|
||||||
parity-hash-fetch = { path = "hash-fetch" }
|
parity-hash-fetch = { path = "hash-fetch" }
|
||||||
@ -86,8 +85,6 @@ jit = ["ethcore/jit"]
|
|||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
test-heavy = ["ethcore/test-heavy"]
|
test-heavy = ["ethcore/test-heavy"]
|
||||||
ethkey-cli = ["ethcore/ethkey-cli"]
|
|
||||||
ethstore-cli = ["ethcore/ethstore-cli"]
|
|
||||||
evm-debug = ["ethcore/evm-debug"]
|
evm-debug = ["ethcore/evm-debug"]
|
||||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||||
slow-blocks = ["ethcore/slow-blocks"]
|
slow-blocks = ["ethcore/slow-blocks"]
|
||||||
@ -104,3 +101,4 @@ lto = false
|
|||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
members = ["ethstore/cli", "ethkey/cli", "evmbin"]
|
||||||
|
@ -66,5 +66,3 @@ dev = ["clippy"]
|
|||||||
default = []
|
default = []
|
||||||
benches = []
|
benches = []
|
||||||
ipc = []
|
ipc = []
|
||||||
ethkey-cli = ["ethkey/cli"]
|
|
||||||
ethstore-cli = ["ethstore/cli"]
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"eip160Transition": 3000000,
|
"eip160Transition": 3000000,
|
||||||
"ecip1010PauseTransition": 3000000,
|
"ecip1010PauseTransition": 3000000,
|
||||||
"ecip1010ContinueTransition": 5000000,
|
"ecip1010ContinueTransition": 5000000,
|
||||||
|
"ecip1017EraRounds": 5000000,
|
||||||
|
|
||||||
"eip161abcTransition": "0x7fffffffffffffff",
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
"eip161dTransition": "0x7fffffffffffffff"
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
|
@ -55,7 +55,7 @@ pub struct TestBlockChainClient {
|
|||||||
/// Blocks.
|
/// Blocks.
|
||||||
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
||||||
/// Mapping of numbers to hashes.
|
/// Mapping of numbers to hashes.
|
||||||
pub numbers: RwLock<HashMap<usize, H256>>,
|
pub numbers: RwLock<HashMap<usize, H256>>,
|
||||||
/// Genesis block hash.
|
/// Genesis block hash.
|
||||||
pub genesis_hash: H256,
|
pub genesis_hash: H256,
|
||||||
/// Last block hash.
|
/// Last block hash.
|
||||||
@ -353,7 +353,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
|||||||
|
|
||||||
impl MiningBlockChainClient for TestBlockChainClient {
|
impl MiningBlockChainClient for TestBlockChainClient {
|
||||||
fn latest_schedule(&self) -> Schedule {
|
fn latest_schedule(&self) -> Schedule {
|
||||||
Schedule::new_post_eip150(24576, true, true, true, true)
|
Schedule::new_post_eip150(24576, true, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||||
|
@ -26,9 +26,8 @@ use account_provider::AccountProvider;
|
|||||||
use block::*;
|
use block::*;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use engines::{Call, Engine, Seal, EngineError};
|
use engines::{Call, Engine, Seal, EngineError};
|
||||||
use header::{Header, BlockNumber};
|
use header::Header;
|
||||||
use error::{Error, TransactionError, BlockError};
|
use error::{Error, TransactionError, BlockError};
|
||||||
use evm::Schedule;
|
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||||
use builtin::Builtin;
|
use builtin::Builtin;
|
||||||
@ -296,11 +295,6 @@ impl Engine for AuthorityRound {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
|
||||||
let eip86 = block_number >= self.params.eip86_transition;
|
|
||||||
Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
||||||
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into();
|
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into();
|
||||||
|
@ -19,9 +19,7 @@ use util::{Address, HashMap};
|
|||||||
use builtin::Builtin;
|
use builtin::Builtin;
|
||||||
use engines::{Engine, Seal};
|
use engines::{Engine, Seal};
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use evm::Schedule;
|
|
||||||
use block::ExecutedBlock;
|
use block::ExecutedBlock;
|
||||||
use header::BlockNumber;
|
|
||||||
|
|
||||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||||
pub struct InstantSeal {
|
pub struct InstantSeal {
|
||||||
@ -58,11 +56,6 @@ impl Engine for InstantSeal {
|
|||||||
&self.builtins
|
&self.builtins
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
|
||||||
let eip86 = block_number >= self.params.eip86_transition;
|
|
||||||
Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn seals_internally(&self) -> Option<bool> { Some(true) }
|
fn seals_internally(&self) -> Option<bool> { Some(true) }
|
||||||
|
|
||||||
fn generate_seal(&self, _block: &ExecutedBlock) -> Seal {
|
fn generate_seal(&self, _block: &ExecutedBlock) -> Seal {
|
||||||
|
@ -147,7 +147,9 @@ pub trait Engine : Sync + Send {
|
|||||||
fn params(&self) -> &CommonParams;
|
fn params(&self) -> &CommonParams;
|
||||||
|
|
||||||
/// Get the EVM schedule for the given `block_number`.
|
/// Get the EVM schedule for the given `block_number`.
|
||||||
fn schedule(&self, block_number: BlockNumber) -> Schedule;
|
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
||||||
|
Schedule::from_params(block_number, self.params())
|
||||||
|
}
|
||||||
|
|
||||||
/// Builtin-contracts we would like to see in the chain.
|
/// Builtin-contracts we would like to see in the chain.
|
||||||
/// (In principle these are just hints for the engine since that has the last word on them.)
|
/// (In principle these are just hints for the engine since that has the last word on them.)
|
||||||
|
@ -38,7 +38,6 @@ use account_provider::AccountProvider;
|
|||||||
use block::*;
|
use block::*;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use engines::{Engine, Seal, EngineError};
|
use engines::{Engine, Seal, EngineError};
|
||||||
use evm::Schedule;
|
|
||||||
use state::CleanupMode;
|
use state::CleanupMode;
|
||||||
use io::IoService;
|
use io::IoService;
|
||||||
use super::signer::EngineSigner;
|
use super::signer::EngineSigner;
|
||||||
@ -404,11 +403,6 @@ impl Engine for Tendermint {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, block_number: BlockNumber) -> Schedule {
|
|
||||||
let eip86 = block_number >= self.params.eip86_transition;
|
|
||||||
Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - view
|
// Chain scoring: total weight is sqrt(U256::max_value())*height - view
|
||||||
let new_difficulty = U256::from(U128::max_value()) + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into();
|
let new_difficulty = U256::from(U128::max_value()) + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into();
|
||||||
|
@ -86,6 +86,8 @@ pub struct EthashParams {
|
|||||||
pub ecip1010_pause_transition: u64,
|
pub ecip1010_pause_transition: u64,
|
||||||
/// Number of first block where ECIP-1010 ends.
|
/// Number of first block where ECIP-1010 ends.
|
||||||
pub ecip1010_continue_transition: u64,
|
pub ecip1010_continue_transition: u64,
|
||||||
|
/// Total block number for one ECIP-1017 era.
|
||||||
|
pub ecip1017_era_rounds: u64,
|
||||||
/// Maximum amount of code that can be deploying into a contract.
|
/// Maximum amount of code that can be deploying into a contract.
|
||||||
pub max_code_size: u64,
|
pub max_code_size: u64,
|
||||||
/// Number of first block where the max gas limit becomes effective.
|
/// Number of first block where the max gas limit becomes effective.
|
||||||
@ -124,6 +126,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
|||||||
eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into),
|
eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into),
|
||||||
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
|
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
|
||||||
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
|
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
|
||||||
|
ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into),
|
||||||
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
||||||
max_gas_limit_transition: p.max_gas_limit_transition.map_or(u64::max_value(), Into::into),
|
max_gas_limit_transition: p.max_gas_limit_transition.map_or(u64::max_value(), Into::into),
|
||||||
max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into),
|
max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into),
|
||||||
@ -196,13 +199,13 @@ impl Engine for Arc<Ethash> {
|
|||||||
} else if block_number < self.ethash_params.eip150_transition {
|
} else if block_number < self.ethash_params.eip150_transition {
|
||||||
Schedule::new_homestead()
|
Schedule::new_homestead()
|
||||||
} else {
|
} else {
|
||||||
Schedule::new_post_eip150(
|
let mut schedule = Schedule::new_post_eip150(
|
||||||
self.ethash_params.max_code_size as usize,
|
self.ethash_params.max_code_size as usize,
|
||||||
block_number >= self.ethash_params.eip160_transition,
|
block_number >= self.ethash_params.eip160_transition,
|
||||||
block_number >= self.ethash_params.eip161abc_transition,
|
block_number >= self.ethash_params.eip161abc_transition,
|
||||||
block_number >= self.ethash_params.eip161d_transition,
|
block_number >= self.ethash_params.eip161d_transition);
|
||||||
block_number >= self.params.eip86_transition
|
schedule.apply_params(block_number, self.params());
|
||||||
)
|
schedule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,13 +258,13 @@ impl Engine for Arc<Ethash> {
|
|||||||
|
|
||||||
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>) -> Result<(), Error> {
|
||||||
let parent_hash = block.fields().header.parent_hash().clone();
|
let parent_hash = block.fields().header.parent_hash().clone();
|
||||||
try!(::engines::common::push_last_hash(block, last_hashes, self, &parent_hash));
|
::engines::common::push_last_hash(block, last_hashes, self, &parent_hash)?;
|
||||||
if block.fields().header.number() == self.ethash_params.dao_hardfork_transition {
|
if block.fields().header.number() == self.ethash_params.dao_hardfork_transition {
|
||||||
let state = block.fields_mut().state;
|
let state = block.fields_mut().state;
|
||||||
for child in &self.ethash_params.dao_hardfork_accounts {
|
for child in &self.ethash_params.dao_hardfork_accounts {
|
||||||
let beneficiary = &self.ethash_params.dao_hardfork_beneficiary;
|
let beneficiary = &self.ethash_params.dao_hardfork_beneficiary;
|
||||||
try!(state.balance(child)
|
state.balance(child)
|
||||||
.and_then(|b| state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty)));
|
.and_then(|b| state.transfer_balance(child, beneficiary, &b, CleanupMode::NoEmpty))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -272,6 +275,8 @@ impl Engine for Arc<Ethash> {
|
|||||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||||
let reward = self.ethash_params.block_reward;
|
let reward = self.ethash_params.block_reward;
|
||||||
let fields = block.fields_mut();
|
let fields = block.fields_mut();
|
||||||
|
let eras_rounds = self.ethash_params.ecip1017_era_rounds;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, fields.header.number());
|
||||||
|
|
||||||
// Bestow block reward
|
// Bestow block reward
|
||||||
fields.state.add_balance(
|
fields.state.add_balance(
|
||||||
@ -283,11 +288,19 @@ impl Engine for Arc<Ethash> {
|
|||||||
// Bestow uncle rewards
|
// Bestow uncle rewards
|
||||||
let current_number = fields.header.number();
|
let current_number = fields.header.number();
|
||||||
for u in fields.uncles.iter() {
|
for u in fields.uncles.iter() {
|
||||||
fields.state.add_balance(
|
if eras == 0 {
|
||||||
u.author(),
|
fields.state.add_balance(
|
||||||
&(reward * U256::from(8 + u.number() - current_number) / U256::from(8)),
|
u.author(),
|
||||||
CleanupMode::NoEmpty
|
&(reward * U256::from(8 + u.number() - current_number) / U256::from(8)),
|
||||||
)?;
|
CleanupMode::NoEmpty
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
fields.state.add_balance(
|
||||||
|
u.author(),
|
||||||
|
&(reward / U256::from(32)),
|
||||||
|
CleanupMode::NoEmpty
|
||||||
|
)
|
||||||
|
}?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit state so that we can actually figure out the state root.
|
// Commit state so that we can actually figure out the state root.
|
||||||
@ -413,6 +426,18 @@ fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256){
|
||||||
|
let eras = if block_number != 0 && block_number % era_rounds == 0 {
|
||||||
|
block_number / era_rounds - 1
|
||||||
|
} else {
|
||||||
|
block_number / era_rounds
|
||||||
|
};
|
||||||
|
for _ in 0..eras {
|
||||||
|
reward = reward / U256::from(5) * U256::from(4);
|
||||||
|
}
|
||||||
|
(eras, reward)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
||||||
impl Ethash {
|
impl Ethash {
|
||||||
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
|
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
|
||||||
@ -524,7 +549,7 @@ mod tests {
|
|||||||
use error::{BlockError, Error};
|
use error::{BlockError, Error};
|
||||||
use header::Header;
|
use header::Header;
|
||||||
use super::super::{new_morden, new_homestead_test};
|
use super::super::{new_morden, new_homestead_test};
|
||||||
use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT};
|
use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT, ecip1017_eras_block_reward};
|
||||||
use rlp;
|
use rlp;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -760,6 +785,42 @@ mod tests {
|
|||||||
assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty);
|
assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn has_valid_ecip1017_eras_block_reward() {
|
||||||
|
let ethparams = EthashParams {
|
||||||
|
// see ethcore/res/ethereum/classic.json
|
||||||
|
ecip1017_era_rounds: 5000000,
|
||||||
|
block_reward: U256::from_str("4563918244F40000").unwrap(),
|
||||||
|
..get_default_ethash_params()
|
||||||
|
};
|
||||||
|
let eras_rounds = ethparams.ecip1017_era_rounds;
|
||||||
|
let reward = ethparams.block_reward;
|
||||||
|
let block_number = 0;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, block_number);
|
||||||
|
assert_eq!(0, eras);
|
||||||
|
assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward);
|
||||||
|
let reward = ethparams.block_reward;
|
||||||
|
let block_number = 5000000;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, block_number);
|
||||||
|
assert_eq!(0, eras);
|
||||||
|
assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward);
|
||||||
|
let reward = ethparams.block_reward;
|
||||||
|
let block_number = 10000000;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, block_number);
|
||||||
|
assert_eq!(1, eras);
|
||||||
|
assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward);
|
||||||
|
let reward = ethparams.block_reward;
|
||||||
|
let block_number = 20000000;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, block_number);
|
||||||
|
assert_eq!(3, eras);
|
||||||
|
assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward);
|
||||||
|
let reward = ethparams.block_reward;
|
||||||
|
let block_number = 80000000;
|
||||||
|
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, block_number);
|
||||||
|
assert_eq!(15, eras);
|
||||||
|
assert_eq!(U256::from_str("271000000000000").unwrap(), reward);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn difficulty_classic_bomb_delay() {
|
fn difficulty_classic_bomb_delay() {
|
||||||
let spec = new_homestead_test();
|
let spec = new_homestead_test();
|
||||||
|
@ -62,7 +62,8 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
/// Built-in contract failed on given input
|
/// Built-in contract failed on given input
|
||||||
BuiltIn(&'static str),
|
BuiltIn(&'static str),
|
||||||
/// Returned on evm internal error. Should never be ignored during development.
|
/// When execution tries to modify the state in static context
|
||||||
|
MutableCallInStaticContext,
|
||||||
/// Likely to cause consensus issues.
|
/// Likely to cause consensus issues.
|
||||||
Internal(String),
|
Internal(String),
|
||||||
}
|
}
|
||||||
@ -90,6 +91,7 @@ impl fmt::Display for Error {
|
|||||||
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
|
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
|
||||||
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
|
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
|
||||||
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
||||||
|
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,25 +53,24 @@ pub enum CreateContractAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Externalities interface for EVMs
|
/// Externalities interface for EVMs
|
||||||
// TODO: [rob] associated error type instead of `trie::Result`. Not all EVMs are trie powered.
|
|
||||||
pub trait Ext {
|
pub trait Ext {
|
||||||
/// Returns a value for given key.
|
/// Returns a value for given key.
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256>;
|
fn storage_at(&self, key: &H256) -> evm::Result<H256>;
|
||||||
|
|
||||||
/// Stores a value for given key.
|
/// Stores a value for given key.
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()>;
|
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()>;
|
||||||
|
|
||||||
/// Determine whether an account exists.
|
/// Determine whether an account exists.
|
||||||
fn exists(&self, address: &Address) -> trie::Result<bool>;
|
fn exists(&self, address: &Address) -> evm::Result<bool>;
|
||||||
|
|
||||||
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
||||||
fn exists_and_not_null(&self, address: &Address) -> trie::Result<bool>;
|
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool>;
|
||||||
|
|
||||||
/// Balance of the origin account.
|
/// Balance of the origin account.
|
||||||
fn origin_balance(&self) -> trie::Result<U256>;
|
fn origin_balance(&self) -> evm::Result<U256>;
|
||||||
|
|
||||||
/// Returns address balance.
|
/// Returns address balance.
|
||||||
fn balance(&self, address: &Address) -> trie::Result<U256>;
|
fn balance(&self, address: &Address) -> evm::Result<U256>;
|
||||||
|
|
||||||
/// Returns the hash of one of the 256 most recent complete blocks.
|
/// Returns the hash of one of the 256 most recent complete blocks.
|
||||||
fn blockhash(&mut self, number: &U256) -> H256;
|
fn blockhash(&mut self, number: &U256) -> H256;
|
||||||
@ -99,13 +98,13 @@ pub trait Ext {
|
|||||||
) -> MessageCallResult;
|
) -> MessageCallResult;
|
||||||
|
|
||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>>;
|
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>>;
|
||||||
|
|
||||||
/// Returns code size at given address
|
/// Returns code size at given address
|
||||||
fn extcodesize(&self, address: &Address) -> trie::Result<usize>;
|
fn extcodesize(&self, address: &Address) -> evm::Result<usize>;
|
||||||
|
|
||||||
/// Creates log entry with given topics and data
|
/// Creates log entry with given topics and data
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()>;
|
||||||
|
|
||||||
/// Should be called when transaction calls `RETURN` opcode.
|
/// Should be called when transaction calls `RETURN` opcode.
|
||||||
/// Returns gas_left if cost of returning the data is not too high.
|
/// Returns gas_left if cost of returning the data is not too high.
|
||||||
@ -113,7 +112,7 @@ pub trait Ext {
|
|||||||
|
|
||||||
/// Should be called when contract commits suicide.
|
/// Should be called when contract commits suicide.
|
||||||
/// Address to which funds should be refunded.
|
/// Address to which funds should be refunded.
|
||||||
fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> ;
|
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> ;
|
||||||
|
|
||||||
/// Returns schedule.
|
/// Returns schedule.
|
||||||
fn schedule(&self) -> &Schedule;
|
fn schedule(&self) -> &Schedule;
|
||||||
|
@ -298,6 +298,7 @@ lazy_static! {
|
|||||||
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special);
|
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special);
|
||||||
arr[RETURN as usize] = InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero);
|
arr[RETURN as usize] = InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero);
|
||||||
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special);
|
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special);
|
||||||
|
arr[STATICCALL as usize] = InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special);
|
||||||
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special);
|
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special);
|
||||||
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special);
|
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special);
|
||||||
arr[REVERT as usize] = InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero);
|
arr[REVERT as usize] = InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero);
|
||||||
@ -584,6 +585,8 @@ pub const DELEGATECALL: Instruction = 0xf4;
|
|||||||
pub const CREATE2: Instruction = 0xfb;
|
pub const CREATE2: Instruction = 0xfb;
|
||||||
/// stop execution and revert state changes. Return output data.
|
/// stop execution and revert state changes. Return output data.
|
||||||
pub const REVERT: Instruction = 0xfd;
|
pub const REVERT: Instruction = 0xfd;
|
||||||
|
/// like CALL but it does not take value, nor modify the state
|
||||||
|
pub const STATICCALL: Instruction = 0xfa;
|
||||||
/// halt execution and register account for later deletion
|
/// halt execution and register account for later deletion
|
||||||
pub const SUICIDE: Instruction = 0xff;
|
pub const SUICIDE: Instruction = 0xff;
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
|
|
||||||
Request::GasMemProvide(gas, mem, Some(requested))
|
Request::GasMemProvide(gas, mem, Some(requested))
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => {
|
instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||||
let gas = Gas::from(schedule.call_gas);
|
let gas = Gas::from(schedule.call_gas);
|
||||||
let mem = cmp::max(
|
let mem = cmp::max(
|
||||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||||
|
@ -199,6 +199,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
|
|
||||||
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
|
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
|
||||||
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
|
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
|
||||||
|
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
|
||||||
(instruction == instructions::REVERT && !schedule.have_revert) {
|
(instruction == instructions::REVERT && !schedule.have_revert) {
|
||||||
|
|
||||||
return Err(evm::Error::BadInstruction {
|
return Err(evm::Error::BadInstruction {
|
||||||
@ -312,14 +313,15 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
||||||
|
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
|
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
|
||||||
let code_address = stack.pop_back();
|
let code_address = stack.pop_back();
|
||||||
let code_address = u256_to_address(&code_address);
|
let code_address = u256_to_address(&code_address);
|
||||||
|
|
||||||
let value = if instruction == instructions::DELEGATECALL {
|
let value = if instruction == instructions::DELEGATECALL || instruction == instructions::STATICCALL {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(stack.pop_back())
|
Some(stack.pop_back())
|
||||||
@ -347,6 +349,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
||||||
|
instructions::STATICCALL => (¶ms.sender, ¶ms.address, true, CallType::StaticCall),
|
||||||
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -405,7 +408,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(H256::from)
|
.map(H256::from)
|
||||||
.collect();
|
.collect();
|
||||||
ext.log(topics, self.mem.read_slice(offset, size));
|
ext.log(topics, self.mem.read_slice(offset, size))?;
|
||||||
},
|
},
|
||||||
instructions::PUSH1...instructions::PUSH32 => {
|
instructions::PUSH1...instructions::PUSH32 => {
|
||||||
let bytes = instructions::get_push_bytes(instruction);
|
let bytes = instructions::get_push_bytes(instruction);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Cost schedule and other parameterisations for the EVM.
|
//! Cost schedule and other parameterisations for the EVM.
|
||||||
|
use spec::CommonParams;
|
||||||
|
|
||||||
/// Definition of the cost schedule and other parameterisations for the EVM.
|
/// Definition of the cost schedule and other parameterisations for the EVM.
|
||||||
pub struct Schedule {
|
pub struct Schedule {
|
||||||
@ -105,6 +106,8 @@ pub struct Schedule {
|
|||||||
pub kill_empty: bool,
|
pub kill_empty: bool,
|
||||||
/// Blockhash instruction gas cost.
|
/// Blockhash instruction gas cost.
|
||||||
pub blockhash_gas: usize,
|
pub blockhash_gas: usize,
|
||||||
|
/// Static Call opcode enabled.
|
||||||
|
pub have_static_call: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schedule {
|
impl Schedule {
|
||||||
@ -119,12 +122,12 @@ impl Schedule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule for the post-EIP-150-era of the Ethereum main net.
|
/// Schedule for the post-EIP-150-era of the Ethereum main net.
|
||||||
pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool, have_metropolis_instructions: bool) -> Schedule {
|
pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule {
|
||||||
Schedule {
|
Schedule {
|
||||||
exceptional_failed_code_deposit: true,
|
exceptional_failed_code_deposit: true,
|
||||||
have_delegate_call: true,
|
have_delegate_call: true,
|
||||||
have_create2: have_metropolis_instructions,
|
have_create2: false,
|
||||||
have_revert: have_metropolis_instructions,
|
have_revert: false,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
||||||
max_depth: 1024,
|
max_depth: 1024,
|
||||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||||
@ -163,13 +166,36 @@ impl Schedule {
|
|||||||
sub_gas_cap_divisor: Some(64),
|
sub_gas_cap_divisor: Some(64),
|
||||||
no_empty: no_empty,
|
no_empty: no_empty,
|
||||||
kill_empty: kill_empty,
|
kill_empty: kill_empty,
|
||||||
blockhash_gas: if have_metropolis_instructions { 350 } else { 20 },
|
blockhash_gas: 20,
|
||||||
|
have_static_call: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schedule for the Metropolis era from common spec params.
|
||||||
|
pub fn from_params(block_number: u64, params: &CommonParams) -> Schedule {
|
||||||
|
let mut schedule = Schedule::new_post_eip150(usize::max_value(), true, true, true);
|
||||||
|
schedule.apply_params(block_number, params);
|
||||||
|
schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply common spec config parameters to the schedule.
|
||||||
|
pub fn apply_params(&mut self, block_number: u64, params: &CommonParams) {
|
||||||
|
self.have_create2 = block_number >= params.eip86_transition;
|
||||||
|
self.have_revert = block_number >= params.eip140_transition;
|
||||||
|
self.have_static_call = block_number >= params.eip214_transition;
|
||||||
|
if block_number >= params.eip210_transition {
|
||||||
|
self.blockhash_gas = 350;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule for the Metropolis of the Ethereum main net.
|
/// Schedule for the Metropolis of the Ethereum main net.
|
||||||
pub fn new_metropolis() -> Schedule {
|
pub fn new_metropolis() -> Schedule {
|
||||||
Self::new_post_eip150(24576, true, true, true, true)
|
let mut schedule = Self::new_post_eip150(24576, true, true, true);
|
||||||
|
schedule.have_create2 = true;
|
||||||
|
schedule.have_revert = true;
|
||||||
|
schedule.have_static_call = true;
|
||||||
|
schedule.blockhash_gas = 350;
|
||||||
|
schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||||
@ -217,6 +243,7 @@ impl Schedule {
|
|||||||
no_empty: false,
|
no_empty: false,
|
||||||
kill_empty: false,
|
kill_empty: false,
|
||||||
blockhash_gas: 20,
|
blockhash_gas: 20,
|
||||||
|
have_static_call: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,28 +87,28 @@ impl Default for Schedule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ext for FakeExt {
|
impl Ext for FakeExt {
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256> {
|
fn storage_at(&self, key: &H256) -> evm::Result<H256> {
|
||||||
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
|
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> {
|
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> {
|
||||||
self.store.insert(key, value);
|
self.store.insert(key, value);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists(&self, address: &Address) -> trie::Result<bool> {
|
fn exists(&self, address: &Address) -> evm::Result<bool> {
|
||||||
Ok(self.balances.contains_key(address))
|
Ok(self.balances.contains_key(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists_and_not_null(&self, address: &Address) -> trie::Result<bool> {
|
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool> {
|
||||||
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
|
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn origin_balance(&self) -> trie::Result<U256> {
|
fn origin_balance(&self) -> evm::Result<U256> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: &Address) -> trie::Result<U256> {
|
fn balance(&self, address: &Address) -> evm::Result<U256> {
|
||||||
Ok(self.balances[address])
|
Ok(self.balances[address])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,26 +152,27 @@ impl Ext for FakeExt {
|
|||||||
MessageCallResult::Success(*gas, ReturnData::empty())
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>> {
|
||||||
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
|
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> trie::Result<usize> {
|
fn extcodesize(&self, address: &Address) -> evm::Result<usize> {
|
||||||
Ok(self.codes.get(address).map_or(0, |c| c.len()))
|
Ok(self.codes.get(address).map_or(0, |c| c.len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()> {
|
||||||
self.logs.push(FakeLogEntry {
|
self.logs.push(FakeLogEntry {
|
||||||
topics: topics,
|
topics: topics,
|
||||||
data: data.to_vec()
|
data: data.to_vec()
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
|
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suicide(&mut self, _refund_address: &Address) -> trie::Result<()> {
|
fn suicide(&mut self, _refund_address: &Address) -> evm::Result<()> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ pub struct Executive<'a, B: 'a + StateBackend, E: 'a + Engine + ?Sized> {
|
|||||||
info: &'a EnvInfo,
|
info: &'a EnvInfo,
|
||||||
engine: &'a E,
|
engine: &'a E,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
static_flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||||
@ -86,16 +87,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
info: info,
|
info: info,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
|
static_flag: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Populates executive from parent properties. Increments executive depth.
|
/// Populates executive from parent properties. Increments executive depth.
|
||||||
pub fn from_parent(state: &'a mut State<B>, info: &'a EnvInfo, engine: &'a E, parent_depth: usize) -> Self {
|
pub fn from_parent(state: &'a mut State<B>, info: &'a EnvInfo, engine: &'a E, parent_depth: usize, static_flag: bool) -> Self {
|
||||||
Executive {
|
Executive {
|
||||||
state: state,
|
state: state,
|
||||||
info: info,
|
info: info,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
depth: parent_depth + 1,
|
depth: parent_depth + 1,
|
||||||
|
static_flag: static_flag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +109,11 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
substate: &'any mut Substate,
|
substate: &'any mut Substate,
|
||||||
output: OutputPolicy<'any, 'any>,
|
output: OutputPolicy<'any, 'any>,
|
||||||
tracer: &'any mut T,
|
tracer: &'any mut T,
|
||||||
vm_tracer: &'any mut V
|
vm_tracer: &'any mut V,
|
||||||
|
static_call: bool,
|
||||||
) -> Externalities<'any, T, V, B, E> where T: Tracer, V: VMTracer {
|
) -> Externalities<'any, T, V, B, E> where T: Tracer, V: VMTracer {
|
||||||
Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output, tracer, vm_tracer)
|
let is_static = self.static_flag || static_call;
|
||||||
|
Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output, tracer, vm_tracer, is_static)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function should be used to execute transaction.
|
/// This function should be used to execute transaction.
|
||||||
@ -246,11 +251,12 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
) -> evm::Result<FinalizationResult> where T: Tracer, V: VMTracer {
|
) -> evm::Result<FinalizationResult> where T: Tracer, V: VMTracer {
|
||||||
|
|
||||||
let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
|
let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
|
||||||
|
let static_call = params.call_type == CallType::StaticCall;
|
||||||
|
|
||||||
// Ordinary execution - keep VM in same thread
|
// Ordinary execution - keep VM in same thread
|
||||||
if (self.depth + 1) % depth_threshold != 0 {
|
if (self.depth + 1) % depth_threshold != 0 {
|
||||||
let vm_factory = self.state.vm_factory();
|
let vm_factory = self.state.vm_factory();
|
||||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call);
|
||||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||||
return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext);
|
return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext);
|
||||||
}
|
}
|
||||||
@ -260,7 +266,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
// https://github.com/aturon/crossbeam/issues/16
|
// https://github.com/aturon/crossbeam/issues/16
|
||||||
crossbeam::scope(|scope| {
|
crossbeam::scope(|scope| {
|
||||||
let vm_factory = self.state.vm_factory();
|
let vm_factory = self.state.vm_factory();
|
||||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call);
|
||||||
|
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
|
vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
|
||||||
@ -280,6 +286,15 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
tracer: &mut T,
|
tracer: &mut T,
|
||||||
vm_tracer: &mut V
|
vm_tracer: &mut V
|
||||||
) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer {
|
) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer {
|
||||||
|
|
||||||
|
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||||
|
if (params.call_type == CallType::StaticCall ||
|
||||||
|
((params.call_type == CallType::Call || params.call_type == CallType::DelegateCall) &&
|
||||||
|
self.static_flag))
|
||||||
|
&& params.value.value() > 0.into() {
|
||||||
|
return Err(evm::Error::MutableCallInStaticContext);
|
||||||
|
}
|
||||||
|
|
||||||
// backup used in case of running out of gas
|
// backup used in case of running out of gas
|
||||||
self.state.checkpoint();
|
self.state.checkpoint();
|
||||||
|
|
||||||
@ -289,7 +304,6 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
if let ActionValue::Transfer(val) = params.value {
|
if let ActionValue::Transfer(val) = params.value {
|
||||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?;
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?;
|
||||||
}
|
}
|
||||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
|
||||||
|
|
||||||
// if destination is builtin, try to execute it
|
// if destination is builtin, try to execute it
|
||||||
if let Some(builtin) = self.engine.builtin(¶ms.code_address, self.info.number) {
|
if let Some(builtin) = self.engine.builtin(¶ms.code_address, self.info.number) {
|
||||||
@ -403,6 +417,12 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
return Err(evm::Error::OutOfGas);
|
return Err(evm::Error::OutOfGas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.call_type == CallType::StaticCall || self.static_flag {
|
||||||
|
let trace_info = tracer.prepare_trace_create(¶ms);
|
||||||
|
tracer.trace_failed_create(trace_info, vec![], evm::Error::MutableCallInStaticContext.into());
|
||||||
|
return Err(evm::Error::MutableCallInStaticContext);
|
||||||
|
}
|
||||||
|
|
||||||
// backup used in case of running out of gas
|
// backup used in case of running out of gas
|
||||||
self.state.checkpoint();
|
self.state.checkpoint();
|
||||||
|
|
||||||
@ -541,6 +561,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
| Err(evm::Error::StackUnderflow {..})
|
| Err(evm::Error::StackUnderflow {..})
|
||||||
| Err(evm::Error::BuiltIn {..})
|
| Err(evm::Error::BuiltIn {..})
|
||||||
| Err(evm::Error::OutOfStack {..})
|
| Err(evm::Error::OutOfStack {..})
|
||||||
|
| Err(evm::Error::MutableCallInStaticContext)
|
||||||
| Ok(FinalizationResult { apply_state: false, .. }) => {
|
| Ok(FinalizationResult { apply_state: false, .. }) => {
|
||||||
self.state.revert_to_checkpoint();
|
self.state.revert_to_checkpoint();
|
||||||
},
|
},
|
||||||
|
@ -40,7 +40,7 @@ pub struct OriginInfo {
|
|||||||
address: Address,
|
address: Address,
|
||||||
origin: Address,
|
origin: Address,
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
value: U256
|
value: U256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OriginInfo {
|
impl OriginInfo {
|
||||||
@ -52,7 +52,7 @@ impl OriginInfo {
|
|||||||
gas_price: params.gas_price,
|
gas_price: params.gas_price,
|
||||||
value: match params.value {
|
value: match params.value {
|
||||||
ActionValue::Transfer(val) | ActionValue::Apparent(val) => val
|
ActionValue::Transfer(val) | ActionValue::Apparent(val) => val
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +71,7 @@ pub struct Externalities<'a, T: 'a, V: 'a, B: 'a, E: 'a + Engine + ?Sized>
|
|||||||
output: OutputPolicy<'a, 'a>,
|
output: OutputPolicy<'a, 'a>,
|
||||||
tracer: &'a mut T,
|
tracer: &'a mut T,
|
||||||
vm_tracer: &'a mut V,
|
vm_tracer: &'a mut V,
|
||||||
|
static_flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E>
|
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E>
|
||||||
@ -87,6 +88,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E>
|
|||||||
output: OutputPolicy<'a, 'a>,
|
output: OutputPolicy<'a, 'a>,
|
||||||
tracer: &'a mut T,
|
tracer: &'a mut T,
|
||||||
vm_tracer: &'a mut V,
|
vm_tracer: &'a mut V,
|
||||||
|
static_flag: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Externalities {
|
Externalities {
|
||||||
state: state,
|
state: state,
|
||||||
@ -99,6 +101,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E>
|
|||||||
output: output,
|
output: output,
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
vm_tracer: vm_tracer,
|
vm_tracer: vm_tracer,
|
||||||
|
static_flag: static_flag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,26 +109,32 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Externalities<'a, T, V, B, E>
|
|||||||
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
||||||
where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized
|
where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized
|
||||||
{
|
{
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256> {
|
fn storage_at(&self, key: &H256) -> evm::Result<H256> {
|
||||||
self.state.storage_at(&self.origin_info.address, key)
|
self.state.storage_at(&self.origin_info.address, key).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> {
|
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> {
|
||||||
self.state.set_storage(&self.origin_info.address, key, value)
|
if self.static_flag {
|
||||||
|
Err(evm::Error::MutableCallInStaticContext)
|
||||||
|
} else {
|
||||||
|
self.state.set_storage(&self.origin_info.address, key, value).map_err(Into::into)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists(&self, address: &Address) -> trie::Result<bool> {
|
fn exists(&self, address: &Address) -> evm::Result<bool> {
|
||||||
self.state.exists(address)
|
self.state.exists(address).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists_and_not_null(&self, address: &Address) -> trie::Result<bool> {
|
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool> {
|
||||||
self.state.exists_and_not_null(address)
|
self.state.exists_and_not_null(address).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn origin_balance(&self) -> trie::Result<U256> { self.balance(&self.origin_info.address) }
|
fn origin_balance(&self) -> evm::Result<U256> {
|
||||||
|
self.balance(&self.origin_info.address).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
fn balance(&self, address: &Address) -> trie::Result<U256> {
|
fn balance(&self, address: &Address) -> evm::Result<U256> {
|
||||||
self.state.balance(address)
|
self.state.balance(address).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blockhash(&mut self, number: &U256) -> H256 {
|
fn blockhash(&mut self, number: &U256) -> H256 {
|
||||||
@ -208,7 +217,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
return ContractCreateResult::Failed
|
return ContractCreateResult::Failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag);
|
||||||
|
|
||||||
// TODO: handle internal error separately
|
// TODO: handle internal error separately
|
||||||
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
||||||
@ -258,7 +267,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
params.value = ActionValue::Transfer(value);
|
params.value = ActionValue::Transfer(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag);
|
||||||
|
|
||||||
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
|
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
|
||||||
Ok((gas_left, return_data)) => MessageCallResult::Success(gas_left, return_data),
|
Ok((gas_left, return_data)) => MessageCallResult::Success(gas_left, return_data),
|
||||||
@ -266,11 +275,11 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>> {
|
||||||
Ok(self.state.code(address)?.unwrap_or_else(|| Arc::new(vec![])))
|
Ok(self.state.code(address)?.unwrap_or_else(|| Arc::new(vec![])))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> trie::Result<usize> {
|
fn extcodesize(&self, address: &Address) -> evm::Result<usize> {
|
||||||
Ok(self.state.code_size(address)?.unwrap_or(0))
|
Ok(self.state.code_size(address)?.unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,18 +321,28 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()> {
|
||||||
use log_entry::LogEntry;
|
use log_entry::LogEntry;
|
||||||
|
|
||||||
|
if self.static_flag {
|
||||||
|
return Err(evm::Error::MutableCallInStaticContext);
|
||||||
|
}
|
||||||
|
|
||||||
let address = self.origin_info.address.clone();
|
let address = self.origin_info.address.clone();
|
||||||
self.substate.logs.push(LogEntry {
|
self.substate.logs.push(LogEntry {
|
||||||
address: address,
|
address: address,
|
||||||
topics: topics,
|
topics: topics,
|
||||||
data: data.to_vec()
|
data: data.to_vec()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> {
|
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> {
|
||||||
|
if self.static_flag {
|
||||||
|
return Err(evm::Error::MutableCallInStaticContext);
|
||||||
|
}
|
||||||
|
|
||||||
let address = self.origin_info.address.clone();
|
let address = self.origin_info.address.clone();
|
||||||
let balance = self.balance(&address)?;
|
let balance = self.balance(&address)?;
|
||||||
if &address == refund_address {
|
if &address == refund_address {
|
||||||
@ -434,7 +453,7 @@ mod tests {
|
|||||||
let mut tracer = NoopTracer;
|
let mut tracer = NoopTracer;
|
||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
|
|
||||||
assert_eq!(ext.env_info().number, 100);
|
assert_eq!(ext.env_info().number, 100);
|
||||||
}
|
}
|
||||||
@ -446,7 +465,7 @@ mod tests {
|
|||||||
let mut tracer = NoopTracer;
|
let mut tracer = NoopTracer;
|
||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
|
|
||||||
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
||||||
|
|
||||||
@ -470,7 +489,7 @@ mod tests {
|
|||||||
let mut tracer = NoopTracer;
|
let mut tracer = NoopTracer;
|
||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
|
|
||||||
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
|
||||||
|
|
||||||
@ -485,7 +504,7 @@ mod tests {
|
|||||||
let mut tracer = NoopTracer;
|
let mut tracer = NoopTracer;
|
||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
@ -513,8 +532,8 @@ mod tests {
|
|||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
ext.log(log_topics, &log_data);
|
ext.log(log_topics, &log_data).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(setup.sub_state.logs.len(), 1);
|
assert_eq!(setup.sub_state.logs.len(), 1);
|
||||||
@ -530,7 +549,7 @@ mod tests {
|
|||||||
let mut vm_tracer = NoopVMTracer;
|
let mut vm_tracer = NoopVMTracer;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
|
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer, false);
|
||||||
ext.suicide(refund_account).unwrap();
|
ext.suicide(refund_account).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +75,10 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> TestExt<'a, T, V, B, E>
|
|||||||
tracer: &'a mut T,
|
tracer: &'a mut T,
|
||||||
vm_tracer: &'a mut V,
|
vm_tracer: &'a mut V,
|
||||||
) -> trie::Result<Self> {
|
) -> trie::Result<Self> {
|
||||||
|
let static_call = false;
|
||||||
Ok(TestExt {
|
Ok(TestExt {
|
||||||
nonce: state.nonce(&address)?,
|
nonce: state.nonce(&address)?,
|
||||||
ext: Externalities::new(state, info, engine, depth, origin_info, substate, output, tracer, vm_tracer),
|
ext: Externalities::new(state, info, engine, depth, origin_info, substate, output, tracer, vm_tracer, static_call),
|
||||||
callcreates: vec![],
|
callcreates: vec![],
|
||||||
sender: address,
|
sender: address,
|
||||||
})
|
})
|
||||||
@ -87,27 +88,27 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> TestExt<'a, T, V, B, E>
|
|||||||
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
||||||
where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized
|
where T: Tracer, V: VMTracer, B: StateBackend, E: Engine + ?Sized
|
||||||
{
|
{
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256> {
|
fn storage_at(&self, key: &H256) -> evm::Result<H256> {
|
||||||
self.ext.storage_at(key)
|
self.ext.storage_at(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()> {
|
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> {
|
||||||
self.ext.set_storage(key, value)
|
self.ext.set_storage(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists(&self, address: &Address) -> trie::Result<bool> {
|
fn exists(&self, address: &Address) -> evm::Result<bool> {
|
||||||
self.ext.exists(address)
|
self.ext.exists(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists_and_not_null(&self, address: &Address) -> trie::Result<bool> {
|
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool> {
|
||||||
self.ext.exists_and_not_null(address)
|
self.ext.exists_and_not_null(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: &Address) -> trie::Result<U256> {
|
fn balance(&self, address: &Address) -> evm::Result<U256> {
|
||||||
self.ext.balance(address)
|
self.ext.balance(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn origin_balance(&self) -> trie::Result<U256> {
|
fn origin_balance(&self) -> evm::Result<U256> {
|
||||||
self.ext.origin_balance()
|
self.ext.origin_balance()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,15 +146,15 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
|||||||
MessageCallResult::Success(*gas, ReturnData::empty())
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>> {
|
||||||
self.ext.extcode(address)
|
self.ext.extcode(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> trie::Result<usize> {
|
fn extcodesize(&self, address: &Address) -> evm::Result<usize> {
|
||||||
self.ext.extcodesize(address)
|
self.ext.extcodesize(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()> {
|
||||||
self.ext.log(topics, data)
|
self.ext.log(topics, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
|||||||
self.ext.ret(gas, data)
|
self.ext.ret(gas, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> {
|
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> {
|
||||||
self.ext.suicide(refund_address)
|
self.ext.suicide(refund_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,8 @@ pub struct CommonParams {
|
|||||||
pub eip210_contract_gas: U256,
|
pub eip210_contract_gas: U256,
|
||||||
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules begin.
|
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules begin.
|
||||||
pub eip211_transition: BlockNumber,
|
pub eip211_transition: BlockNumber,
|
||||||
|
/// Number of first block where EIP-214 rules begin.
|
||||||
|
pub eip214_transition: BlockNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::Params> for CommonParams {
|
impl From<ethjson::spec::Params> for CommonParams {
|
||||||
@ -97,6 +99,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
Into::into),
|
Into::into),
|
||||||
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
||||||
eip211_transition: p.eip211_transition.map_or(BlockNumber::max_value(), Into::into),
|
eip211_transition: p.eip211_transition.map_or(BlockNumber::max_value(), Into::into),
|
||||||
|
eip214_transition: p.eip214_transition.map_or(BlockNumber::max_value(), Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +477,7 @@ mod tests {
|
|||||||
let mut batch = DBTransaction::new();
|
let mut batch = DBTransaction::new();
|
||||||
|
|
||||||
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
||||||
// balance [ 5 5 4 3 2 2 ]
|
// balance [ 5 5 4 3 2 2 ]
|
||||||
let mut s = state_db.boxed_clone_canon(&root_parent);
|
let mut s = state_db.boxed_clone_canon(&root_parent);
|
||||||
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
||||||
s.journal_under(&mut batch, 0, &h0).unwrap();
|
s.journal_under(&mut batch, 0, &h0).unwrap();
|
||||||
|
@ -418,6 +418,7 @@ pub fn get_default_ethash_params() -> EthashParams{
|
|||||||
eip161d_transition: u64::max_value(),
|
eip161d_transition: u64::max_value(),
|
||||||
ecip1010_pause_transition: u64::max_value(),
|
ecip1010_pause_transition: u64::max_value(),
|
||||||
ecip1010_continue_transition: u64::max_value(),
|
ecip1010_continue_transition: u64::max_value(),
|
||||||
|
ecip1017_era_rounds: u64::max_value(),
|
||||||
max_code_size: u64::max_value(),
|
max_code_size: u64::max_value(),
|
||||||
max_gas_limit_transition: u64::max_value(),
|
max_gas_limit_transition: u64::max_value(),
|
||||||
max_gas_limit: U256::max_value(),
|
max_gas_limit: U256::max_value(),
|
||||||
|
@ -36,6 +36,8 @@ pub enum CallType {
|
|||||||
CallCode,
|
CallCode,
|
||||||
/// DELEGATECALL.
|
/// DELEGATECALL.
|
||||||
DelegateCall,
|
DelegateCall,
|
||||||
|
/// STATICCALL
|
||||||
|
StaticCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for CallType {
|
impl Encodable for CallType {
|
||||||
@ -45,6 +47,7 @@ impl Encodable for CallType {
|
|||||||
CallType::Call => 1,
|
CallType::Call => 1,
|
||||||
CallType::CallCode => 2,
|
CallType::CallCode => 2,
|
||||||
CallType::DelegateCall => 3,
|
CallType::DelegateCall => 3,
|
||||||
|
CallType::StaticCall => 4,
|
||||||
};
|
};
|
||||||
Encodable::rlp_append(&v, s);
|
Encodable::rlp_append(&v, s);
|
||||||
}
|
}
|
||||||
@ -57,6 +60,7 @@ impl Decodable for CallType {
|
|||||||
1 => CallType::Call,
|
1 => CallType::Call,
|
||||||
2 => CallType::CallCode,
|
2 => CallType::CallCode,
|
||||||
3 => CallType::DelegateCall,
|
3 => CallType::DelegateCall,
|
||||||
|
4 => CallType::StaticCall,
|
||||||
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -145,6 +149,8 @@ pub enum ExecutionError {
|
|||||||
/// Actual balance.
|
/// Actual balance.
|
||||||
got: U512
|
got: U512
|
||||||
},
|
},
|
||||||
|
/// When execution tries to modify the state in static context
|
||||||
|
MutableCallInStaticContext,
|
||||||
/// Returned when internal evm error occurs.
|
/// Returned when internal evm error occurs.
|
||||||
Internal(String),
|
Internal(String),
|
||||||
/// Returned when generic transaction occurs
|
/// Returned when generic transaction occurs
|
||||||
@ -172,6 +178,7 @@ impl fmt::Display for ExecutionError {
|
|||||||
NotEnoughCash { ref required, ref got } =>
|
NotEnoughCash { ref required, ref got } =>
|
||||||
format!("Cost of transaction exceeds sender balance. {} is required \
|
format!("Cost of transaction exceeds sender balance. {} is required \
|
||||||
but the sender only has {}", required, got),
|
but the sender only has {}", required, got),
|
||||||
|
MutableCallInStaticContext => "Mutable Call in static context".to_owned(),
|
||||||
Internal(ref msg) => msg.clone(),
|
Internal(ref msg) => msg.clone(),
|
||||||
TransactionMalformed(ref err) => format!("Malformed transaction: {}", err),
|
TransactionMalformed(ref err) => format!("Malformed transaction: {}", err),
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,8 @@ pub enum Error {
|
|||||||
/// Returned on evm internal error. Should never be ignored during development.
|
/// Returned on evm internal error. Should never be ignored during development.
|
||||||
/// Likely to cause consensus issues.
|
/// Likely to cause consensus issues.
|
||||||
Internal,
|
Internal,
|
||||||
|
/// When execution tries to modify the state in static context
|
||||||
|
MutableCallInStaticContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a EvmError> for Error {
|
impl<'a> From<&'a EvmError> for Error {
|
||||||
@ -52,6 +54,7 @@ impl<'a> From<&'a EvmError> for Error {
|
|||||||
EvmError::OutOfStack { .. } => Error::OutOfStack,
|
EvmError::OutOfStack { .. } => Error::OutOfStack,
|
||||||
EvmError::BuiltIn { .. } => Error::BuiltIn,
|
EvmError::BuiltIn { .. } => Error::BuiltIn,
|
||||||
EvmError::Internal(_) => Error::Internal,
|
EvmError::Internal(_) => Error::Internal,
|
||||||
|
EvmError::MutableCallInStaticContext => Error::MutableCallInStaticContext,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +76,7 @@ impl fmt::Display for Error {
|
|||||||
OutOfStack => "Out of stack",
|
OutOfStack => "Out of stack",
|
||||||
BuiltIn => "Built-in failed",
|
BuiltIn => "Built-in failed",
|
||||||
Internal => "Internal error",
|
Internal => "Internal error",
|
||||||
|
MutableCallInStaticContext => "Mutable Call In Static Context",
|
||||||
};
|
};
|
||||||
message.fmt(f)
|
message.fmt(f)
|
||||||
}
|
}
|
||||||
@ -89,6 +93,7 @@ impl Encodable for Error {
|
|||||||
OutOfStack => 4,
|
OutOfStack => 4,
|
||||||
Internal => 5,
|
Internal => 5,
|
||||||
BuiltIn => 6,
|
BuiltIn => 6,
|
||||||
|
MutableCallInStaticContext => 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
s.append_internal(&value);
|
s.append_internal(&value);
|
||||||
@ -107,6 +112,7 @@ impl Decodable for Error {
|
|||||||
4 => Ok(OutOfStack),
|
4 => Ok(OutOfStack),
|
||||||
5 => Ok(Internal),
|
5 => Ok(Internal),
|
||||||
6 => Ok(BuiltIn),
|
6 => Ok(BuiltIn),
|
||||||
|
7 => Ok(MutableCallInStaticContext),
|
||||||
_ => Err(DecoderError::Custom("Invalid error type")),
|
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,6 @@ lazy_static = "0.2"
|
|||||||
tiny-keccak = "1.2"
|
tiny-keccak = "1.2"
|
||||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
docopt = { version = "0.7", optional = true }
|
|
||||||
ethcore-bigint = { path = "../util/bigint" }
|
ethcore-bigint = { path = "../util/bigint" }
|
||||||
rust-crypto = "0.2"
|
rust-crypto = "0.2"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
cli = ["docopt"]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "ethkey"
|
|
||||||
path = "src/bin/main.rs"
|
|
||||||
doc = false
|
|
||||||
|
14
ethkey/cli/Cargo.toml
Normal file
14
ethkey/cli/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "ethkey-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ethkey = { path = "../" }
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
docopt = "0.7"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "ethkey"
|
||||||
|
path = "src/main.rs"
|
||||||
|
doc = false
|
@ -1,21 +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/>.
|
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
include!("ethkey.rs");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "cli"))]
|
|
||||||
fn main() {}
|
|
@ -14,7 +14,6 @@ serde_derive = "0.9"
|
|||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
rust-crypto = "0.2.36"
|
rust-crypto = "0.2.36"
|
||||||
tiny-keccak = "1.0"
|
tiny-keccak = "1.0"
|
||||||
docopt = { version = "0.7", optional = true }
|
|
||||||
time = "0.1.34"
|
time = "0.1.34"
|
||||||
itertools = "0.5"
|
itertools = "0.5"
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
@ -24,12 +23,4 @@ smallvec = "0.4"
|
|||||||
parity-wordlist = "1.0"
|
parity-wordlist = "1.0"
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
|
|
||||||
[features]
|
|
||||||
cli = ["docopt"]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "ethstore"
|
|
||||||
path = "src/bin/main.rs"
|
|
||||||
doc = false
|
|
||||||
|
14
ethstore/cli/Cargo.toml
Normal file
14
ethstore/cli/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "ethstore-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
docopt = "0.7"
|
||||||
|
ethstore = { path = "../" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "ethstore"
|
||||||
|
path = "src/main.rs"
|
||||||
|
doc = false
|
@ -1,21 +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/>.
|
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
include!("ethstore.rs");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "cli"))]
|
|
||||||
fn main() {}
|
|
@ -296,6 +296,7 @@ mod test {
|
|||||||
assert!(check_vault_name("vault_with_underscores"));
|
assert!(check_vault_name("vault_with_underscores"));
|
||||||
assert!(check_vault_name("vault-with-dashes"));
|
assert!(check_vault_name("vault-with-dashes"));
|
||||||
assert!(check_vault_name("vault-with-digits-123"));
|
assert!(check_vault_name("vault-with-digits-123"));
|
||||||
|
assert!(check_vault_name("vault中文名字"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "1.7.91",
|
"version": "1.7.92",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
|
@ -129,6 +129,8 @@ export default class Accounts {
|
|||||||
|
|
||||||
set dappsDefaultAddress (value) {
|
set dappsDefaultAddress (value) {
|
||||||
this._dappsDefaultAddress = value.toLowerCase();
|
this._dappsDefaultAddress = value.toLowerCase();
|
||||||
|
|
||||||
|
this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
get (address) {
|
get (address) {
|
||||||
@ -191,10 +193,16 @@ export default class Accounts {
|
|||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
addresses () {
|
allAddresses () {
|
||||||
return Object.keys(this._store);
|
return Object.keys(this._store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountAddresses () {
|
||||||
|
return Object
|
||||||
|
.keys(this._store)
|
||||||
|
.filter((address) => this._store[address].uuid);
|
||||||
|
}
|
||||||
|
|
||||||
map (mapper) {
|
map (mapper) {
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
const register = this.register.bind(this);
|
const register = this.register.bind(this);
|
||||||
|
|
||||||
register('eth_accounts', () => {
|
register('eth_accounts', () => {
|
||||||
return accounts.addresses();
|
return accounts.accountAddresses();
|
||||||
});
|
});
|
||||||
|
|
||||||
register('eth_coinbase', () => {
|
register('eth_coinbase', () => {
|
||||||
@ -73,7 +73,7 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
});
|
});
|
||||||
|
|
||||||
register('parity_defaultAccount', () => {
|
register('parity_defaultAccount', () => {
|
||||||
return accounts.lastAddress;
|
return accounts.dappsDefaultAddress;
|
||||||
});
|
});
|
||||||
|
|
||||||
register('parity_exportAccount', ([address, password]) => {
|
register('parity_exportAccount', ([address, password]) => {
|
||||||
@ -98,11 +98,11 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
});
|
});
|
||||||
|
|
||||||
register('parity_getNewDappsAddresses', () => {
|
register('parity_getNewDappsAddresses', () => {
|
||||||
return [];
|
return accounts.accountAddresses();
|
||||||
});
|
});
|
||||||
|
|
||||||
register('parity_getNewDappsDefaultAddress', () => {
|
register('parity_getNewDappsDefaultAddress', () => {
|
||||||
return accounts.lastAddress;
|
return accounts.dappsDefaultAddress;
|
||||||
});
|
});
|
||||||
|
|
||||||
register('parity_hardwareAccountsInfo', () => {
|
register('parity_hardwareAccountsInfo', () => {
|
||||||
@ -149,6 +149,8 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
|
|
||||||
register('parity_setNewDappsDefaultAddress', ([address]) => {
|
register('parity_setNewDappsDefaultAddress', ([address]) => {
|
||||||
accounts.dappsDefaultAddress = address;
|
accounts.dappsDefaultAddress = address;
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
register('parity_postTransaction', ([tx]) => {
|
register('parity_postTransaction', ([tx]) => {
|
||||||
@ -210,6 +212,10 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
return account.isValidPassword(password);
|
return account.isValidPassword(password);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('parity_upgradeReady', () => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
register('signer_confirmRequest', ([id, modify, password]) => {
|
register('signer_confirmRequest', ([id, modify, password]) => {
|
||||||
const {
|
const {
|
||||||
gasPrice,
|
gasPrice,
|
||||||
|
@ -57,7 +57,7 @@ export default class Signer {
|
|||||||
this._updateSubscriptions('signer_requestsToConfirm', null, requests);
|
this._updateSubscriptions('signer_requestsToConfirm', null, requests);
|
||||||
nextTimeout();
|
nextTimeout();
|
||||||
})
|
})
|
||||||
.catch(nextTimeout);
|
.catch(() => nextTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
_postTransaction (data) {
|
_postTransaction (data) {
|
||||||
|
@ -93,8 +93,8 @@ export default class Http extends JsonRpcBase {
|
|||||||
|
|
||||||
this
|
this
|
||||||
.execute('net_listening')
|
.execute('net_listening')
|
||||||
.then(nextTimeout)
|
.then(() => nextTimeout())
|
||||||
.catch(nextTimeout);
|
.catch(() => nextTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
set url (url) {
|
set url (url) {
|
||||||
|
@ -21,6 +21,10 @@ export default class Middleware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
register (method, handler) {
|
register (method, handler) {
|
||||||
|
if (method in this._handlers) {
|
||||||
|
throw new Error(`${method} is already defined in the middleware!`);
|
||||||
|
}
|
||||||
|
|
||||||
this._handlers[method] = handler;
|
this._handlers[method] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ export default class SignerStore {
|
|||||||
|
|
||||||
this.setLocalHashes(keys);
|
this.setLocalHashes(keys);
|
||||||
})
|
})
|
||||||
.then(nextTimeout)
|
.then(() => nextTimeout())
|
||||||
.catch(nextTimeout);
|
.catch(() => nextTimeout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,10 @@ pub struct EthashParams {
|
|||||||
#[serde(rename="ecip1010ContinueTransition")]
|
#[serde(rename="ecip1010ContinueTransition")]
|
||||||
pub ecip1010_continue_transition: Option<Uint>,
|
pub ecip1010_continue_transition: Option<Uint>,
|
||||||
|
|
||||||
|
/// See main EthashParams docs.
|
||||||
|
#[serde(rename="ecip1017EraRounds")]
|
||||||
|
pub ecip1017_era_rounds: Option<Uint>,
|
||||||
|
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="maxCodeSize")]
|
#[serde(rename="maxCodeSize")]
|
||||||
pub max_code_size: Option<Uint>,
|
pub max_code_size: Option<Uint>,
|
||||||
|
@ -78,6 +78,9 @@ pub struct Params {
|
|||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
#[serde(rename="eip211Transition")]
|
#[serde(rename="eip211Transition")]
|
||||||
pub eip211_transition: Option<Uint>,
|
pub eip211_transition: Option<Uint>,
|
||||||
|
/// See `CommonParams` docs.
|
||||||
|
#[serde(rename="eip214Transition")]
|
||||||
|
pub eip214_transition: Option<Uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -81,7 +81,7 @@ impl ws::MetaExtractor<Metadata> for WsExtractor {
|
|||||||
let dapp = req.origin.as_ref().map(|origin| (&**origin).into()).unwrap_or_default();
|
let dapp = req.origin.as_ref().map(|origin| (&**origin).into()).unwrap_or_default();
|
||||||
metadata.origin = match self.authcodes_path {
|
metadata.origin = match self.authcodes_path {
|
||||||
Some(ref path) => {
|
Some(ref path) => {
|
||||||
let authorization = req.protocols.get(0).and_then(|p| auth_token_hash(&path, p));
|
let authorization = req.protocols.get(0).and_then(|p| auth_token_hash(&path, p, true));
|
||||||
match authorization {
|
match authorization {
|
||||||
Some(id) => Origin::Signer { session: id.into(), dapp: dapp },
|
Some(id) => Origin::Signer { session: id.into(), dapp: dapp },
|
||||||
None => Origin::Ws { session: id.into(), dapp: dapp },
|
None => Origin::Ws { session: id.into(), dapp: dapp },
|
||||||
@ -117,7 +117,7 @@ impl ws::RequestMiddleware for WsExtractor {
|
|||||||
let protocols = req.protocols().ok().unwrap_or_else(Vec::new);
|
let protocols = req.protocols().ok().unwrap_or_else(Vec::new);
|
||||||
if let Some(ref path) = self.authcodes_path {
|
if let Some(ref path) = self.authcodes_path {
|
||||||
if protocols.len() == 1 {
|
if protocols.len() == 1 {
|
||||||
let authorization = auth_token_hash(&path, protocols[0]);
|
let authorization = auth_token_hash(&path, protocols[0], false);
|
||||||
if authorization.is_none() {
|
if authorization.is_none() {
|
||||||
warn!(
|
warn!(
|
||||||
"Blocked connection from {} using invalid token.",
|
"Blocked connection from {} using invalid token.",
|
||||||
@ -142,7 +142,7 @@ fn add_security_headers(res: &mut ws::ws::Response) {
|
|||||||
headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec()));
|
headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth_token_hash(codes_path: &Path, protocol: &str) -> Option<H256> {
|
fn auth_token_hash(codes_path: &Path, protocol: &str, save_file: bool) -> Option<H256> {
|
||||||
let mut split = protocol.split('_');
|
let mut split = protocol.split('_');
|
||||||
let auth = split.next().and_then(|v| v.parse().ok());
|
let auth = split.next().and_then(|v| v.parse().ok());
|
||||||
let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
|
let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
|
||||||
@ -156,9 +156,12 @@ fn auth_token_hash(codes_path: &Path, protocol: &str) -> Option<H256> {
|
|||||||
codes.clear_garbage();
|
codes.clear_garbage();
|
||||||
|
|
||||||
let res = codes.is_valid(&auth, time);
|
let res = codes.is_valid(&auth, time);
|
||||||
// make sure to save back authcodes - it might have been modified
|
|
||||||
if codes.to_file(codes_path).is_err() {
|
if save_file {
|
||||||
warn!(target: "signer", "Couldn't save authorization codes to file.");
|
// make sure to save back authcodes - it might have been modified
|
||||||
|
if codes.to_file(codes_path).is_err() {
|
||||||
|
warn!(target: "signer", "Couldn't save authorization codes to file.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if res {
|
if res {
|
||||||
|
@ -251,6 +251,9 @@ pub enum CallType {
|
|||||||
/// Delegate call
|
/// Delegate call
|
||||||
#[serde(rename="delegatecall")]
|
#[serde(rename="delegatecall")]
|
||||||
DelegateCall,
|
DelegateCall,
|
||||||
|
/// Static call
|
||||||
|
#[serde(rename="staticcall")]
|
||||||
|
StaticCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<executed::CallType> for CallType {
|
impl From<executed::CallType> for CallType {
|
||||||
@ -260,6 +263,7 @@ impl From<executed::CallType> for CallType {
|
|||||||
executed::CallType::Call => CallType::Call,
|
executed::CallType::Call => CallType::Call,
|
||||||
executed::CallType::CallCode => CallType::CallCode,
|
executed::CallType::CallCode => CallType::CallCode,
|
||||||
executed::CallType::DelegateCall => CallType::DelegateCall,
|
executed::CallType::DelegateCall => CallType::DelegateCall,
|
||||||
|
executed::CallType::StaticCall => CallType::StaticCall,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ Description=Parity Daemon
|
|||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
EnvironmentFile=%h/.parity/parity.conf
|
EnvironmentFile=-%h/.parity/parity.conf
|
||||||
ExecStart=/usr/bin/parity $ARGS
|
ExecStart=/usr/bin/parity $ARGS
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
@ -24,7 +24,7 @@ type DeadlineBox<F> where F: Future = BoxFuture<DeadlineStatus<F::Item>, F::Erro
|
|||||||
/// Complete a passed future or fail if it is not completed within timeout.
|
/// Complete a passed future or fail if it is not completed within timeout.
|
||||||
pub fn deadline<F, T>(duration: Duration, handle: &Handle, future: F) -> Result<Deadline<F>, io::Error>
|
pub fn deadline<F, T>(duration: Duration, handle: &Handle, future: F) -> Result<Deadline<F>, io::Error>
|
||||||
where F: Future<Item = T, Error = io::Error> + Send + 'static, T: 'static {
|
where F: Future<Item = T, Error = io::Error> + Send + 'static, T: 'static {
|
||||||
let timeout = try!(Timeout::new(duration, handle)).map(|_| DeadlineStatus::Timeout).boxed();
|
let timeout = Timeout::new(duration, handle)?.map(|_| DeadlineStatus::Timeout).boxed();
|
||||||
let future = future.map(DeadlineStatus::Meet).boxed();
|
let future = future.map(DeadlineStatus::Meet).boxed();
|
||||||
let deadline = Deadline {
|
let deadline = Deadline {
|
||||||
future: timeout.select(future),
|
future: timeout.select(future),
|
||||||
|
@ -709,7 +709,7 @@ impl LightSync {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut light_proto = LightProtocol::new(params.client.clone(), light_params);
|
let mut light_proto = LightProtocol::new(params.client.clone(), light_params);
|
||||||
let sync_handler = Arc::new(try!(SyncHandler::new(params.client.clone())));
|
let sync_handler = Arc::new(SyncHandler::new(params.client.clone())?);
|
||||||
light_proto.add_handler(sync_handler.clone());
|
light_proto.add_handler(sync_handler.clone());
|
||||||
|
|
||||||
for handler in params.handlers {
|
for handler in params.handlers {
|
||||||
@ -719,7 +719,7 @@ impl LightSync {
|
|||||||
(sync_handler, Arc::new(light_proto))
|
(sync_handler, Arc::new(light_proto))
|
||||||
};
|
};
|
||||||
|
|
||||||
let service = try!(NetworkService::new(params.network_config));
|
let service = NetworkService::new(params.network_config)?;
|
||||||
|
|
||||||
Ok(LightSync {
|
Ok(LightSync {
|
||||||
proto: light_proto,
|
proto: light_proto,
|
||||||
|
@ -521,7 +521,8 @@ impl ChainSync {
|
|||||||
sn > fork_block &&
|
sn > fork_block &&
|
||||||
self.highest_block.map_or(true, |highest| highest >= sn && (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD)
|
self.highest_block.map_or(true, |highest| highest >= sn && (highest - sn) <= SNAPSHOT_RESTORE_THRESHOLD)
|
||||||
))
|
))
|
||||||
.filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone())));
|
.filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone())))
|
||||||
|
.filter(|&(_, ref hash)| !self.snapshot.is_known_bad(hash));
|
||||||
|
|
||||||
let mut snapshot_peers = HashMap::new();
|
let mut snapshot_peers = HashMap::new();
|
||||||
let mut max_peers: usize = 0;
|
let mut max_peers: usize = 0;
|
||||||
@ -1075,10 +1076,18 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check service status
|
// check service status
|
||||||
match io.snapshot_service().status() {
|
let status = io.snapshot_service().status();
|
||||||
|
match status {
|
||||||
RestorationStatus::Inactive | RestorationStatus::Failed => {
|
RestorationStatus::Inactive | RestorationStatus::Failed => {
|
||||||
trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id);
|
trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id);
|
||||||
self.state = SyncState::WaitingPeers;
|
self.state = SyncState::WaitingPeers;
|
||||||
|
|
||||||
|
// only note bad if restoration failed.
|
||||||
|
if let (Some(hash), RestorationStatus::Failed) = (self.snapshot.snapshot_hash(), status) {
|
||||||
|
trace!(target: "sync", "Noting snapshot hash {} as bad", hash);
|
||||||
|
self.snapshot.note_bad(hash);
|
||||||
|
}
|
||||||
|
|
||||||
self.snapshot.clear();
|
self.snapshot.clear();
|
||||||
self.continue_sync(io);
|
self.continue_sync(io);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -551,7 +551,7 @@ impl<L: AsLightClient> LightSync<L> {
|
|||||||
peers: RwLock::new(HashMap::new()),
|
peers: RwLock::new(HashMap::new()),
|
||||||
pending_reqs: Mutex::new(HashSet::new()),
|
pending_reqs: Mutex::new(HashSet::new()),
|
||||||
client: client,
|
client: client,
|
||||||
rng: Mutex::new(try!(OsRng::new())),
|
rng: Mutex::new(OsRng::new()?),
|
||||||
state: Mutex::new(SyncState::Idle),
|
state: Mutex::new(SyncState::Idle),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ impl From<DecoderError> for BasicError {
|
|||||||
|
|
||||||
impl fmt::Display for BasicError {
|
impl fmt::Display for BasicError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
try!(write!(f, "Header response verification error: "));
|
write!(f, "Header response verification error: ")?;
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
BasicError::WrongSkip(ref exp, ref got)
|
BasicError::WrongSkip(ref exp, ref got)
|
||||||
@ -78,13 +78,13 @@ pub fn verify(headers: &[encoded::Header], request: &HeadersRequest) -> Result<V
|
|||||||
|
|
||||||
let reverse = request.reverse;
|
let reverse = request.reverse;
|
||||||
|
|
||||||
try!(Max(request.max as usize).verify(&headers, reverse));
|
Max(request.max as usize).verify(&headers, reverse)?;
|
||||||
match request.start {
|
match request.start {
|
||||||
HashOrNumber::Number(ref num) => try!(StartsAtNumber(*num).verify(&headers, reverse)),
|
HashOrNumber::Number(ref num) => StartsAtNumber(*num).verify(&headers, reverse)?,
|
||||||
HashOrNumber::Hash(ref hash) => try!(StartsAtHash(*hash).verify(&headers, reverse)),
|
HashOrNumber::Hash(ref hash) => StartsAtHash(*hash).verify(&headers, reverse)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
try!(SkipsBetween(request.skip).verify(&headers, reverse));
|
SkipsBetween(request.skip).verify(&headers, reverse)?;
|
||||||
|
|
||||||
Ok(headers)
|
Ok(headers)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ pub struct Snapshot {
|
|||||||
downloading_chunks: HashSet<H256>,
|
downloading_chunks: HashSet<H256>,
|
||||||
completed_chunks: HashSet<H256>,
|
completed_chunks: HashSet<H256>,
|
||||||
snapshot_hash: Option<H256>,
|
snapshot_hash: Option<H256>,
|
||||||
|
bad_hashes: HashSet<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snapshot {
|
impl Snapshot {
|
||||||
@ -42,6 +43,7 @@ impl Snapshot {
|
|||||||
downloading_chunks: HashSet::new(),
|
downloading_chunks: HashSet::new(),
|
||||||
completed_chunks: HashSet::new(),
|
completed_chunks: HashSet::new(),
|
||||||
snapshot_hash: None,
|
snapshot_hash: None,
|
||||||
|
bad_hashes: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +111,16 @@ impl Snapshot {
|
|||||||
self.downloading_chunks.remove(hash);
|
self.downloading_chunks.remove(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// note snapshot hash as bad.
|
||||||
|
pub fn note_bad(&mut self, hash: H256) {
|
||||||
|
self.bad_hashes.insert(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// whether snapshot hash is known to be bad.
|
||||||
|
pub fn is_known_bad(&self, hash: &H256) -> bool {
|
||||||
|
self.bad_hashes.contains(hash)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snapshot_hash(&self) -> Option<H256> {
|
pub fn snapshot_hash(&self) -> Option<H256> {
|
||||||
self.snapshot_hash
|
self.snapshot_hash
|
||||||
}
|
}
|
||||||
@ -205,5 +217,15 @@ mod test {
|
|||||||
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
|
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
|
||||||
assert_eq!(snapshot.snapshot_hash(), Some(manifest.into_rlp().sha3()));
|
assert_eq!(snapshot.snapshot_hash(), Some(manifest.into_rlp().sha3()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tracks_known_bad() {
|
||||||
|
let mut snapshot = Snapshot::new();
|
||||||
|
let hash = H256::random();
|
||||||
|
|
||||||
|
assert_eq!(snapshot.is_known_bad(&hash), false);
|
||||||
|
snapshot.note_bad(hash);
|
||||||
|
assert_eq!(snapshot.is_known_bad(&hash), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +239,21 @@ mod tests {
|
|||||||
jdb.commit_batch(3, &b"3".sha3(), Some((0, b"0".sha3()))).unwrap();
|
jdb.commit_batch(3, &b"3".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
assert!(jdb.contains(&h));
|
assert!(jdb.contains(&h));
|
||||||
jdb.commit_batch(4, &b"4".sha3(), Some((1, b"1".sha3()))).unwrap();
|
jdb.commit_batch(4, &b"4".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
assert!(jdb.contains(&h));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn multiple_owed_removal_not_allowed() {
|
||||||
|
let mut jdb = ArchiveDB::new_temp();
|
||||||
|
let h = jdb.insert(b"foo");
|
||||||
|
jdb.commit_batch(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.contains(&h));
|
||||||
|
jdb.remove(&h);
|
||||||
|
jdb.remove(&h);
|
||||||
|
// commit_batch would call journal_under(),
|
||||||
|
// and we don't allow multiple owned removals.
|
||||||
|
jdb.commit_batch(1, &b"1".sha3(), None).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user