Merge branch 'master' into bettermining
This commit is contained in:
commit
97449afbb9
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -15,6 +15,7 @@ dependencies = [
|
||||
"ethsync 1.1.0",
|
||||
"fdlimit 0.1.0",
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -18,6 +18,7 @@ time = "0.1"
|
||||
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
|
||||
fdlimit = { path = "util/fdlimit" }
|
||||
daemonize = "0.2"
|
||||
num_cpus = "0.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "0.1"
|
||||
clippy = { version = "0.0.54", optional = true }
|
||||
|
35
ethcore/res/null_homestead_morden.json
Normal file
35
ethcore/res/null_homestead_morden.json
Normal 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" }
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
"engineName": "NullEngine",
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0xfffa2990",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"tieBreakingGas": false,
|
||||
"minGasLimit": "0x1388",
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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!();
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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(¶ms), 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(¶ms), 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(¶ms), 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:
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -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();
|
||||
|
@ -65,7 +65,7 @@ pub use transaction_queue::{TransactionQueue, AccountDetails};
|
||||
pub use miner::{Miner};
|
||||
|
||||
use std::sync::Mutex;
|
||||
use util::{H256, Address, Bytes};
|
||||
use util::{H256, Address, FixedHash, Bytes};
|
||||
use ethcore::client::{BlockChainClient};
|
||||
use ethcore::block::{ClosedBlock};
|
||||
use ethcore::error::{Error};
|
||||
@ -77,6 +77,12 @@ pub trait MinerService : Send + Sync {
|
||||
/// Returns miner's status.
|
||||
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.
|
||||
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Vec<Result<(), Error>>
|
||||
where T: Fn(&Address) -> AccountDetails;
|
||||
|
@ -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>>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||
|
@ -192,7 +192,12 @@ impl TransactionSet {
|
||||
/// Inserts `TransactionOrder` to this set
|
||||
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
|
||||
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`.
|
||||
@ -371,7 +376,6 @@ impl TransactionQueue {
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
let vtx = try!(VerifiedTransaction::new(tx));
|
||||
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);
|
||||
// Keep track of highest nonce stored in current
|
||||
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);
|
||||
// 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);
|
||||
|
||||
trace!(target: "miner", "status: {:?}", self.status());
|
||||
@ -597,13 +612,11 @@ impl TransactionQueue {
|
||||
let new_fee = order.gas_price;
|
||||
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
||||
// 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
|
||||
set.by_priority.remove(&order);
|
||||
by_hash.remove(&hash);
|
||||
} else {
|
||||
// Make sure we remove old transaction entirely
|
||||
set.by_priority.remove(&old);
|
||||
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) {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
@ -693,6 +718,69 @@ mod test {
|
||||
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]
|
||||
fn should_import_tx() {
|
||||
|
@ -20,6 +20,7 @@
|
||||
#![cfg_attr(feature="dev", feature(plugin))]
|
||||
#![cfg_attr(feature="dev", plugin(clippy))]
|
||||
extern crate docopt;
|
||||
extern crate num_cpus;
|
||||
extern crate rustc_serialize;
|
||||
extern crate ethcore_util as util;
|
||||
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"))]
|
||||
|
@ -23,18 +23,27 @@ use ethminer::{MinerService, AccountDetails};
|
||||
use jsonrpc_core::*;
|
||||
use util::numbers::*;
|
||||
use util::sha3::*;
|
||||
use util::rlp::encode;
|
||||
use util::rlp::{encode, UntrustedRlp, View};
|
||||
use util::crypto::KeyPair;
|
||||
use ethcore::client::*;
|
||||
use ethcore::block::IsBlock;
|
||||
use ethcore::views::*;
|
||||
use ethcore::ethereum::Ethash;
|
||||
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::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 util::keys::store::AccountProvider;
|
||||
|
||||
fn default_gas() -> U256 {
|
||||
U256::from(21_000)
|
||||
}
|
||||
|
||||
fn default_gas_price() -> U256 {
|
||||
shannon() * U256::from(50)
|
||||
}
|
||||
|
||||
/// Eth rpc implementation.
|
||||
pub struct EthClient<C, S, A, M, EM = ExternalMiner>
|
||||
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>
|
||||
where C: BlockChainClient,
|
||||
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> {
|
||||
// TODO: implement!
|
||||
Ok(Value::Null)
|
||||
fn uncle(&self, id: UncleId) -> Result<Value, Error> {
|
||||
let client = take_weak!(self.client);
|
||||
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.
|
||||
fn author(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => to_value(&Address::new()),
|
||||
_ => Err(Error::invalid_params())
|
||||
Params::None => to_value(&take_weak!(self.miner).author()),
|
||||
_ => 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> {
|
||||
match params {
|
||||
Params::None => to_value(&(shannon() * U256::from(50))),
|
||||
Params::None => to_value(&default_gas_price()),
|
||||
_ => 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> {
|
||||
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> {
|
||||
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> {
|
||||
@ -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> {
|
||||
// TODO: Index should be U256.
|
||||
from_params::<(U256, H256)>(params).and_then(|(rate, id)| {
|
||||
self.external_miner.submit_hashrate(rate, id);
|
||||
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> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(transaction_request, )| {
|
||||
.and_then(|(request, )| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&transaction_request.from) {
|
||||
match accounts.account_secret(&request.from) {
|
||||
Ok(secret) => {
|
||||
let miner = take_weak!(self.miner);
|
||||
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 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> {
|
||||
from_params::<(TransactionRequest, BlockNumber)>(params)
|
||||
.and_then(|(transaction_request, _block_number)| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&transaction_request.from) {
|
||||
Ok(secret) => {
|
||||
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Bytes, )>(params)
|
||||
.and_then(|(raw_transaction, )| {
|
||||
let decoded: Result<SignedTransaction, _> = UntrustedRlp::new(&raw_transaction.to_vec()).as_val();
|
||||
match decoded {
|
||||
Ok(signed_tx) => {
|
||||
let miner = take_weak!(self.miner);
|
||||
let client = take_weak!(self.client);
|
||||
|
||||
let transaction: EthTransaction = transaction_request.into();
|
||||
let signed_transaction = transaction.sign(&secret);
|
||||
|
||||
let output = client.call(&signed_transaction)
|
||||
.map(|e| Bytes::new(e.output))
|
||||
.unwrap_or(Bytes::default());
|
||||
|
||||
to_value(&output)
|
||||
let hash = signed_tx.hash();
|
||||
let import = miner.import_transactions(vec![signed_tx], |a: &Address| AccountDetails {
|
||||
nonce: client.nonce(a),
|
||||
balance: client.balance(a),
|
||||
});
|
||||
match import.into_iter().collect::<Result<Vec<_>, _>>() {
|
||||
Ok(_) => to_value(&hash),
|
||||
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> {
|
||||
from_params::<(TransactionRequest, BlockNumber)>(params)
|
||||
.and_then(|(transaction_request, _block_number)| {
|
||||
from_params::<(CallRequest, BlockNumber)>(params)
|
||||
.and_then(|(request, _block_number)| {
|
||||
let client = take_weak!(self.client);
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&transaction_request.from) {
|
||||
Ok(secret) => {
|
||||
let client = take_weak!(self.client);
|
||||
let signed = Self::sign_call(&client, &accounts, request);
|
||||
let output = signed.map(|tx| client.call(&tx)
|
||||
.map(|e| e.gas_used + e.refunded)
|
||||
.unwrap_or(U256::zero()));
|
||||
|
||||
let transaction: EthTransaction = transaction_request.into();
|
||||
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()) }
|
||||
}
|
||||
to_value(&output)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::hash::{Address, H256};
|
||||
use util::hash::{Address, H256, FixedHash};
|
||||
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::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService, TestExternalMiner};
|
||||
|
||||
@ -382,6 +385,63 @@ fn rpc_eth_sign() {
|
||||
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]
|
||||
fn rpc_eth_compilers() {
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#;
|
||||
|
106
rpc/src/v1/types/call_request.rs
Normal file
106
rpc/src/v1/types/call_request.rs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
@ -20,19 +20,19 @@ use v1::types::Bytes;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Log {
|
||||
address: Address,
|
||||
topics: Vec<H256>,
|
||||
data: Bytes,
|
||||
pub address: Address,
|
||||
pub topics: Vec<H256>,
|
||||
pub data: Bytes,
|
||||
#[serde(rename="blockHash")]
|
||||
block_hash: H256,
|
||||
pub block_hash: H256,
|
||||
#[serde(rename="blockNumber")]
|
||||
block_number: U256,
|
||||
pub block_number: U256,
|
||||
#[serde(rename="transactionHash")]
|
||||
transaction_hash: H256,
|
||||
pub transaction_hash: H256,
|
||||
#[serde(rename="transactionIndex")]
|
||||
transaction_index: U256,
|
||||
pub transaction_index: U256,
|
||||
#[serde(rename="logIndex")]
|
||||
log_index: U256,
|
||||
pub log_index: U256,
|
||||
}
|
||||
|
||||
impl From<LocalizedLogEntry> for Log {
|
||||
|
@ -24,6 +24,7 @@ mod optionals;
|
||||
mod sync;
|
||||
mod transaction;
|
||||
mod transaction_request;
|
||||
mod call_request;
|
||||
mod receipt;
|
||||
|
||||
pub use self::block::{Block, BlockTransactions};
|
||||
@ -36,5 +37,6 @@ pub use self::optionals::OptionalValue;
|
||||
pub use self::sync::{SyncStatus, SyncInfo};
|
||||
pub use self::transaction::Transaction;
|
||||
pub use self::transaction_request::TransactionRequest;
|
||||
pub use self::call_request::CallRequest;
|
||||
pub use self::receipt::Receipt;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::hash::Address;
|
||||
use util::numbers::{Uint, U256};
|
||||
use ethcore::transaction::{Action, Transaction};
|
||||
use util::numbers::U256;
|
||||
use v1::types::Bytes;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Deserialize)]
|
||||
@ -31,19 +30,6 @@ pub struct TransactionRequest {
|
||||
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)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
@ -55,50 +41,6 @@ mod tests {
|
||||
use v1::types::Bytes;
|
||||
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]
|
||||
fn transaction_request_deserialize() {
|
||||
let s = r#"{
|
||||
|
@ -20,6 +20,7 @@ use numbers::*;
|
||||
use bytes::*;
|
||||
use secp256k1::{key, Secp256k1};
|
||||
use rand::os::OsRng;
|
||||
use sha3::Hashable;
|
||||
|
||||
/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data.
|
||||
pub type Secret = H256;
|
||||
@ -135,15 +136,22 @@ impl KeyPair {
|
||||
public: p,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns public key
|
||||
pub fn public(&self) -> &Public {
|
||||
&self.public
|
||||
}
|
||||
|
||||
/// Returns private key
|
||||
pub fn secret(&self) -> &Secret {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
/// Returns address.
|
||||
pub fn address(&self) -> Address {
|
||||
Address::from(self.public.sha3())
|
||||
}
|
||||
|
||||
/// Sign a message with our secret key.
|
||||
pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user