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:
committed by
Marek Kotewicz
parent
81b52c7336
commit
9d7d6f7108
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
))
|
||||
|
||||
@@ -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)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()]),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()),
|
||||
|
||||
Reference in New Issue
Block a user