Merge branch 'master' into bettermining

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

1
Cargo.lock generated
View File

@ -15,6 +15,7 @@ dependencies = [
"ethsync 1.1.0", "ethsync 1.1.0",
"fdlimit 0.1.0", "fdlimit 0.1.0",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -18,6 +18,7 @@ time = "0.1"
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" } ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
fdlimit = { path = "util/fdlimit" } fdlimit = { path = "util/fdlimit" }
daemonize = "0.2" daemonize = "0.2"
num_cpus = "0.2"
number_prefix = "0.2" number_prefix = "0.2"
rpassword = "0.1" rpassword = "0.1"
clippy = { version = "0.0.54", optional = true } clippy = { version = "0.0.54", optional = true }

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", "engineName": "NullEngine",
"params": { "params": {
"accountStartNonce": "0x0100000", "accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0xfffa2990", "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20", "maximumExtraDataSize": "0x20",
"tieBreakingGas": false, "tieBreakingGas": false,
"minGasLimit": "0x1388", "minGasLimit": "0x1388",

View File

@ -21,7 +21,7 @@ use util::*;
use util::panics::*; use util::panics::*;
use views::BlockView; use views::BlockView;
use error::*; use error::*;
use header::{BlockNumber}; use header::{BlockNumber, Header};
use state::State; use state::State;
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
@ -36,7 +36,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient}; use client::{BlockId, TransactionId, UncleId, ClientConfig, BlockChainClient};
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed}; use executive::{Executive, Executed};
use receipt::LocalizedReceipt; 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)) 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> { fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
self.transaction_address(id).and_then(|address| { self.transaction_address(id).and_then(|address| {
let t = self.chain.block(&address.block_hash) let t = self.chain.block(&address.block_hash)

View File

@ -20,7 +20,7 @@ use util::hash::H256;
use header::BlockNumber; use header::BlockNumber;
/// Uniquely identifies block. /// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum BlockId { pub enum BlockId {
/// Block's sha3. /// Block's sha3.
/// Querying by hash is always faster. /// Querying by hash is always faster.
@ -34,7 +34,7 @@ pub enum BlockId {
} }
/// Uniquely identifies transaction. /// Uniquely identifies transaction.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum TransactionId { pub enum TransactionId {
/// Transaction's sha3. /// Transaction's sha3.
Hash(H256), Hash(H256),
@ -42,3 +42,11 @@ pub enum TransactionId {
/// Querying by block position is always faster. /// Querying by block position is always faster.
Location(BlockId, usize) 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::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig}; 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 self::test_client::{TestBlockChainClient, EachBlockWith};
pub use executive::Executed; pub use executive::Executed;
@ -34,7 +34,7 @@ use util::numbers::U256;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{ClosedBlock, SealedBlock}; use block::{ClosedBlock, SealedBlock};
use header::BlockNumber; use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use filter::Filter; use filter::Filter;
@ -77,6 +77,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get transaction with given hash. /// Get transaction with given hash.
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>; 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. /// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>; fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;

View File

@ -19,7 +19,7 @@
use util::*; use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute; 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 header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -52,6 +52,8 @@ pub struct TestBlockChainClient {
pub code: RwLock<HashMap<Address, Bytes>>, pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result. /// Execution result.
pub execution_result: RwLock<Option<Executed>>, pub execution_result: RwLock<Option<Executed>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionId, LocalizedReceipt>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -87,12 +89,18 @@ impl TestBlockChainClient {
storage: RwLock::new(HashMap::new()), storage: RwLock::new(HashMap::new()),
code: RwLock::new(HashMap::new()), code: RwLock::new(HashMap::new()),
execution_result: RwLock::new(None), execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
}; };
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone(); client.genesis_hash = client.last_hash.read().unwrap().clone();
client 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. /// Set the execution result.
pub fn set_execution_result(&self, result: Executed) { pub fn set_execution_result(&self, result: Executed) {
*self.execution_result.write().unwrap() = Some(result); *self.execution_result.write().unwrap() = Some(result);
@ -224,10 +232,14 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn transaction_receipt(&self, _id: TransactionId) -> Option<LocalizedReceipt> { fn uncle(&self, _id: UncleId) -> Option<BlockHeader> {
unimplemented!(); 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>> { fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
unimplemented!(); 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); } 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. // 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()) 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 { impl Engine for Ethash {
@ -199,6 +189,16 @@ impl Engine for Ethash {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender 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 #[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 { impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem(&self, fn get_gas_cost_mem(
ext: &evm::Ext, &self,
instruction: Instruction, ext: &evm::Ext,
mem: &mut Memory, instruction: Instruction,
stack: &Stack<U256> mem: &mut Memory,
) -> Result<(U256, usize), evm::Error> { stack: &Stack<U256>
) -> Result<(U256, usize), evm::Error> {
let schedule = ext.schedule(); let schedule = ext.schedule();
let info = instructions::get_info(instruction); let info = instructions::get_info(instruction);
@ -522,15 +523,16 @@ impl Interpreter {
} }
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(&self, fn exec_instruction(
gas: Gas, &self,
params: &ActionParams, gas: Gas,
ext: &mut evm::Ext, params: &ActionParams,
instruction: Instruction, ext: &mut evm::Ext,
code: &mut CodeReader, instruction: Instruction,
mem: &mut Memory, code: &mut CodeReader,
stack: &mut Stack<U256> mem: &mut Memory,
) -> Result<InstructionResult, evm::Error> { stack: &mut Stack<U256>
) -> Result<InstructionResult, evm::Error> {
match instruction { match instruction {
instructions::JUMP => { instructions::JUMP => {
let jump = stack.pop_back(); 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 { if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory(); 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); return vm_factory.create().exec(params, &mut ext);
} }
@ -245,35 +246,49 @@ impl<'a> Executive<'a> {
Err(evm::Error::OutOfGas) Err(evm::Error::OutOfGas)
} }
} }
} else if params.code.is_some() { } else {
// if destination is a contract, do normal message call // if destination is a contract, do normal message call
// part of substate that may be reverted // don't trace if it's DELEGATECALL or CALLCODE.
let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); 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. // 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_info, mut trace_output) = if should_trace {
let mut trace_output = trace_info.as_ref().map(|_| vec![]); (Some((TraceAction::from_call(&params), self.depth)), Some(vec![]))
} else { (None, None) };
let res = { if params.code.is_some() {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut())) // 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. let res = {
if let Some((TraceAction::Call(ref mut c), _)) = trace_info { self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()))
c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: qed"))); };
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 next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default(); let mut params = ActionParams::default();
params.address = address.clone(); params.address = address.clone();
params.code_address = address.clone();
params.sender = sender.clone(); params.sender = sender.clone();
params.origin = sender.clone(); params.origin = sender.clone();
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
@ -627,6 +643,7 @@ mod tests {
assert_eq!(substate.subtraces, expected_trace); assert_eq!(substate.subtraces, expected_trace);
assert_eq!(gas_left, U256::from(96_776)); 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} 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) { fn test_create_contract_value_too_high(factory: Factory) {
// code: // code:

View File

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

View File

@ -41,7 +41,16 @@ impl Engine for NullEngine {
fn vm_factory(&self) -> &Factory { fn vm_factory(&self) -> &Factory {
&self.factory &self.factory
} }
fn name(&self) -> &str { "NullEngine" } fn name(&self) -> &str { "NullEngine" }
fn spec(&self) -> &Spec { &self.spec } 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. /// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt { pub struct LocalizedReceipt {
/// Transaction hash. /// Transaction hash.
pub transaction_hash: H256, 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. /// 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")) } 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)] #[cfg(test)]

View File

@ -481,6 +481,44 @@ fn should_trace_call_transaction() {
assert_eq!(result.trace, expected_trace); 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] #[test]
fn should_not_trace_call_transaction_to_builtin() { fn should_not_trace_call_transaction_to_builtin() {
init_log(); init_log();
@ -544,6 +582,87 @@ fn should_not_trace_subcall_transaction_to_builtin() {
assert_eq!(result.trace, expected_trace); 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] #[test]
fn should_trace_failed_call_transaction() { fn should_trace_failed_call_transaction() {
init_log(); init_log();
@ -584,6 +703,7 @@ fn should_trace_failed_call_transaction() {
assert_eq!(result.trace, expected_trace); assert_eq!(result.trace, expected_trace);
} }
#[test] #[test]
fn should_trace_call_with_subcall_transaction() { fn should_trace_call_with_subcall_transaction() {
init_log(); init_log();
@ -635,6 +755,95 @@ fn should_trace_call_with_subcall_transaction() {
assert_eq!(result.trace, expected_trace); 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] #[test]
fn should_trace_failed_subcall_transaction() { fn should_trace_failed_subcall_transaction() {
init_log(); init_log();

View File

@ -65,7 +65,7 @@ pub use transaction_queue::{TransactionQueue, AccountDetails};
pub use miner::{Miner}; pub use miner::{Miner};
use std::sync::Mutex; use std::sync::Mutex;
use util::{H256, Address, Bytes}; use util::{H256, Address, FixedHash, Bytes};
use ethcore::client::{BlockChainClient}; use ethcore::client::{BlockChainClient};
use ethcore::block::{ClosedBlock}; use ethcore::block::{ClosedBlock};
use ethcore::error::{Error}; use ethcore::error::{Error};
@ -77,6 +77,12 @@ pub trait MinerService : Send + Sync {
/// Returns miner's status. /// Returns miner's status.
fn status(&self) -> MinerStatus; fn status(&self) -> MinerStatus;
/// Get the author that we will seal blocks as.
fn author(&self) -> Address { Address::zero() }
/// Get the extra_data that we will seal blocks wuth.
fn extra_data(&self) -> Bytes { vec![] }
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Vec<Result<(), Error>> fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Vec<Result<(), Error>>
where T: Fn(&Address) -> AccountDetails; where T: Fn(&Address) -> AccountDetails;

View File

@ -225,6 +225,14 @@ impl MinerService for Miner {
} }
} }
fn author(&self) -> Address {
*self.author.read().unwrap()
}
fn extra_data(&self) -> Bytes {
self.extra_data.read().unwrap().clone()
}
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Vec<Result<(), Error>> fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Vec<Result<(), Error>>
where T: Fn(&Address) -> AccountDetails { where T: Fn(&Address) -> AccountDetails {
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();

View File

@ -192,7 +192,12 @@ impl TransactionSet {
/// Inserts `TransactionOrder` to this set /// Inserts `TransactionOrder` to this set
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> { fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
self.by_priority.insert(order.clone()); self.by_priority.insert(order.clone());
self.by_address.insert(sender, nonce, order) let r = self.by_address.insert(sender, nonce, order);
// If transaction was replaced remove it from priority queue
if let Some(ref order) = r {
self.by_priority.remove(order);
}
r
} }
/// Remove low priority transactions if there is more then specified by given `limit`. /// Remove low priority transactions if there is more then specified by given `limit`.
@ -371,7 +376,6 @@ impl TransactionQueue {
})); }));
} }
let vtx = try!(VerifiedTransaction::new(tx)); let vtx = try!(VerifiedTransaction::new(tx));
let account = fetch_account(&vtx.sender()); let account = fetch_account(&vtx.sender());
@ -571,9 +575,20 @@ impl TransactionQueue {
} }
Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash); Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash);
// Keep track of highest nonce stored in current
self.last_nonces.insert(address, nonce); self.last_nonces.insert(address, nonce);
// But maybe there are some more items waiting in future? // Update nonces of transactions in future
self.update_future(&address, state_nonce);
// Maybe there are some more items waiting in future?
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce); self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
// There might be exactly the same transaction waiting in future
// same (sender, nonce), but above function would not move it.
if let Some(order) = self.future.drop(&address, &nonce) {
// Let's insert that transaction to current (if it has higher gas_price)
let future_tx = self.by_hash.remove(&order.hash).unwrap();
Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash);
}
// Also enforce the limit
self.current.enforce_limit(&mut self.by_hash); self.current.enforce_limit(&mut self.by_hash);
trace!(target: "miner", "status: {:?}", self.status()); trace!(target: "miner", "status: {:?}", self.status());
@ -597,13 +612,11 @@ impl TransactionQueue {
let new_fee = order.gas_price; let new_fee = order.gas_price;
if old_fee.cmp(&new_fee) == Ordering::Greater { if old_fee.cmp(&new_fee) == Ordering::Greater {
// Put back old transaction since it has greater priority (higher gas_price) // Put back old transaction since it has greater priority (higher gas_price)
set.by_address.insert(address, nonce, old); set.insert(address, nonce, old);
// and remove new one // and remove new one
set.by_priority.remove(&order);
by_hash.remove(&hash); by_hash.remove(&hash);
} else { } else {
// Make sure we remove old transaction entirely // Make sure we remove old transaction entirely
set.by_priority.remove(&old);
by_hash.remove(&old.hash); by_hash.remove(&old.hash);
} }
} }
@ -643,6 +656,18 @@ mod test {
} }
} }
/// Returns two transactions with identical (sender, nonce) but different hashes
fn new_similar_txs() -> (SignedTransaction, SignedTransaction) {
let keypair = KeyPair::create().unwrap();
let secret = &keypair.secret();
let nonce = U256::from(123);
let tx = new_unsigned_tx(nonce);
let mut tx2 = new_unsigned_tx(nonce);
tx2.gas_price = U256::from(2);
(tx.sign(secret), tx2.sign(secret))
}
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) { fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
let keypair = KeyPair::create().unwrap(); let keypair = KeyPair::create().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
@ -693,6 +718,69 @@ mod test {
assert_eq!(set.by_address.len(), 0); assert_eq!(set.by_address.len(), 0);
} }
#[test]
fn should_replace_transaction_in_set() {
let mut set = TransactionSet {
by_priority: BTreeSet::new(),
by_address: Table::new(),
limit: 1
};
// Create two transactions with same nonce
// (same hash)
let (tx1, tx2) = new_txs(U256::from(0));
let tx1 = VerifiedTransaction::new(tx1).unwrap();
let tx2 = VerifiedTransaction::new(tx2).unwrap();
let by_hash = {
let mut x = HashMap::new();
let tx1 = VerifiedTransaction::new(tx1.transaction.clone()).unwrap();
let tx2 = VerifiedTransaction::new(tx2.transaction.clone()).unwrap();
x.insert(tx1.hash(), tx1);
x.insert(tx2.hash(), tx2);
x
};
// Insert both transactions
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1);
// Two different orders (imagine nonce changed in the meantime)
let order2 = TransactionOrder::for_transaction(&tx2, U256::one());
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1);
// then
assert_eq!(by_hash.len(), 1);
assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1);
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order2);
}
#[test]
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
// given
let mut txq = TransactionQueue::new();
let (tx, tx2) = new_similar_txs();
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
!U256::zero() };
// First insert one transaction to future
let res = txq.add(tx, &prev_nonce);
assert!(res.is_ok());
assert_eq!(txq.status().future, 1);
// now import second transaction to current
let res = txq.add(tx2.clone(), &default_nonce);
// and then there should be only one transaction in current (the one with higher gas_price)
assert!(res.is_ok());
assert_eq!(txq.status().pending, 1);
assert_eq!(txq.status().future, 0);
assert_eq!(txq.current.by_priority.len(), 1);
assert_eq!(txq.current.by_address.len(), 1);
assert_eq!(txq.top_transactions()[0], tx2);
}
#[test] #[test]
fn should_import_tx() { fn should_import_tx() {

View File

@ -20,6 +20,7 @@
#![cfg_attr(feature="dev", feature(plugin))] #![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))] #![cfg_attr(feature="dev", plugin(clippy))]
extern crate docopt; extern crate docopt;
extern crate num_cpus;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate ethcore; extern crate ethcore;
@ -269,7 +270,7 @@ fn setup_rpc_server(
} }
} }
} }
Some(server.start_http(url, cors_domain, 1)) Some(server.start_http(url, cors_domain, ::num_cpus::get()))
} }
#[cfg(not(feature = "rpc"))] #[cfg(not(feature = "rpc"))]

View File

@ -23,18 +23,27 @@ use ethminer::{MinerService, AccountDetails};
use jsonrpc_core::*; use jsonrpc_core::*;
use util::numbers::*; use util::numbers::*;
use util::sha3::*; use util::sha3::*;
use util::rlp::encode; use util::rlp::{encode, UntrustedRlp, View};
use util::crypto::KeyPair;
use ethcore::client::*; use ethcore::client::*;
use ethcore::block::IsBlock; use ethcore::block::IsBlock;
use ethcore::views::*; use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
use ethcore::ethereum::denominations::shannon; use ethcore::ethereum::denominations::shannon;
use ethcore::transaction::Transaction as EthTransaction; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use v1::traits::{Eth, EthFilter}; use v1::traits::{Eth, EthFilter};
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner}; use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner};
use util::keys::store::AccountProvider; use util::keys::store::AccountProvider;
fn default_gas() -> U256 {
U256::from(21_000)
}
fn default_gas_price() -> U256 {
shannon() * U256::from(50)
}
/// Eth rpc implementation. /// Eth rpc implementation.
pub struct EthClient<C, S, A, M, EM = ExternalMiner> pub struct EthClient<C, S, A, M, EM = ExternalMiner>
where C: BlockChainClient, where C: BlockChainClient,
@ -61,7 +70,6 @@ impl<C, S, A, M> EthClient<C, S, A, M, ExternalMiner>
} }
} }
impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
where C: BlockChainClient, where C: BlockChainClient,
S: SyncProvider, S: SyncProvider,
@ -127,9 +135,65 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
} }
} }
fn uncle(&self, _block: BlockId, _index: usize) -> Result<Value, Error> { fn uncle(&self, id: UncleId) -> Result<Value, Error> {
// TODO: implement! let client = take_weak!(self.client);
Ok(Value::Null) match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockId::Hash(u.hash())).map(|diff| (diff, u))) {
Some((difficulty, uncle)) => {
let block = Block {
hash: OptionalValue::Value(uncle.hash()),
parent_hash: uncle.parent_hash,
uncles_hash: uncle.uncles_hash,
author: uncle.author,
miner: uncle.author,
state_root: uncle.state_root,
transactions_root: uncle.transactions_root,
number: OptionalValue::Value(U256::from(uncle.number)),
gas_used: uncle.gas_used,
gas_limit: uncle.gas_limit,
logs_bloom: uncle.log_bloom,
timestamp: U256::from(uncle.timestamp),
difficulty: uncle.difficulty,
total_difficulty: difficulty,
receipts_root: uncle.receipts_root,
extra_data: Bytes::new(uncle.extra_data),
// todo:
nonce: H64::from(0),
uncles: vec![],
transactions: BlockTransactions::Hashes(vec![]),
};
to_value(&block)
},
None => Ok(Value::Null)
}
}
fn sign_call(client: &Arc<C>, accounts: &Arc<A>, request: CallRequest) -> Option<SignedTransaction> {
match request.from {
Some(ref from) => {
let transaction = EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.nonce(from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(default_gas),
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
};
accounts.account_secret(from).ok().map(|secret| transaction.sign(&secret))
},
None => {
let transaction = EthTransaction {
nonce: request.nonce.unwrap_or_else(U256::zero),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(default_gas),
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
};
KeyPair::create().ok().map(|kp| transaction.sign(kp.secret()))
}
}
} }
} }
@ -168,8 +232,8 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
// TODO: do not hardcode author. // TODO: do not hardcode author.
fn author(&self, params: Params) -> Result<Value, Error> { fn author(&self, params: Params) -> Result<Value, Error> {
match params { match params {
Params::None => to_value(&Address::new()), Params::None => to_value(&take_weak!(self.miner).author()),
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params()),
} }
} }
@ -191,7 +255,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn gas_price(&self, params: Params) -> Result<Value, Error> { fn gas_price(&self, params: Params) -> Result<Value, Error> {
match params { match params {
Params::None => to_value(&(shannon() * U256::from(50))), Params::None => to_value(&default_gas_price()),
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())
} }
} }
@ -304,12 +368,12 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> { fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256, Index)>(params) from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.uncle(BlockId::Hash(hash), index.value())) .and_then(|(hash, index)| self.uncle(UncleId(BlockId::Hash(hash), index.value())))
} }
fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> { fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(BlockNumber, Index)>(params) from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.uncle(number.into(), index.value())) .and_then(|(number, index)| self.uncle(UncleId(number.into(), index.value())))
} }
fn compilers(&self, params: Params) -> Result<Value, Error> { fn compilers(&self, params: Params) -> Result<Value, Error> {
@ -369,7 +433,6 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
} }
fn submit_hashrate(&self, params: Params) -> Result<Value, Error> { fn submit_hashrate(&self, params: Params) -> Result<Value, Error> {
// TODO: Index should be U256.
from_params::<(U256, H256)>(params).and_then(|(rate, id)| { from_params::<(U256, H256)>(params).and_then(|(rate, id)| {
self.external_miner.submit_hashrate(rate, id); self.external_miner.submit_hashrate(rate, id);
to_value(&true) to_value(&true)
@ -378,14 +441,22 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn send_transaction(&self, params: Params) -> Result<Value, Error> { fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params) from_params::<(TransactionRequest, )>(params)
.and_then(|(transaction_request, )| { .and_then(|(request, )| {
let accounts = take_weak!(self.accounts); let accounts = take_weak!(self.accounts);
match accounts.account_secret(&transaction_request.from) { match accounts.account_secret(&request.from) {
Ok(secret) => { Ok(secret) => {
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
let client = take_weak!(self.client); let client = take_weak!(self.client);
let transaction: EthTransaction = transaction_request.into(); let transaction = EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.nonce(&request.from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(default_gas),
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
};
let signed_transaction = transaction.sign(&secret); let signed_transaction = transaction.sign(&secret);
let hash = signed_transaction.hash(); let hash = signed_transaction.hash();
@ -406,47 +477,58 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
}) })
} }
fn call(&self, params: Params) -> Result<Value, Error> { fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, BlockNumber)>(params) from_params::<(Bytes, )>(params)
.and_then(|(transaction_request, _block_number)| { .and_then(|(raw_transaction, )| {
let accounts = take_weak!(self.accounts); let decoded: Result<SignedTransaction, _> = UntrustedRlp::new(&raw_transaction.to_vec()).as_val();
match accounts.account_secret(&transaction_request.from) { match decoded {
Ok(secret) => { Ok(signed_tx) => {
let miner = take_weak!(self.miner);
let client = take_weak!(self.client); let client = take_weak!(self.client);
let transaction: EthTransaction = transaction_request.into(); let hash = signed_tx.hash();
let signed_transaction = transaction.sign(&secret); let import = miner.import_transactions(vec![signed_tx], |a: &Address| AccountDetails {
nonce: client.nonce(a),
let output = client.call(&signed_transaction) balance: client.balance(a),
.map(|e| Bytes::new(e.output)) });
.unwrap_or(Bytes::default()); match import.into_iter().collect::<Result<Vec<_>, _>>() {
Ok(_) => to_value(&hash),
to_value(&output) Err(e) => {
warn!("Error sending transaction: {:?}", e);
to_value(&U256::zero())
}
}
}, },
Err(_) => { to_value(&Bytes::default()) } Err(_) => { to_value(&U256::zero()) }
} }
})
}
fn call(&self, params: Params) -> Result<Value, Error> {
from_params::<(CallRequest, BlockNumber)>(params)
.and_then(|(request, _block_number)| {
let client = take_weak!(self.client);
let accounts = take_weak!(self.accounts);
let signed = Self::sign_call(&client, &accounts, request);
let output = signed.map(|tx| client.call(&tx)
.map(|e| Bytes::new(e.output))
.unwrap_or(Bytes::default()));
to_value(&output)
}) })
} }
fn estimate_gas(&self, params: Params) -> Result<Value, Error> { fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, BlockNumber)>(params) from_params::<(CallRequest, BlockNumber)>(params)
.and_then(|(transaction_request, _block_number)| { .and_then(|(request, _block_number)| {
let client = take_weak!(self.client);
let accounts = take_weak!(self.accounts); let accounts = take_weak!(self.accounts);
match accounts.account_secret(&transaction_request.from) { let signed = Self::sign_call(&client, &accounts, request);
Ok(secret) => { let output = signed.map(|tx| client.call(&tx)
let client = take_weak!(self.client); .map(|e| e.gas_used + e.refunded)
.unwrap_or(U256::zero()));
let transaction: EthTransaction = transaction_request.into(); to_value(&output)
let signed_transaction = transaction.sign(&secret);
let gas_used = client.call(&signed_transaction)
.map(|e| e.gas_used + e.refunded)
.unwrap_or(U256::zero());
to_value(&gas_used)
},
Err(_) => { to_value(&U256::zero()) }
}
}) })
} }
} }

View File

@ -14,12 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use util::hash::{Address, H256}; use util::hash::{Address, H256, FixedHash};
use util::numbers::{Uint, U256}; use util::numbers::{Uint, U256};
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed}; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt;
use v1::{Eth, EthClient}; use v1::{Eth, EthClient};
use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService, TestExternalMiner}; use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService, TestExternalMiner};
@ -382,6 +385,63 @@ fn rpc_eth_sign() {
unimplemented!() unimplemented!()
} }
#[test]
fn rpc_eth_transaction_receipt() {
let receipt = LocalizedReceipt {
transaction_hash: H256::zero(),
transaction_index: 0,
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: 0x4510c,
cumulative_gas_used: U256::from(0x20),
gas_used: U256::from(0x10),
contract_address: None,
logs: vec![LocalizedLogEntry {
entry: LogEntry {
address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(),
topics: vec![
H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(),
H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap()
],
data: vec![],
},
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: 0x4510c,
transaction_hash: H256::new(),
transaction_index: 0,
log_index: 1,
}]
};
let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap();
let tester = EthTester::default();
tester.client.set_transaction_receipt(TransactionId::Hash(hash), receipt);
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","data":"0x","logIndex":"0x01","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00"}],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00"},"id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test]
fn rpc_eth_transaction_receipt_null() {
let tester = EthTester::default();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test] #[test]
fn rpc_eth_compilers() { fn rpc_eth_compilers() {
let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#; let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#;

View File

@ -0,0 +1,106 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::hash::Address;
use util::numbers::U256;
use v1::types::Bytes;
#[derive(Debug, Default, PartialEq, Deserialize)]
pub struct CallRequest {
pub from: Option<Address>,
pub to: Option<Address>,
#[serde(rename="gasPrice")]
pub gas_price: Option<U256>,
pub gas: Option<U256>,
pub value: Option<U256>,
pub data: Option<Bytes>,
pub nonce: Option<U256>,
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use serde_json;
use util::numbers::{Uint, U256};
use util::hash::Address;
use ethcore::transaction::{Transaction, Action};
use v1::types::Bytes;
use super::*;
#[test]
fn transaction_request_deserialize() {
let s = r#"{
"from":"0x0000000000000000000000000000000000000001",
"to":"0x0000000000000000000000000000000000000002",
"gasPrice":"0x1",
"gas":"0x2",
"value":"0x3",
"data":"0x123456",
"nonce":"0x4"
}"#;
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, CallRequest {
from: Some(Address::from(1)),
to: Some(Address::from(2)),
gas_price: Some(U256::from(1)),
gas: Some(U256::from(2)),
value: Some(U256::from(3)),
data: Some(Bytes::new(vec![0x12, 0x34, 0x56])),
nonce: Some(U256::from(4)),
});
}
#[test]
fn transaction_request_deserialize2() {
let s = r#"{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}"#;
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, CallRequest {
from: Some(Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from_str("9184e72a000").unwrap()),
gas: Some(U256::from_str("76c0").unwrap()),
value: Some(U256::from_str("9184e72a").unwrap()),
data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())),
nonce: None
});
}
#[test]
fn transaction_request_deserialize_empty() {
let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#;
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, CallRequest {
from: Some(Address::from(1)),
to: None,
gas_price: None,
gas: None,
value: None,
data: None,
nonce: None,
});
}
}

View File

@ -20,19 +20,19 @@ use v1::types::Bytes;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Log { pub struct Log {
address: Address, pub address: Address,
topics: Vec<H256>, pub topics: Vec<H256>,
data: Bytes, pub data: Bytes,
#[serde(rename="blockHash")] #[serde(rename="blockHash")]
block_hash: H256, pub block_hash: H256,
#[serde(rename="blockNumber")] #[serde(rename="blockNumber")]
block_number: U256, pub block_number: U256,
#[serde(rename="transactionHash")] #[serde(rename="transactionHash")]
transaction_hash: H256, pub transaction_hash: H256,
#[serde(rename="transactionIndex")] #[serde(rename="transactionIndex")]
transaction_index: U256, pub transaction_index: U256,
#[serde(rename="logIndex")] #[serde(rename="logIndex")]
log_index: U256, pub log_index: U256,
} }
impl From<LocalizedLogEntry> for Log { impl From<LocalizedLogEntry> for Log {

View File

@ -24,6 +24,7 @@ mod optionals;
mod sync; mod sync;
mod transaction; mod transaction;
mod transaction_request; mod transaction_request;
mod call_request;
mod receipt; mod receipt;
pub use self::block::{Block, BlockTransactions}; pub use self::block::{Block, BlockTransactions};
@ -36,5 +37,6 @@ pub use self::optionals::OptionalValue;
pub use self::sync::{SyncStatus, SyncInfo}; pub use self::sync::{SyncStatus, SyncInfo};
pub use self::transaction::Transaction; pub use self::transaction::Transaction;
pub use self::transaction_request::TransactionRequest; pub use self::transaction_request::TransactionRequest;
pub use self::call_request::CallRequest;
pub use self::receipt::Receipt; pub use self::receipt::Receipt;

View File

@ -53,4 +53,42 @@ impl From<LocalizedReceipt> for Receipt {
} }
} }
#[cfg(test)]
mod tests {
use serde_json;
use std::str::FromStr;
use util::numbers::*;
use v1::types::{Bytes, Log, Receipt};
#[test]
fn receipt_serialization() {
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01"}]}"#;
let receipt = Receipt {
transaction_hash: H256::zero(),
transaction_index: U256::zero(),
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: U256::from(0x4510c),
cumulative_gas_used: U256::from(0x20),
gas_used: U256::from(0x10),
contract_address: None,
logs: vec![Log {
address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(),
topics: vec![
H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(),
H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap()
],
data: Bytes::new(vec![]),
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: U256::from(0x4510c),
transaction_hash: H256::new(),
transaction_index: U256::zero(),
log_index: U256::one()
}]
};
let serialized = serde_json::to_string(&receipt).unwrap();
assert_eq!(serialized, s);
}
}

View File

@ -15,8 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::hash::Address; use util::hash::Address;
use util::numbers::{Uint, U256}; use util::numbers::U256;
use ethcore::transaction::{Action, Transaction};
use v1::types::Bytes; use v1::types::Bytes;
#[derive(Debug, Default, PartialEq, Deserialize)] #[derive(Debug, Default, PartialEq, Deserialize)]
@ -31,19 +30,6 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
} }
impl Into<Transaction> for TransactionRequest {
fn into(self) -> Transaction {
Transaction {
nonce: self.nonce.unwrap_or_else(U256::zero),
action: self.to.map_or(Action::Create, Action::Call),
gas: self.gas.unwrap_or_else(U256::zero),
gas_price: self.gas_price.unwrap_or_else(U256::zero),
value: self.value.unwrap_or_else(U256::zero),
data: self.data.map_or_else(Vec::new, |d| d.to_vec()),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
@ -55,50 +41,6 @@ mod tests {
use v1::types::Bytes; use v1::types::Bytes;
use super::*; use super::*;
#[test]
fn transaction_request_into_transaction() {
let tr = TransactionRequest {
from: Address::default(),
to: Some(Address::from(10)),
gas_price: Some(U256::from(20)),
gas: Some(U256::from(10_000)),
value: Some(U256::from(1)),
data: Some(Bytes::new(vec![10, 20])),
nonce: Some(U256::from(12)),
};
assert_eq!(Transaction {
nonce: U256::from(12),
action: Action::Call(Address::from(10)),
gas: U256::from(10_000),
gas_price: U256::from(20),
value: U256::from(1),
data: vec![10, 20],
}, tr.into());
}
#[test]
fn empty_transaction_request_into_transaction() {
let tr = TransactionRequest {
from: Address::default(),
to: None,
gas_price: None,
gas: None,
value: None,
data: None,
nonce: None,
};
assert_eq!(Transaction {
nonce: U256::zero(),
action: Action::Create,
gas: U256::zero(),
gas_price: U256::zero(),
value: U256::zero(),
data: vec![],
}, tr.into());
}
#[test] #[test]
fn transaction_request_deserialize() { fn transaction_request_deserialize() {
let s = r#"{ let s = r#"{

View File

@ -20,6 +20,7 @@ use numbers::*;
use bytes::*; use bytes::*;
use secp256k1::{key, Secp256k1}; use secp256k1::{key, Secp256k1};
use rand::os::OsRng; use rand::os::OsRng;
use sha3::Hashable;
/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data. /// Secret key for secp256k1 EC operations. 256 bit generic "hash" data.
pub type Secret = H256; pub type Secret = H256;
@ -135,15 +136,22 @@ impl KeyPair {
public: p, public: p,
}) })
} }
/// Returns public key /// Returns public key
pub fn public(&self) -> &Public { pub fn public(&self) -> &Public {
&self.public &self.public
} }
/// Returns private key /// Returns private key
pub fn secret(&self) -> &Secret { pub fn secret(&self) -> &Secret {
&self.secret &self.secret
} }
/// Returns address.
pub fn address(&self) -> Address {
Address::from(self.public.sha3())
}
/// Sign a message with our secret key. /// Sign a message with our secret key.
pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) } pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) }
} }