Merge branch 'master' into bettermining

This commit is contained in:
Gav Wood
2016-03-23 14:18:16 +01:00
29 changed files with 840 additions and 183 deletions

View File

@@ -0,0 +1,35 @@
{
"name": "Morden",
"engineName": "NullEngine",
"params": {
"accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x0",
"maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2"
},
"genesis": {
"nonce": "0x00006d6f7264656e",
"difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
}
}

View File

@@ -3,7 +3,7 @@
"engineName": "NullEngine",
"params": {
"accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0xfffa2990",
"frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388",

View File

@@ -21,7 +21,7 @@ use util::*;
use util::panics::*;
use views::BlockView;
use error::*;
use header::{BlockNumber};
use header::{BlockNumber, Header};
use state::State;
use spec::Spec;
use engine::Engine;
@@ -36,7 +36,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient};
use client::{BlockId, TransactionId, UncleId, ClientConfig, BlockChainClient};
use env_info::EnvInfo;
use executive::{Executive, Executed};
use receipt::LocalizedReceipt;
@@ -549,6 +549,11 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
}
fn uncle(&self, id: UncleId) -> Option<Header> {
let index = id.1;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
self.transaction_address(id).and_then(|address| {
let t = self.chain.block(&address.block_hash)

View File

@@ -20,7 +20,7 @@ use util::hash::H256;
use header::BlockNumber;
/// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum BlockId {
/// Block's sha3.
/// Querying by hash is always faster.
@@ -34,7 +34,7 @@ pub enum BlockId {
}
/// Uniquely identifies transaction.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum TransactionId {
/// Transaction's sha3.
Hash(H256),
@@ -42,3 +42,11 @@ pub enum TransactionId {
/// Querying by block position is always faster.
Location(BlockId, usize)
}
/// Uniquely identifies Uncle.
pub struct UncleId (
/// Block id.
pub BlockId,
/// Position in block.
pub usize
);

View File

@@ -23,7 +23,7 @@ mod test_client;
pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
pub use self::ids::{BlockId, TransactionId};
pub use self::ids::{BlockId, TransactionId, UncleId};
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use executive::Executed;
@@ -34,7 +34,7 @@ use util::numbers::U256;
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
use block::{ClosedBlock, SealedBlock};
use header::BlockNumber;
use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
@@ -77,6 +77,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get transaction with given hash.
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleId) -> Option<Header>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;

View File

@@ -19,7 +19,7 @@
use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId};
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@@ -52,6 +52,8 @@ pub struct TestBlockChainClient {
pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result.
pub execution_result: RwLock<Option<Executed>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionId, LocalizedReceipt>>,
}
#[derive(Clone)]
@@ -87,12 +89,18 @@ impl TestBlockChainClient {
storage: RwLock::new(HashMap::new()),
code: RwLock::new(HashMap::new()),
execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
};
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone();
client
}
/// Set the transaction receipt result
pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) {
self.receipts.write().unwrap().insert(id, receipt);
}
/// Set the execution result.
pub fn set_execution_result(&self, result: Executed) {
*self.execution_result.write().unwrap() = Some(result);
@@ -224,10 +232,14 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn transaction_receipt(&self, _id: TransactionId) -> Option<LocalizedReceipt> {
fn uncle(&self, _id: UncleId) -> Option<BlockHeader> {
unimplemented!();
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
self.receipts.read().unwrap().get(&id).cloned()
}
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
unimplemented!();
}

View File

@@ -103,4 +103,14 @@ pub trait Engine : Sync + Send {
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); }
// TODO: sealing stuff - though might want to leave this for later.
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to u64, or 0 if that fails.
fn u64_param(&self, name: &str) -> u64 {
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a))
}
/// Get a named parameter from the `spec()`'s `engine_params` item and convert to U256, or 0 if that fails.
fn u256_param(&self, name: &str) -> U256 {
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a))
}
}

View File

@@ -57,16 +57,6 @@ impl Ethash {
u256_params: RwLock::new(HashMap::new())
}
}
fn u64_param(&self, name: &str) -> u64 {
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
}
fn u256_param(&self, name: &str) -> U256 {
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
}
}
impl Engine for Ethash {
@@ -199,6 +189,16 @@ impl Engine for Ethash {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
fn u64_param(&self, name: &str) -> u64 {
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
}
fn u256_param(&self, name: &str) -> U256 {
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
}
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self

View File

@@ -348,12 +348,13 @@ impl evm::Evm for Interpreter {
impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem(&self,
ext: &evm::Ext,
instruction: Instruction,
mem: &mut Memory,
stack: &Stack<U256>
) -> Result<(U256, usize), evm::Error> {
fn get_gas_cost_mem(
&self,
ext: &evm::Ext,
instruction: Instruction,
mem: &mut Memory,
stack: &Stack<U256>
) -> Result<(U256, usize), evm::Error> {
let schedule = ext.schedule();
let info = instructions::get_info(instruction);
@@ -522,15 +523,16 @@ impl Interpreter {
}
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(&self,
gas: Gas,
params: &ActionParams,
ext: &mut evm::Ext,
instruction: Instruction,
code: &mut CodeReader,
mem: &mut Memory,
stack: &mut Stack<U256>
) -> Result<InstructionResult, evm::Error> {
fn exec_instruction(
&self,
gas: Gas,
params: &ActionParams,
ext: &mut evm::Ext,
instruction: Instruction,
code: &mut CodeReader,
mem: &mut Memory,
stack: &mut Stack<U256>
) -> Result<InstructionResult, evm::Error> {
match instruction {
instructions::JUMP => {
let jump = stack.pop_back();

View File

@@ -196,6 +196,7 @@ impl<'a> Executive<'a> {
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory();
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create().exec(params, &mut ext);
}
@@ -245,35 +246,49 @@ impl<'a> Executive<'a> {
Err(evm::Error::OutOfGas)
}
}
} else if params.code.is_some() {
} else {
// if destination is a contract, do normal message call
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some());
// don't trace if it's DELEGATECALL or CALLCODE.
let should_trace = if let ActionValue::Transfer(_) = params.value {
params.code_address == params.address && substate.subtraces.is_some()
} else { false };
// transaction tracing stuff. None if there's no tracing.
let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_call(&params), self.depth));
let mut trace_output = trace_info.as_ref().map(|_| vec![]);
let (mut trace_info, mut trace_output) = if should_trace {
(Some((TraceAction::from_call(&params), self.depth)), Some(vec![]))
} else { (None, None) };
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()))
};
if params.code.is_some() {
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(should_trace);
// if there's tracing, make up trace_info's result with trace_output and some arithmetic.
if let Some((TraceAction::Call(ref mut c), _)) = trace_info {
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: qed")));
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()))
};
trace!(target: "executive", "res={:?}", res);
// if there's tracing, make up trace_info's result with trace_output and some arithmetic.
if let Some((TraceAction::Call(ref mut c), _)) = trace_info {
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: so should_trace: qed")));
}
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
self.enact_result(&res, substate, unconfirmed_substate, trace_info);
trace!(target: "executive", "enacted: substate={:?}\n", substate);
res
} else {
// otherwise it's just a basic transaction, only do tracing, if necessary.
trace!(target: "executive", "Basic message (send funds) should_trace={}", should_trace);
self.state.clear_snapshot();
if let Some((TraceAction::Call(ref mut c), _)) = trace_info {
c.result = Some((x!(0), vec![]));
}
substate.accrue_trace(if should_trace {Some(vec![])} else {None}, trace_info);
Ok(params.gas)
}
trace!(target: "executive", "sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
self.enact_result(&res, substate, unconfirmed_substate, trace_info);
trace!(target: "executive", "enacted: substate={:?}\n", substate);
res
} else {
// otherwise, nothing
self.state.clear_snapshot();
Ok(params.gas)
}
}
@@ -530,6 +545,7 @@ mod tests {
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.code_address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
@@ -627,6 +643,7 @@ mod tests {
assert_eq!(substate.subtraces, expected_trace);
assert_eq!(gas_left, U256::from(96_776));
}
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
fn test_create_contract_value_too_high(factory: Factory) {
// code:

View File

@@ -78,7 +78,7 @@ impl FromJson for LogEntry {
}
/// Log localized in a blockchain.
#[derive(Default, Debug, PartialEq)]
#[derive(Default, Debug, PartialEq, Clone)]
pub struct LocalizedLogEntry {
/// Plain log entry.
pub entry: LogEntry,

View File

@@ -41,7 +41,16 @@ impl Engine for NullEngine {
fn vm_factory(&self) -> &Factory {
&self.factory
}
fn name(&self) -> &str { "NullEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
if env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
Schedule::new_frontier()
} else {
Schedule::new_homestead()
}
}
}

View File

@@ -76,6 +76,7 @@ impl HeapSizeOf for Receipt {
}
/// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt {
/// Transaction hash.
pub transaction_hash: H256,

View File

@@ -333,6 +333,9 @@ impl Spec {
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_morden.json")) }
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
pub fn new_homestead_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_homestead_morden.json")) }
}
#[cfg(test)]

View File

@@ -481,6 +481,44 @@ fn should_trace_call_transaction() {
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_basic_call_transaction() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000);
let engine = TestEngine::new(5, Factory::default());
let t = Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(100_000),
action: Action::Call(x!(0xa)),
value: x!(100),
data: vec![],
}.sign(&"".sha3());
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
to: x!(0xa),
value: x!(100),
gas: x!(79000),
input: vec![],
result: Some((x!(0), vec![]))
}),
subs: vec![]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_not_trace_call_transaction_to_builtin() {
init_log();
@@ -544,6 +582,87 @@ fn should_not_trace_subcall_transaction_to_builtin() {
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_not_trace_callcode() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000);
let engine = Spec::new_test().to_engine().unwrap();
let t = Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(100_000),
action: Action::Call(x!(0xa)),
value: x!(0),
data: vec![],
}.sign(&"".sha3());
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap());
let result = state.apply(&info, engine.deref(), &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
to: x!(0xa),
value: x!(0),
gas: x!(79000),
input: vec![],
result: Some((x!(64), vec![]))
}),
subs: vec![]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_not_trace_delegatecall() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000);
info.number = 0x789b0;
let engine = Spec::new_test().to_engine().unwrap();
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
let t = Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(100_000),
action: Action::Call(x!(0xa)),
value: x!(0),
data: vec![],
}.sign(&"".sha3());
state.init_code(&x!(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap());
let result = state.apply(&info, engine.deref(), &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
to: x!(0xa),
value: x!(0),
gas: x!(79000),
input: vec![],
result: Some((x!(61), vec![]))
}),
subs: vec![]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_failed_call_transaction() {
init_log();
@@ -584,6 +703,7 @@ fn should_trace_failed_call_transaction() {
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_call_with_subcall_transaction() {
init_log();
@@ -635,6 +755,95 @@ fn should_trace_call_with_subcall_transaction() {
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_call_with_basic_subcall_transaction() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000);
let engine = TestEngine::new(5, Factory::default());
let t = Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(100_000),
action: Action::Call(x!(0xa)),
value: x!(100),
data: vec![],
}.sign(&"".sha3());
state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
to: x!(0xa),
value: x!(100),
gas: x!(79000),
input: vec![],
result: Some((x!(31761), vec![]))
}),
subs: vec![Trace {
depth: 1,
action: TraceAction::Call(TraceCall {
from: x!(0xa),
to: x!(0xb),
value: x!(69),
gas: x!(2300),
input: vec![],
result: Some((x!(0), vec![]))
}),
subs: vec![]
}]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
init_log();
let temp = RandomTempPath::new();
let mut state = get_temp_state_in(temp.as_path());
let mut info = EnvInfo::default();
info.gas_limit = x!(1_000_000);
let engine = TestEngine::new(5, Factory::default());
let t = Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(100_000),
action: Action::Call(x!(0xa)),
value: x!(100),
data: vec![],
}.sign(&"".sha3());
state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
state.add_balance(t.sender().as_ref().unwrap(), &x!(100));
let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = Some(Trace {
depth: 0,
action: TraceAction::Call(TraceCall {
from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"),
to: x!(0xa),
value: x!(100),
gas: x!(79000),
input: vec![],
result: Some((x!(31761), vec![]))
}),
subs: vec![]
});
assert_eq!(result.trace, expected_trace);
}
#[test]
fn should_trace_failed_subcall_transaction() {
init_log();