Client refactoring (#7038)

* Improves `BestBlock` comment

* Improves `TraceDB` comment

* Improves `journaldb::Algorithm` comment.

Probably the whole enum should be renamed to `Strategy` or something alike.

* Comments some of the `Client`'s fields

* Deglobs client imports

* Fixes comments

* Extracts `import_lock` to `Importer` struct

* Extracts `verifier` to `Importer` struct

* Extracts `block_queue` to `Importer` struct

* Extracts `miner` to `Importer` struct

* Extracts `ancient_verifier` to `Importer` struct

* Extracts `rng` to `Importer` struct

* Extracts `import_old_block` to `Importer` struct

* Adds `Nonce` trait

* Adds `Balance` trait

* Adds `ChainInfo` trait

* Fixes imports for tests using `chain_info` method

* Adds `BlockInfo` trait

* Adds more `ChainInfo` imports

* Adds `BlockInfo` imports

* Adds `ReopenBlock` trait

* Adds `PrepareOpenBlock` trait

* Fixes import in tests

* Adds `CallContract` trait

* Fixes imports in tests using `call_contract` method

* Adds `TransactionInfo` trait

* Adds `RegistryInfo` trait

* Fixes imports in tests using `registry_address` method

* Adds `ScheduleInfo` trait

* Adds `ImportSealedBlock` trait

* Fixes imports in test using `import_sealed_block` method

* Adds `BroadcastProposalBlock` trait

* Migrates `Miner` to static dispatch

* Fixes tests

* Moves `calculate_enacted_retracted` to `Importer`

* Moves import-related methods to `Importer`

* Removes redundant `import_old_block` wrapper

* Extracts `import_block*` into separate trait

* Fixes tests

* Handles `Pending` in `LightFetch`

* Handles `Pending` in filters

* Handles `Pending` in `ParityClient`

* Handles `Pending` in `EthClient`

* Removes `BlockId::Pending`, partly refactors dependent code

* Adds `StateInfo` trait

* Exports `StateOrBlock` and `BlockChain` types from `client` module

* Refactors `balance` RPC using generic API

* Refactors `storage_at` RPC using generic API

* Makes `MinerService::pending_state`'s return type dynamic

* Adds `StateOrBlock` and `BlockChain` types

* Adds impl of `client::BlockChain` for `Client`

* Exports `StateInfo` trait from `client` module

* Missing `self` use

To be fixed up to "Adds impl of `client::BlockChain` for `Client`"

* Adds `number_to_id` and refactors dependent RPC methods

* Refactors `code_at` using generic API

* Adds `StateClient` trait

* Refactors RPC to use `StateClient` trait

* Reverts `client::BlockChain` trait stuff, refactors methods to accept `StateOrBlock`

* Refactors TestClient

* Adds helper function `block_number_to_id`

* Uses `block_number_to_id` instead of local function

* Handles `Pending` in `list_accounts` and `list_storage_keys`

* Attempt to use associated types for state instead of trait objects

* Simplifies `state_at_beginning`

* Extracts `call` and `call_many` into separate trait

* Refactors `build_last_hashes` to accept reference

* Exports `Call` type from the module

* Refactors `call` and `call_many` to accept state and header

* Exports `state_at` in `StateClient`

* Exports `pending_block_header` from `MinerService`

* Refactors RPC `call` method using new API

* Adds missing parentheses

* Refactors `parity::call` to use new call API

* Update .gitlab-ci.yml

fix gitlab lint

* Fixes error handling

* Refactors `traces::call` and `call_many` to use new call API

* Refactors `call_contract`

* Refactors `block_header`

* Refactors internal RPC method `block`

* Moves `estimate_gas` to `Call` trait, refactors parameters

* Refactors `estimate_gas` in RPC

* Refactors `uncle`

* Refactors RPC `transaction`

* Covers missing branches

* Makes it all compile, fixes compiler grumbles

* Adds casts in `blockchain` module

* Fixes `PendingBlock` tests, work on `MinerService`

* Adds test stubs for StateClient and EngineInfo

* Makes `state_db` public

* Adds missing impls for `TestBlockChainClient`

* Adds trait documentation

* Adds missing docs to the `state_db` module

* Fixes trivial compilation errors

* Moves `code_hash` method to a `BlockInfo` trait

* Refactors `Verifier` to be generic over client

* Refactors `TransactionFilter` to be generic over client

* Refactors `Miner` and `Client` to reflect changes in verifier and txfilter API

* Moves `ServiceTransactionChecker` back to `ethcore`

* Fixes trait bounds in `Miner` API

* Fixes `Client`

* Fixes lifetime bound in `FullFamilyParams`

* Adds comments to `FullFamilyParams`

* Fixes imports in `ethcore`

* Fixes BlockNumber handling in `code_at` and `replay_block_transactions`

* fix compile issues

* First step to redundant trait merge

* Fixes compilation error in RPC tests

* Adds mock `State` as a stub for `TestClient`

* Handles `StateOrBlock::State` in `TestBlockChainClient::balance`

* Fixes `transaction_count` RPC

* Fixes `transaction_count`

* Moves `service_transaction.json` to the `contracts` subfolder

* Fixes compilation errors in tests

* Refactors client to use `AccountData`

* Refactors client to use `BlockChain`

* Refactors miner to use aggregate traits

* Adds `SealedBlockImporter` trait

* Refactors miner to use `SealedBlockImporter` trait

* Removes unused imports

* Simplifies `RegistryInfo::registry_address`

* Fixes indentation

* Removes commented out trait bound
This commit is contained in:
Dmitry Kashitsyn
2018-03-03 18:42:13 +01:00
committed by Marek Kotewicz
parent 81b52c7336
commit 9d7d6f7108
65 changed files with 2067 additions and 1238 deletions

View File

@@ -68,6 +68,7 @@ extern crate rlp;
extern crate stats;
extern crate keccak_hash as hash;
extern crate hardware_wallet;
extern crate patricia_trie as trie;
#[macro_use]
extern crate log;

View File

@@ -197,7 +197,19 @@ impl LightFetch {
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
let req: CallRequestHelper = req.into();
let id = num.unwrap_or_default().into();
// Note: Here we treat `Pending` as `Latest`.
// Since light clients don't produce pending blocks
// (they don't have state) we can safely fallback to `Latest`.
let id = match num.unwrap_or_default() {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => {
warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`");
BlockId::Latest
}
};
let from = req.from.unwrap_or(Address::zero());
let nonce_fut = match req.nonce {
@@ -308,7 +320,7 @@ impl LightFetch {
let best_number = self.client.chain_info().best_block_number;
let block_number = |id| match id {
BlockId::Earliest => Some(0),
BlockId::Latest | BlockId::Pending => Some(best_number),
BlockId::Latest => Some(best_number),
BlockId::Hash(h) => self.client.block_header(BlockId::Hash(h)).map(|hdr| hdr.number()),
BlockId::Number(x) => Some(x),
};

View File

@@ -28,16 +28,17 @@ use parking_lot::Mutex;
use ethash::SeedHashCompute;
use ethcore::account_provider::{AccountProvider, DappId};
use ethcore::block::IsBlock;
use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId};
use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
use ethcore::ethereum::Ethash;
use ethcore::filter::Filter as EthcoreFilter;
use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber};
use ethcore::header::{BlockNumber as EthBlockNumber, Seal};
use ethcore::log_entry::LogEntry;
use ethcore::miner::MinerService;
use ethcore::snapshot::SnapshotService;
use ethcore::encoded;
use ethsync::{SyncProvider};
use miner::external::ExternalMinerService;
use transaction::SignedTransaction;
use transaction::{SignedTransaction, LocalizedTransaction};
use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_core::futures::future;
@@ -51,11 +52,11 @@ use v1::traits::Eth;
use v1::types::{
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
Transaction, CallRequest, Index, Filter, Log, Receipt, Work,
H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256,
H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, block_number_to_id,
};
use v1::metadata::Metadata;
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
const EXTRA_INFO_PROOF: &'static str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed";
/// Eth RPC options
pub struct EthClientOptions {
@@ -109,11 +110,43 @@ pub struct EthClient<C, SN: ?Sized, S: ?Sized, M, EM> where
eip86_transition: u64,
}
impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
C: MiningBlockChainClient,
enum BlockNumberOrId {
Number(BlockNumber),
Id(BlockId),
}
impl From<BlockId> for BlockNumberOrId {
fn from(value: BlockId) -> BlockNumberOrId {
BlockNumberOrId::Id(value)
}
}
impl From<BlockNumber> for BlockNumberOrId {
fn from(value: BlockNumber) -> BlockNumberOrId {
BlockNumberOrId::Number(value)
}
}
enum PendingOrBlock {
Block(BlockId),
Pending,
}
struct PendingUncleId {
id: PendingOrBlock,
position: usize,
}
enum PendingTransactionId {
Hash(H256),
Location(PendingOrBlock, usize)
}
impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> EthClient<C, SN, S, M, EM> where
C: MiningBlockChainClient + StateClient<State=T> + Call<State=T> + EngineInfo,
SN: SnapshotService,
S: SyncProvider,
M: MinerService,
M: MinerService<State=T>,
EM: ExternalMinerService {
/// Creates new EthClient.
@@ -145,9 +178,46 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
unwrap_provider(&self.accounts)
}
fn block(&self, id: BlockId, include_txs: bool) -> Result<Option<RichBlock>> {
fn rich_block(&self, id: BlockNumberOrId, include_txs: bool) -> Result<Option<RichBlock>> {
let client = &self.client;
match (client.block(id.clone()), client.block_total_difficulty(id)) {
let client_query = |id| (client.block(id), client.block_total_difficulty(id), client.block_extra_info(id));
let (block, difficulty, extra) = match id {
BlockNumberOrId::Number(BlockNumber::Pending) => {
let info = self.client.chain_info();
let pending_block = self.miner.pending_block(info.best_block_number);
let difficulty = {
let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed");
let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty());
if let Some(difficulty) = pending_difficulty {
difficulty + latest_difficulty
} else {
latest_difficulty
}
};
let extra = pending_block.as_ref().map(|b| self.client.engine().extra_info(&b.header));
(pending_block.map(|b| encoded::Block::new(b.rlp_bytes(Seal::Without))), Some(difficulty), extra)
},
BlockNumberOrId::Number(num) => {
let id = match num {
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Pending => unreachable!(), // Already covered
};
client_query(id)
},
BlockNumberOrId::Id(id) => client_query(id),
};
match (block, difficulty) {
(Some(block), Some(total_difficulty)) => {
let view = block.header_view();
Ok(Some(RichBlock {
@@ -176,29 +246,109 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
},
extra_data: Bytes::new(view.extra_data()),
},
extra_info: client.block_extra_info(id.clone()).expect(EXTRA_INFO_PROOF),
extra_info: extra.expect(EXTRA_INFO_PROOF),
}))
},
_ => Ok(None)
}
}
fn transaction(&self, id: TransactionId) -> Result<Option<Transaction>> {
match self.client.transaction(id) {
fn transaction(&self, id: PendingTransactionId) -> Result<Option<Transaction>> {
let client_transaction = |id| match self.client.transaction(id) {
Some(t) => Ok(Some(Transaction::from_localized(t, self.eip86_transition))),
None => Ok(None),
};
match id {
PendingTransactionId::Hash(hash) => client_transaction(TransactionId::Hash(hash)),
PendingTransactionId::Location(PendingOrBlock::Block(block), index) => {
client_transaction(TransactionId::Location(block, index))
},
PendingTransactionId::Location(PendingOrBlock::Pending, index) => {
let info = self.client.chain_info();
let pending_block = match self.miner.pending_block(info.best_block_number) {
Some(block) => block,
None => return Ok(None),
};
// Implementation stolen from `extract_transaction_at_index`
let transaction = pending_block.transactions.get(index)
// Verify if transaction signature is correct.
.and_then(|tx| SignedTransaction::new(tx.clone()).ok())
.map(|signed_tx| {
let (signed, sender, _) = signed_tx.deconstruct();
let block_hash = pending_block.header.hash();
let block_number = pending_block.header.number();
let transaction_index = index;
let cached_sender = Some(sender);
LocalizedTransaction {
signed,
block_number,
block_hash,
transaction_index,
cached_sender,
}
})
.map(|tx| Transaction::from_localized(tx, self.eip86_transition));
Ok(transaction)
}
}
}
fn uncle(&self, id: UncleId) -> Result<Option<RichBlock>> {
fn uncle(&self, id: PendingUncleId) -> Result<Option<RichBlock>> {
let client = &self.client;
let uncle: BlockHeader = match client.uncle(id) {
Some(hdr) => hdr.decode(),
None => { return Ok(None); }
};
let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) {
Some(difficulty) => difficulty,
None => { return Ok(None); }
let (uncle, parent_difficulty, extra) = match id {
PendingUncleId { id: PendingOrBlock::Pending, position } => {
let info = self.client.chain_info();
let pending_block = match self.miner.pending_block(info.best_block_number) {
Some(block) => block,
None => return Ok(None),
};
let uncle = match pending_block.uncles.get(position) {
Some(uncle) => uncle.clone(),
None => return Ok(None),
};
let difficulty = {
let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed");
let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty());
if let Some(difficulty) = pending_difficulty {
difficulty + latest_difficulty
} else {
latest_difficulty
}
};
let extra = self.client.engine().extra_info(&pending_block.header);
(uncle, difficulty, extra)
},
PendingUncleId { id: PendingOrBlock::Block(block_id), position } => {
let uncle_id = UncleId { block: block_id, position };
let uncle = match client.uncle(uncle_id) {
Some(hdr) => hdr.decode(),
None => { return Ok(None); }
};
let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) {
Some(difficulty) => difficulty,
None => { return Ok(None); }
};
let extra = client.uncle_extra_info(uncle_id).expect(EXTRA_INFO_PROOF);
(uncle, parent_difficulty, extra)
}
};
let size = client.block(BlockId::Hash(uncle.hash()))
@@ -229,7 +379,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
uncles: vec![],
transactions: BlockTransactions::Hashes(vec![]),
},
extra_info: client.uncle_extra_info(id).expect(EXTRA_INFO_PROOF),
extra_info: extra,
};
Ok(Some(block))
}
@@ -241,6 +391,24 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
.and_then(|_| store.dapp_addresses(dapp))
.map_err(|e| errors::account("Could not fetch accounts.", e))
}
fn get_state(&self, number: BlockNumber) -> StateOrBlock {
match number {
BlockNumber::Num(num) => BlockId::Number(num).into(),
BlockNumber::Earliest => BlockId::Earliest.into(),
BlockNumber::Latest => BlockId::Latest.into(),
BlockNumber::Pending => {
let info = self.client.chain_info();
self.miner
.pending_state(info.best_block_number)
.map(|s| Box::new(s) as Box<StateInfo>)
.unwrap_or(Box::new(self.client.latest_state()) as Box<StateInfo>)
.into()
}
}
}
}
pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
@@ -265,20 +433,27 @@ pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFi
fn check_known<C>(client: &C, number: BlockNumber) -> Result<()> where C: MiningBlockChainClient {
use ethcore::block_status::BlockStatus;
match client.block_status(number.into()) {
let id = match number {
BlockNumber::Pending => return Ok(()),
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Earliest => BlockId::Earliest,
};
match client.block_status(id) {
BlockStatus::InChain => Ok(()),
BlockStatus::Pending => Ok(()),
_ => Err(errors::unknown_block()),
}
}
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
C: MiningBlockChainClient + 'static,
impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<C, SN, S, M, EM> where
C: MiningBlockChainClient + StateClient<State=T> + Call<State=T> + EngineInfo + 'static,
SN: SnapshotService + 'static,
S: SyncProvider + 'static,
M: MinerService + 'static,
M: MinerService<State=T> + 'static,
EM: ExternalMinerService + 'static,
{
type Metadata = Metadata;
@@ -357,10 +532,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
let address = address.into();
let id = num.unwrap_or_default();
let num = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let res = match self.client.balance(&address, id.into()) {
try_bf!(check_known(&*self.client, num.clone()));
let res = match self.client.balance(&address, self.get_state(num)) {
Some(balance) => Ok(balance.into()),
None => Err(errors::state_pruned()),
};
@@ -372,10 +547,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
let address: Address = RpcH160::into(address);
let position: U256 = RpcU256::into(pos);
let id = num.unwrap_or_default();
let num = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let res = match self.client.storage_at(&address, &H256::from(position), id.into()) {
try_bf!(check_known(&*self.client, num.clone()));
let res = match self.client.storage_at(&address, &H256::from(position), self.get_state(num)) {
Some(s) => Ok(s.into()),
None => Err(errors::state_pruned()),
};
@@ -390,15 +565,33 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
BlockNumber::Pending if self.options.pending_nonce_from_queue => {
let nonce = self.miner.last_nonce(&address)
.map(|n| n + 1.into())
.or_else(|| self.client.nonce(&address, BlockNumber::Pending.into()));
.or_else(|| self.client.nonce(&address, BlockId::Latest));
match nonce {
Some(nonce) => Ok(nonce.into()),
None => Err(errors::database("latest nonce missing"))
}
}
id => {
try_bf!(check_known(&*self.client, id.clone()));
match self.client.nonce(&address, id.into()) {
},
BlockNumber::Pending => {
let info = self.client.chain_info();
let nonce = self.miner
.pending_state(info.best_block_number)
.and_then(|s| s.nonce(&address).ok())
.or_else(|| {
warn!("Fallback to `BlockId::Latest`");
self.client.nonce(&address, BlockId::Latest)
});
match nonce {
Some(nonce) => Ok(nonce.into()),
None => Err(errors::database("latest nonce missing"))
}
},
number => {
try_bf!(check_known(&*self.client, number.clone()));
match self.client.nonce(&address, block_number_to_id(number)) {
Some(nonce) => Ok(nonce.into()),
None => Err(errors::state_pruned()),
}
@@ -419,7 +612,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
self.miner.status().transactions_in_pending_block.into()
),
_ =>
self.client.block(num.into())
self.client.block(block_number_to_id(num))
.map(|block| block.transactions_count().into())
}))
}
@@ -432,7 +625,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
Box::new(future::ok(match num {
BlockNumber::Pending => Some(0.into()),
_ => self.client.block(num.into())
_ => self.client.block(block_number_to_id(num))
.map(|block| block.uncles_count().into()
),
}))
@@ -441,10 +634,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn code_at(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<Bytes> {
let address: Address = RpcH160::into(address);
let id = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let num = num.unwrap_or_default();
try_bf!(check_known(&*self.client, num.clone()));
let res = match self.client.code(&address, id.into()) {
let res = match self.client.code(&address, self.get_state(num)) {
Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)),
None => Err(errors::state_pruned()),
};
@@ -453,17 +646,17 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
}
fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> BoxFuture<Option<RichBlock>> {
Box::new(future::done(self.block(BlockId::Hash(hash.into()), include_txs)))
Box::new(future::done(self.rich_block(BlockId::Hash(hash.into()).into(), include_txs)))
}
fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>> {
Box::new(future::done(self.block(num.into(), include_txs)))
Box::new(future::done(self.rich_block(num.into(), include_txs)))
}
fn transaction_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<Transaction>> {
let hash: H256 = hash.into();
let block_number = self.client.chain_info().best_block_number;
let tx = try_bf!(self.transaction(TransactionId::Hash(hash))).or_else(|| {
let tx = try_bf!(self.transaction(PendingTransactionId::Hash(hash))).or_else(|| {
self.miner.transaction(block_number, &hash)
.map(|t| Transaction::from_pending(t, block_number, self.eip86_transition))
});
@@ -472,15 +665,20 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
}
fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture<Option<Transaction>> {
Box::new(future::done(
self.transaction(TransactionId::Location(BlockId::Hash(hash.into()), index.value()))
))
let id = PendingTransactionId::Location(PendingOrBlock::Block(BlockId::Hash(hash.into())), index.value());
Box::new(future::done(self.transaction(id)))
}
fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<Transaction>> {
Box::new(future::done(
self.transaction(TransactionId::Location(num.into(), index.value()))
))
let block_id = match num {
BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest),
BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest),
BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)),
BlockNumber::Pending => PendingOrBlock::Pending,
};
let transaction_id = PendingTransactionId::Location(block_id, index.value());
Box::new(future::done(self.transaction(transaction_id)))
}
fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture<Option<Receipt>> {
@@ -497,17 +695,22 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
}
fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture<Option<RichBlock>> {
Box::new(future::done(self.uncle(UncleId {
block: BlockId::Hash(hash.into()),
Box::new(future::done(self.uncle(PendingUncleId {
id: PendingOrBlock::Block(BlockId::Hash(hash.into())),
position: index.value()
})))
}
fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<RichBlock>> {
Box::new(future::done(self.uncle(UncleId {
block: num.into(),
position: index.value()
})))
let id = match num {
BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() },
BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() },
BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() },
BlockNumber::Pending => PendingUncleId { id: PendingOrBlock::Pending, position: index.value() },
};
Box::new(future::done(self.uncle(id)))
}
fn compilers(&self) -> Result<Vec<String>> {
@@ -630,7 +833,28 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
let signed = try_bf!(fake_sign::sign_call(request, meta.is_dapp()));
let num = num.unwrap_or_default();
let result = self.client.call(&signed, Default::default(), num.into());
let (mut state, header) = if num == BlockNumber::Pending {
let info = self.client.chain_info();
let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned()));
let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned()));
(state, header)
} else {
let id = match num {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => unreachable!(), // Already covered
};
let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned()));
let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned()));
(state, header.decode())
};
let result = self.client.call(&signed, Default::default(), &mut state, &header);
Box::new(future::done(result
.map(|b| b.output.into())
@@ -641,7 +865,29 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn estimate_gas(&self, meta: Self::Metadata, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
let request = CallRequest::into(request);
let signed = try_bf!(fake_sign::sign_call(request, meta.is_dapp()));
Box::new(future::done(self.client.estimate_gas(&signed, num.unwrap_or_default().into())
let num = num.unwrap_or_default();
let (state, header) = if num == BlockNumber::Pending {
let info = self.client.chain_info();
let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned()));
let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned()));
(state, header)
} else {
let id = match num {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => unreachable!(), // Already covered
};
let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned()));
let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned()));
(state, header.decode())
};
Box::new(future::done(self.client.estimate_gas(&signed, &state, &header)
.map(Into::into)
.map_err(errors::call)
))

View File

@@ -65,6 +65,23 @@ pub struct EthClient<T> {
gas_price_percentile: usize,
}
impl<T> EthClient<T> {
fn num_to_id(num: BlockNumber) -> BlockId {
// Note: Here we treat `Pending` as `Latest`.
// Since light clients don't produce pending blocks
// (they don't have state) we can safely fallback to `Latest`.
match num {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => {
warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`");
BlockId::Latest
}
}
}
}
impl<T> Clone for EthClient<T> {
fn clone(&self) -> Self {
// each instance should have its own poll manager.
@@ -264,7 +281,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
}
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().into())
Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default()))
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()))
}
@@ -277,11 +294,11 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
}
fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>> {
Box::new(self.rich_block(num.into(), include_txs).map(Some))
Box::new(self.rich_block(Self::num_to_id(num), include_txs).map(Some))
}
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().into())
Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default()))
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()))
}
@@ -304,7 +321,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
Box::new(self.fetcher().header(num.into()).and_then(move |hdr| {
Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| {
if hdr.transactions_root() == KECCAK_NULL_RLP {
Either::A(future::ok(Some(U256::from(0).into())))
} else {
@@ -336,7 +353,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
Box::new(self.fetcher().header(num.into()).and_then(move |hdr| {
Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| {
if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP {
Either::B(future::ok(Some(U256::from(0).into())))
} else {
@@ -350,7 +367,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
}
fn code_at(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<Bytes> {
Box::new(self.fetcher().code(address.into(), num.unwrap_or_default().into()).map(Into::into))
Box::new(self.fetcher().code(address.into(), Self::num_to_id(num.unwrap_or_default())).map(Into::into))
}
fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256> {
@@ -422,7 +439,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
fn transaction_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<Transaction>> {
let eip86 = self.client.eip86_transition();
Box::new(self.fetcher().block(num.into()).map(move |block| {
Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| {
light_fetch::extract_transaction_at_index(block, idx.value(), eip86)
}))
}
@@ -467,7 +484,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
fn uncle_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<RichBlock>> {
let client = self.client.clone();
Box::new(self.fetcher().block(num.into()).map(move |block| {
Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| {
extract_uncle_at_index(block, idx, client)
}))
}

View File

@@ -27,6 +27,7 @@ use ethsync::LightSyncProvider;
use ethcore::account_provider::AccountProvider;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use ethcore::ids::BlockId;
use light::client::LightChainClient;
@@ -405,7 +406,16 @@ impl Parity for ParityClient {
}
};
Box::new(self.fetcher().header(number.unwrap_or_default().into()).map(from_encoded))
// Note: Here we treat `Pending` as `Latest`.
// Since light clients don't produce pending blocks
// (they don't have state) we can safely fallback to `Latest`.
let id = match number.unwrap_or_default() {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest,
};
Box::new(self.fetcher().header(id).map(from_encoded))
}
fn ipfs_cid(&self, content: Bytes) -> Result<String> {

View File

@@ -27,10 +27,11 @@ use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::{SyncProvider, ManageNetwork};
use ethcore::account_provider::AccountProvider;
use ethcore::client::{MiningBlockChainClient};
use ethcore::client::{MiningBlockChainClient, StateClient, Call};
use ethcore::ids::BlockId;
use ethcore::miner::MinerService;
use ethcore::mode::Mode;
use ethcore::state::StateInfo;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use updater::{Service as UpdateService};
@@ -48,7 +49,8 @@ use v1::types::{
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
OperationsInfo, DappId, ChainStatus,
AccountInfo, HwAccountInfo, RichHeader
AccountInfo, HwAccountInfo, RichHeader,
block_number_to_id
};
use Host;
@@ -112,9 +114,10 @@ impl<C, M, U> ParityClient<C, M, U> where
}
}
impl<C, M, U> Parity for ParityClient<C, M, U> where
C: MiningBlockChainClient + 'static,
M: MinerService + 'static,
impl<C, M, U, S> Parity for ParityClient<C, M, U> where
S: StateInfo + 'static,
C: MiningBlockChainClient + StateClient<State=S> + Call<State=S> + 'static,
M: MinerService<State=S> + 'static,
U: UpdateService + 'static,
{
type Metadata = Metadata;
@@ -275,14 +278,32 @@ impl<C, M, U> Parity for ParityClient<C, M, U> where
}
fn list_accounts(&self, count: u64, after: Option<H160>, block_number: Trailing<BlockNumber>) -> Result<Option<Vec<H160>>> {
let number = match block_number.unwrap_or_default() {
BlockNumber::Pending => {
warn!("BlockNumber::Pending is unsupported");
return Ok(None);
},
num => block_number_to_id(num)
};
Ok(self.client
.list_accounts(block_number.unwrap_or_default().into(), after.map(Into::into).as_ref(), count)
.list_accounts(number, after.map(Into::into).as_ref(), count)
.map(|a| a.into_iter().map(Into::into).collect()))
}
fn list_storage_keys(&self, address: H160, count: u64, after: Option<H256>, block_number: Trailing<BlockNumber>) -> Result<Option<Vec<H256>>> {
let number = match block_number.unwrap_or_default() {
BlockNumber::Pending => {
warn!("BlockNumber::Pending is unsupported");
return Ok(None);
},
num => block_number_to_id(num)
};
Ok(self.client
.list_storage(block_number.unwrap_or_default().into(), &address.into(), after.map(Into::into).as_ref(), count)
.list_storage(number, &address.into(), after.map(Into::into).as_ref(), count)
.map(|a| a.into_iter().map(Into::into).collect()))
}
@@ -396,17 +417,31 @@ impl<C, M, U> Parity for ParityClient<C, M, U> where
}
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<RichHeader> {
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
const EXTRA_INFO_PROOF: &str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed";
let number = number.unwrap_or_default();
let id: BlockId = number.unwrap_or_default().into();
let encoded = match self.client.block_header(id.clone()) {
Some(encoded) => encoded,
None => return Box::new(future::err(errors::unknown_block())),
let (header, extra) = if number == BlockNumber::Pending {
let info = self.client.chain_info();
let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::unknown_block()));
(header.encoded(), None)
} else {
let id = match number {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => unreachable!(), // Already covered
};
let header = try_bf!(self.client.block_header(id.clone()).ok_or(errors::unknown_block()));
let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF);
(header, Some(info))
};
Box::new(future::ok(RichHeader {
inner: encoded.into(),
extra_info: self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF),
inner: header.into(),
extra_info: extra.unwrap_or_default(),
}))
}
@@ -414,7 +449,7 @@ impl<C, M, U> Parity for ParityClient<C, M, U> where
ipfs::cid(content)
}
fn call(&self, meta: Self::Metadata, requests: Vec<CallRequest>, block: Trailing<BlockNumber>) -> Result<Vec<Bytes>> {
fn call(&self, meta: Self::Metadata, requests: Vec<CallRequest>, num: Trailing<BlockNumber>) -> Result<Vec<Bytes>> {
let requests = requests
.into_iter()
.map(|request| Ok((
@@ -423,9 +458,29 @@ impl<C, M, U> Parity for ParityClient<C, M, U> where
)))
.collect::<Result<Vec<_>>>()?;
let block = block.unwrap_or_default();
let num = num.unwrap_or_default();
self.client.call_many(&requests, block.into())
let (mut state, header) = if num == BlockNumber::Pending {
let info = self.client.chain_info();
let state = self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned())?;
let header = self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned())?;
(state, header)
} else {
let id = match num {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => unreachable!(), // Already covered
};
let state = self.client.state_at(id).ok_or(errors::state_pruned())?;
let header = self.client.block_header(id).ok_or(errors::state_pruned())?;
(state, header.decode())
};
self.client.call_many(&requests, &mut state, &header)
.map(|res| res.into_iter().map(|res| res.output.into()).collect())
.map_err(errors::call)
}

View File

@@ -18,7 +18,7 @@
use std::sync::Arc;
use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId};
use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId};
use rlp::UntrustedRlp;
use transaction::SignedTransaction;
@@ -27,7 +27,7 @@ use jsonrpc_macros::Trailing;
use v1::Metadata;
use v1::traits::Traces;
use v1::helpers::{errors, fake_sign};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256, block_number_to_id};
fn to_call_analytics(flags: TraceOptions) -> CallAnalytics {
CallAnalytics {
@@ -51,7 +51,10 @@ impl<C> TracesClient<C> {
}
}
impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
impl<C, S> Traces for TracesClient<C> where
S: StateInfo + 'static,
C: MiningBlockChainClient + StateClient<State=S> + Call<State=S> + 'static
{
type Metadata = Metadata;
fn filter(&self, filter: TraceFilter) -> Result<Option<Vec<LocalizedTrace>>> {
@@ -60,7 +63,12 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
}
fn block_traces(&self, block_number: BlockNumber) -> Result<Option<Vec<LocalizedTrace>>> {
Ok(self.client.block_traces(block_number.into())
let id = match block_number {
BlockNumber::Pending => return Ok(None),
num => block_number_to_id(num)
};
Ok(self.client.block_traces(id)
.map(|traces| traces.into_iter().map(LocalizedTrace::from).collect()))
}
@@ -85,7 +93,18 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
let request = CallRequest::into(request);
let signed = fake_sign::sign_call(request, meta.is_dapp())?;
self.client.call(&signed, to_call_analytics(flags), block.into())
let id = match block {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())),
};
let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?;
let header = self.client.block_header(id).ok_or(errors::state_pruned())?;
self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode())
.map(TraceResults::from)
.map_err(errors::call)
}
@@ -101,7 +120,18 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
})
.collect::<Result<Vec<_>>>()?;
self.client.call_many(&requests, block.into())
let id = match block {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())),
};
let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?;
let header = self.client.block_header(id).ok_or(errors::state_pruned())?;
self.client.call_many(&requests, &mut state, &header.decode())
.map(|results| results.into_iter().map(TraceResults::from).collect())
.map_err(errors::call)
}
@@ -112,7 +142,18 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
let tx = UntrustedRlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?;
let signed = SignedTransaction::new(tx).map_err(errors::transaction)?;
self.client.call(&signed, to_call_analytics(flags), block.into())
let id = match block {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())),
};
let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?;
let header = self.client.block_header(id).ok_or(errors::state_pruned())?;
self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode())
.map(TraceResults::from)
.map_err(errors::call)
}
@@ -124,7 +165,15 @@ impl<C> Traces for TracesClient<C> where C: MiningBlockChainClient + 'static {
}
fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result<Vec<TraceResults>> {
self.client.replay_block_transactions(block_number.into(), to_call_analytics(flags))
let id = match block_number {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())),
};
self.client.replay_block_transactions(id, to_call_analytics(flags))
.map(|results| results.into_iter().map(TraceResults::from).collect())
.map_err(errors::call)
}

View File

@@ -22,10 +22,10 @@ use std::time::Duration;
use ethereum_types::{U256, H256, Address};
use ethcore::account_provider::AccountProvider;
use ethcore::block::Block;
use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::client::{BlockChainClient, Client, ClientConfig, ChainInfo, ImportBlock};
use ethcore::ethereum;
use ethcore::ids::BlockId;
use ethcore::miner::{MinerOptions, Banning, GasPricer, MinerService, Miner, PendingSet, GasLimit};
use ethcore::miner::{MinerOptions, Banning, GasPricer, Miner, PendingSet, GasLimit};
use ethcore::spec::{Genesis, Spec};
use ethcore::views::BlockView;
use ethjson::blockchain::BlockChain;
@@ -101,7 +101,7 @@ fn make_spec(chain: &BlockChain) -> Spec {
struct EthTester {
client: Arc<Client>,
_miner: Arc<MinerService>,
_miner: Arc<Miner>,
_snapshot: Arc<TestSnapshotService>,
accounts: Arc<AccountProvider>,
handler: IoHandler<Metadata>,

View File

@@ -18,16 +18,19 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::hash_map::Entry;
use ethereum_types::{H256, U256, Address};
use bytes::Bytes;
use ethcore::account_provider::SignError as AccountError;
use ethcore::block::ClosedBlock;
use ethcore::client::MiningBlockChainClient;
use ethcore::block::{Block, ClosedBlock};
use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo};
use ethcore::engines::EthEngine;
use ethcore::error::Error;
use ethcore::header::BlockNumber;
use ethcore::header::{BlockNumber, Header};
use ethcore::ids::BlockId;
use ethcore::miner::{MinerService, MinerStatus};
use miner::local_transactions::Status as LocalTransactionStatus;
use ethcore::receipt::{Receipt, RichReceipt};
use ethereum_types::{H256, U256, Address};
use miner::local_transactions::Status as LocalTransactionStatus;
use parking_lot::{RwLock, Mutex};
use transaction::{UnverifiedTransaction, SignedTransaction, PendingTransaction, ImportResult as TransactionImportResult};
@@ -92,7 +95,39 @@ impl TestMinerService {
}
}
impl StateClient for TestMinerService {
// State will not be used by test client anyway, since all methods that accept state are mocked
type State = ();
fn latest_state(&self) -> Self::State {
()
}
fn state_at(&self, _id: BlockId) -> Option<Self::State> {
Some(())
}
}
impl EngineInfo for TestMinerService {
fn engine(&self) -> &EthEngine {
unimplemented!()
}
}
impl MinerService for TestMinerService {
type State = ();
fn pending_state(&self, _latest_block_number: BlockNumber) -> Option<Self::State> {
None
}
fn pending_block_header(&self, _latest_block_number: BlockNumber) -> Option<Header> {
None
}
fn pending_block(&self, _latest_block_number: BlockNumber) -> Option<Block> {
None
}
/// Returns miner's status.
fn status(&self) -> MinerStatus {
@@ -164,7 +199,7 @@ impl MinerService for TestMinerService {
}
/// Imports transactions to transaction queue.
fn import_external_transactions(&self, _chain: &MiningBlockChainClient, transactions: Vec<UnverifiedTransaction>) ->
fn import_external_transactions<C>(&self, _chain: &C, transactions: Vec<UnverifiedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> {
// lets assume that all txs are valid
let transactions: Vec<_> = transactions.into_iter().map(|tx| SignedTransaction::new(tx).unwrap()).collect();
@@ -181,7 +216,7 @@ impl MinerService for TestMinerService {
}
/// Imports transactions to transaction queue.
fn import_own_transaction(&self, chain: &MiningBlockChainClient, pending: PendingTransaction) ->
fn import_own_transaction<C: Nonce>(&self, chain: &C, pending: PendingTransaction) ->
Result<TransactionImportResult, Error> {
// keep the pending nonces up to date
@@ -201,12 +236,12 @@ impl MinerService for TestMinerService {
}
/// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset(&self, _chain: &MiningBlockChainClient) {
fn clear_and_reset<C>(&self, _chain: &C) {
unimplemented!();
}
/// Called when blocks are imported to chain, updates transactions queue.
fn chain_new_blocks(&self, _chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
fn chain_new_blocks<C>(&self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
unimplemented!();
}
@@ -216,11 +251,11 @@ impl MinerService for TestMinerService {
}
/// New chain head event. Restart mining operation.
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
fn update_sealing<C>(&self, _chain: &C) {
unimplemented!();
}
fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
fn map_sealing_work<C: PrepareOpenBlock, F, T>(&self, chain: &C, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
let open_block = chain.prepare_open_block(self.author(), *self.gas_range_target.write(), self.extra_data());
Some(f(&open_block.close()))
}
@@ -229,7 +264,7 @@ impl MinerService for TestMinerService {
self.pending_transactions.lock().get(hash).cloned().map(Into::into)
}
fn remove_pending_transaction(&self, _chain: &MiningBlockChainClient, hash: &H256) -> Option<PendingTransaction> {
fn remove_pending_transaction<C>(&self, _chain: &C, hash: &H256) -> Option<PendingTransaction> {
self.pending_transactions.lock().remove(hash).map(Into::into)
}
@@ -279,7 +314,7 @@ impl MinerService for TestMinerService {
/// Submit `seal` as a valid solution for the header of `pow_hash`.
/// Will check the seal, but not actually insert the block into the chain.
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
fn submit_seal<C>(&self, _chain: &C, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
unimplemented!();
}

View File

@@ -82,7 +82,7 @@ fn should_subscribe_to_new_heads() {
fn should_subscribe_to_logs() {
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::ids::BlockId;
use ethcore::client::BlockChainClient;
use ethcore::client::BlockInfo;
// given
let el = EventLoop::spawn();

View File

@@ -91,14 +91,14 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
}
}
impl Into<BlockId> for BlockNumber {
fn into(self) -> BlockId {
match self {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => BlockId::Pending,
}
/// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending`
pub fn block_number_to_id(number: BlockNumber) -> BlockId {
match number {
BlockNumber::Num(num) => BlockId::Number(num),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually")
}
}
@@ -122,11 +122,17 @@ mod tests {
}
#[test]
fn block_number_into() {
assert_eq!(BlockId::Number(100), BlockNumber::Num(100).into());
assert_eq!(BlockId::Earliest, BlockNumber::Earliest.into());
assert_eq!(BlockId::Latest, BlockNumber::Latest.into());
assert_eq!(BlockId::Pending, BlockNumber::Pending.into());
fn normal_block_number_to_id() {
assert_eq!(block_number_to_id(BlockNumber::Num(100)), BlockId::Number(100));
assert_eq!(block_number_to_id(BlockNumber::Earliest), BlockId::Earliest);
assert_eq!(block_number_to_id(BlockNumber::Latest), BlockId::Latest);
}
#[test]
#[should_panic]
fn pending_block_number_to_id() {
// Since this function is not allowed to be called in such way, panic should happen
block_number_to_id(BlockNumber::Pending);
}
}

View File

@@ -72,9 +72,15 @@ pub struct Filter {
impl Into<EthFilter> for Filter {
fn into(self) -> EthFilter {
let num_to_id = |num| match num {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest,
};
EthFilter {
from_block: self.from_block.map_or_else(|| BlockId::Latest, Into::into),
to_block: self.to_block.map_or_else(|| BlockId::Latest, Into::into),
from_block: self.from_block.map_or_else(|| BlockId::Latest, &num_to_id),
to_block: self.to_block.map_or_else(|| BlockId::Latest, &num_to_id),
address: self.address.and_then(|address| match address {
VariadicValue::Null => None,
VariadicValue::Single(a) => Some(vec![a.into()]),

View File

@@ -50,7 +50,7 @@ pub mod pubsub;
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo};
pub use self::bytes::Bytes;
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
pub use self::block_number::BlockNumber;
pub use self::block_number::{BlockNumber, block_number_to_id};
pub use self::call_request::CallRequest;
pub use self::confirmations::{
ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken,

View File

@@ -44,8 +44,17 @@ pub struct TraceFilter {
impl Into<client::TraceFilter> for TraceFilter {
fn into(self) -> client::TraceFilter {
let start = self.from_block.map_or(BlockId::Latest, Into::into);
let end = self.to_block.map_or(BlockId::Latest, Into::into);
let num_to_id = |num| match num {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => {
warn!("Pending traces are not supported and might be removed in future versions. Falling back to Latest");
BlockId::Latest
}
};
let start = self.from_block.map_or(BlockId::Latest, &num_to_id);
let end = self.to_block.map_or(BlockId::Latest, &num_to_id);
client::TraceFilter {
range: start..end,
from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()),