Support 'pending' block in RPC (#1007)

* Support `pending` block in RPC

* Forward calls from miner to client in case no pending block is available
This commit is contained in:
Arkadiy Paronyan 2016-04-28 21:47:44 +02:00 committed by Gav Wood
parent ea669ac6b6
commit 8f7624f5cb
9 changed files with 175 additions and 34 deletions

View File

@ -702,6 +702,10 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
}) })
.collect() .collect()
} }
fn last_hashes(&self) -> LastHashes {
self.build_last_hashes(self.chain.best_block_hash())
}
} }
impl MayPanic for Client { impl MayPanic for Client {

View File

@ -25,7 +25,8 @@ pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig}; pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
pub use self::ids::{BlockId, TransactionId, UncleId}; 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, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo};
use std::collections::HashSet; use std::collections::HashSet;
use util::bytes::Bytes; use util::bytes::Bytes;
@ -132,5 +133,8 @@ pub trait BlockChainClient : Sync + Send {
/// Executes a function providing it with a reference to an engine. /// Executes a function providing it with a reference to an engine.
fn engine(&self) -> &Engine; fn engine(&self) -> &Engine;
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;
} }

View File

@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
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, UncleId}; use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, LastHashes};
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;
@ -268,6 +268,10 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn last_hashes(&self) -> LastHashes {
unimplemented!();
}
fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> (Option<ClosedBlock>, HashSet<H256>) { fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> (Option<ClosedBlock>, HashSet<H256>) {
(None, HashSet::new()) (None, HashSet::new())
} }

View File

@ -117,7 +117,7 @@ impl<'a> Executive<'a> {
Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output, tracer) Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output, tracer)
} }
/// This funtion should be used to execute transaction. /// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, Error> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, Error> {
let check = options.check_nonce; let check = options.check_nonce;
match options.tracing { match options.tracing {
@ -126,6 +126,7 @@ impl<'a> Executive<'a> {
} }
} }
/// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T>(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result<Executed, Error> where T: Tracer { pub fn transact_with_tracer<T>(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result<Executed, Error> where T: Tracer {
let sender = try!(t.sender()); let sender = try!(t.sender());
let nonce = self.state.nonce(&sender); let nonce = self.state.nonce(&sender);

View File

@ -61,7 +61,7 @@ pub use miner::{Miner};
pub use external::{ExternalMiner, ExternalMinerService}; pub use external::{ExternalMiner, ExternalMinerService};
use util::{H256, U256, Address, Bytes}; use util::{H256, U256, Address, Bytes};
use ethcore::client::{BlockChainClient}; use ethcore::client::{BlockChainClient, Executed};
use ethcore::block::{ClosedBlock}; use ethcore::block::{ClosedBlock};
use ethcore::error::{Error}; use ethcore::error::{Error};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
@ -145,6 +145,21 @@ pub trait MinerService : Send + Sync {
/// Suggested gas limit. /// Suggested gas limit.
fn sensible_gas_limit(&self) -> U256 { x!(21000) } fn sensible_gas_limit(&self) -> U256 { x!(21000) }
/// Account balance
fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256;
/// Call into contract code using pending state.
fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result<Executed, Error>;
/// Get storage value in pending state.
fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256;
/// Get account nonce in pending state.
fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256;
/// Get contract code in pending state.
fn code(&self, chain: &BlockChainClient, address: &Address) -> Option<Bytes>;
} }
/// Mining status /// Mining status

View File

@ -22,6 +22,7 @@ use ethcore::views::{BlockView, HeaderView};
use ethcore::client::{BlockChainClient, BlockId}; use ethcore::client::{BlockChainClient, BlockId};
use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::error::*; use ethcore::error::*;
use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult}; use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult};
@ -195,6 +196,58 @@ impl MinerService for Miner {
} }
} }
fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result<Executed, Error> {
let sealing_work = self.sealing_work.lock().unwrap();
match sealing_work.peek_last_ref() {
Some(work) => {
let block = work.block();
let header = block.header();
let last_hashes = chain.last_hashes();
let env_info = EnvInfo {
number: header.number(),
author: header.author().clone(),
timestamp: header.timestamp(),
difficulty: header.difficulty().clone(),
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: U256::max_value(),
};
// that's just a copy of the state.
let mut state = block.state().clone();
let sender = try!(t.sender());
let balance = state.balance(&sender);
// give the sender max balance
state.sub_balance(&sender, &balance);
state.add_balance(&sender, &U256::max_value());
let options = TransactOptions { tracing: false, check_nonce: false };
Executive::new(&mut state, &env_info, chain.engine()).transact(t, options)
},
None => {
chain.call(t)
}
}
}
fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.balance(address), |b| b.block().fields().state.balance(address))
}
fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256 {
let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.storage_at(address, position), |b| b.block().fields().state.storage_at(address, position))
}
fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.nonce(address), |b| b.block().fields().state.nonce(address))
}
fn code(&self, chain: &BlockChainClient, address: &Address) -> Option<Bytes> {
let sealing_work = self.sealing_work.lock().unwrap();
sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
}
fn set_author(&self, author: Address) { fn set_author(&self, author: Address) {
*self.author.write().unwrap() = author; *self.author.write().unwrap() = author;
} }

View File

@ -200,17 +200,17 @@ fn params_len(params: &Params) -> usize {
} }
} }
fn from_params_discard_second<F>(params: Params) -> Result<(F,), Error> where F: serde::de::Deserialize { fn from_params_default_second<F>(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize {
match params_len(&params) { match params_len(&params) {
1 => from_params::<(F, )>(params), 1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)),
_ => from_params::<(F, BlockNumber)>(params).map(|(f, _block_number)| (f, )), _ => from_params::<(F, BlockNumber)>(params),
} }
} }
fn from_params_discard_third<F1, F2>(params: Params) -> Result<(F1, F2,), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize { fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize {
match params_len(&params) { match params_len(&params) {
2 => from_params::<(F1, F2, )>(params), 2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)),
_ => from_params::<(F1, F2, BlockNumber)>(params).map(|(f1, f2, _block_number)| (f1, f2, )), _ => from_params::<(F1, F2, BlockNumber)>(params)
} }
} }
@ -293,18 +293,30 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
} }
fn balance(&self, params: Params) -> Result<Value, Error> { fn balance(&self, params: Params) -> Result<Value, Error> {
from_params_discard_second(params).and_then(|(address, )| from_params_default_second(params)
to_value(&take_weak!(self.client).balance(&address))) .and_then(|(address, block_number,)| match block_number {
BlockNumber::Latest => to_value(&take_weak!(self.client).balance(&address)),
BlockNumber::Pending => to_value(&take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address)),
_ => Err(Error::invalid_params()),
})
} }
fn storage_at(&self, params: Params) -> Result<Value, Error> { fn storage_at(&self, params: Params) -> Result<Value, Error> {
from_params_discard_third::<Address, U256>(params).and_then(|(address, position, )| from_params_default_third::<Address, U256>(params)
to_value(&U256::from(take_weak!(self.client).storage_at(&address, &H256::from(position))))) .and_then(|(address, position, block_number,)| match block_number {
BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).storage_at(take_weak!(self.client).deref(), &address, &H256::from(position)))),
BlockNumber::Latest => to_value(&U256::from(take_weak!(self.client).storage_at(&address, &H256::from(position)))),
_ => Err(Error::invalid_params()),
})
} }
fn transaction_count(&self, params: Params) -> Result<Value, Error> { fn transaction_count(&self, params: Params) -> Result<Value, Error> {
from_params_discard_second(params).and_then(|(address, )| from_params_default_second(params)
to_value(&take_weak!(self.client).nonce(&address))) .and_then(|(address, block_number,)| match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address)),
BlockNumber::Latest => to_value(&take_weak!(self.client).nonce(&address)),
_ => Err(Error::invalid_params()),
})
} }
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> { fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
@ -341,10 +353,13 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
}) })
} }
// TODO: do not ignore block number param
fn code_at(&self, params: Params) -> Result<Value, Error> { fn code_at(&self, params: Params) -> Result<Value, Error> {
from_params_discard_second(params).and_then(|(address, )| from_params_default_second(params)
to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new))) .and_then(|(address, block_number,)| match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)),
BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)),
_ => Err(Error::invalid_params()),
})
} }
fn block_by_hash(&self, params: Params) -> Result<Value, Error> { fn block_by_hash(&self, params: Params) -> Result<Value, Error> {
@ -502,21 +517,29 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn call(&self, params: Params) -> Result<Value, Error> { fn call(&self, params: Params) -> Result<Value, Error> {
trace!(target: "jsonrpc", "call: {:?}", params); trace!(target: "jsonrpc", "call: {:?}", params);
from_params_discard_second(params).and_then(|(request, )| { from_params_default_second(params)
let signed = try!(self.sign_call(request)); .and_then(|(request, block_number,)| {
let client = take_weak!(self.client); let signed = try!(self.sign_call(request));
let output = client.call(&signed).map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])); let r = match block_number {
to_value(&output) BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
}) BlockNumber::Latest => take_weak!(self.client).call(&signed),
_ => panic!("{:?}", block_number),
};
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
})
} }
fn estimate_gas(&self, params: Params) -> Result<Value, Error> { fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
from_params_discard_second(params).and_then(|(request, )| { from_params_default_second(params)
let signed = try!(self.sign_call(request)); .and_then(|(request, block_number,)| {
let client = take_weak!(self.client); let signed = try!(self.sign_call(request));
let used = client.call(&signed).map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)); let r = match block_number {
to_value(&used) BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed),
}) BlockNumber::Latest => take_weak!(self.client).call(&signed),
_ => return Err(Error::invalid_params()),
};
to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))
})
} }
} }

View File

@ -192,6 +192,22 @@ fn rpc_eth_balance() {
assert_eq!(tester.io.handle_request(request), Some(response.to_owned())); assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
} }
#[ignore] //TODO: propert test
#[test]
fn rpc_eth_balance_pending() {
let tester = EthTester::default();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0x0000000000000000000000000000000000000001", "latest"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x","id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test] #[test]
fn rpc_eth_storage_at() { fn rpc_eth_storage_at() {
let tester = EthTester::default(); let tester = EthTester::default();

View File

@ -16,11 +16,11 @@
//! Test implementation of miner service. //! Test implementation of miner service.
use util::{Address, H256, Bytes, U256, FixedHash}; use util::{Address, H256, Bytes, U256, FixedHash, Uint};
use util::standard::*; use util::standard::*;
use ethcore::error::Error; use ethcore::error::Error;
use ethcore::client::BlockChainClient; use ethcore::client::{BlockChainClient, Executed};
use ethcore::block::ClosedBlock; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult}; use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
@ -174,4 +174,25 @@ impl MinerService for TestMinerService {
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
unimplemented!(); unimplemented!();
} }
fn balance(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
}
fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction) -> Result<Executed, Error> {
unimplemented!();
}
fn storage_at(&self, _chain: &BlockChainClient, address: &Address, position: &H256) -> H256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(H256::default, |b| b.block().fields().state.storage_at(address, position).clone())
}
fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.nonce(address).clone())
}
fn code(&self, _chain: &BlockChainClient, address: &Address) -> Option<Bytes> {
self.latest_closed_block.lock().unwrap().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone())
}
} }