RPC: parity_getBlockReceipts (#9527)
* Block receipts RPC. * Use lazy evaluation of block receipts (ecrecover). * Optimize transaction_receipt to prevent performance regression. * Fix RPC grumbles. * Add block & transaction receipt tests. * Fix conversion to block id.
This commit is contained in:
parent
3f95a62e4f
commit
cc963d42a0
@ -176,7 +176,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
|
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
|
||||||
BlockChainClient::block_receipts(self, &req.hash)
|
BlockChainClient::encoded_block_receipts(self, &req.hash)
|
||||||
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) })
|
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,11 +160,6 @@ pub trait BlockProvider {
|
|||||||
.and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index)))
|
.and_then(|n| body.view().localized_transaction_at(&address.block_hash, n, address.index)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get transaction receipt.
|
|
||||||
fn transaction_receipt(&self, address: &TransactionAddress) -> Option<Receipt> {
|
|
||||||
self.block_receipts(&address.block_hash).and_then(|br| br.receipts.into_iter().nth(address.index))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a list of transactions for a given block.
|
/// Get a list of transactions for a given block.
|
||||||
/// Returns None if block does not exist.
|
/// Returns None if block does not exist.
|
||||||
fn transactions(&self, hash: &H256) -> Option<Vec<LocalizedTransaction>> {
|
fn transactions(&self, hash: &H256) -> Option<Vec<LocalizedTransaction>> {
|
||||||
|
@ -1792,26 +1792,49 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
|
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
|
||||||
|
// NOTE Don't use block_receipts here for performance reasons
|
||||||
|
let address = self.transaction_address(id)?;
|
||||||
|
let hash = address.block_hash;
|
||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
self.transaction_address(id)
|
let number = chain.block_number(&hash)?;
|
||||||
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
|
let body = chain.block_body(&hash)?;
|
||||||
let transaction = chain.block_body(&address.block_hash)
|
let mut receipts = chain.block_receipts(&hash)?.receipts;
|
||||||
.and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index));
|
receipts.truncate(address.index + 1);
|
||||||
|
|
||||||
let previous_receipts = (0..address.index + 1)
|
let transaction = body.view().localized_transaction_at(&hash, number, address.index)?;
|
||||||
.map(|index| {
|
let receipt = receipts.pop()?;
|
||||||
let mut address = address.clone();
|
let gas_used = receipts.last().map_or_else(|| 0.into(), |r| r.gas_used);
|
||||||
address.index = index;
|
let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
|
||||||
chain.transaction_receipt(&address)
|
|
||||||
})
|
let receipt = transaction_receipt(self.engine().machine(), transaction, receipt, gas_used, no_of_logs);
|
||||||
.collect();
|
Some(receipt)
|
||||||
match (transaction, previous_receipts) {
|
}
|
||||||
(Some(transaction), Some(previous_receipts)) => {
|
|
||||||
Some(transaction_receipt(self.engine().machine(), transaction, previous_receipts))
|
fn block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||||
},
|
let hash = self.block_hash(id)?;
|
||||||
_ => None,
|
|
||||||
}
|
let chain = self.chain.read();
|
||||||
}))
|
let receipts = chain.block_receipts(&hash)?;
|
||||||
|
let number = chain.block_number(&hash)?;
|
||||||
|
let body = chain.block_body(&hash)?;
|
||||||
|
let engine = self.engine.clone();
|
||||||
|
|
||||||
|
let mut gas_used = 0.into();
|
||||||
|
let mut no_of_logs = 0;
|
||||||
|
|
||||||
|
Some(body
|
||||||
|
.view()
|
||||||
|
.localized_transactions(&hash, number)
|
||||||
|
.into_iter()
|
||||||
|
.zip(receipts.receipts)
|
||||||
|
.map(move |(transaction, receipt)| {
|
||||||
|
let result = transaction_receipt(engine.machine(), transaction, receipt, gas_used, no_of_logs);
|
||||||
|
gas_used = result.cumulative_gas_used;
|
||||||
|
no_of_logs += result.logs.len();
|
||||||
|
result
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||||
@ -1830,7 +1853,7 @@ impl BlockChainClient for Client {
|
|||||||
self.state_db.read().journal_db().state(hash)
|
self.state_db.read().journal_db().state(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||||
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).into_vec())
|
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).into_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2385,16 +2408,14 @@ impl Drop for Client {
|
|||||||
|
|
||||||
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
|
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
|
||||||
/// and a vector of receipts from given block up to transaction index.
|
/// and a vector of receipts from given block up to transaction index.
|
||||||
fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
|
fn transaction_receipt(
|
||||||
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");
|
machine: &::machine::EthereumMachine,
|
||||||
|
mut tx: LocalizedTransaction,
|
||||||
|
receipt: Receipt,
|
||||||
|
prior_gas_used: U256,
|
||||||
|
prior_no_of_logs: usize,
|
||||||
|
) -> LocalizedReceipt {
|
||||||
let sender = tx.sender();
|
let sender = tx.sender();
|
||||||
let receipt = receipts.pop().expect("Current receipt is provided; qed");
|
|
||||||
let prior_gas_used = match tx.transaction_index {
|
|
||||||
0 => 0.into(),
|
|
||||||
i => receipts.get(i - 1).expect("All previous receipts are provided; qed").gas_used,
|
|
||||||
};
|
|
||||||
let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
|
|
||||||
let transaction_hash = tx.hash();
|
let transaction_hash = tx.hash();
|
||||||
let block_hash = tx.block_hash;
|
let block_hash = tx.block_hash;
|
||||||
let block_number = tx.block_number;
|
let block_number = tx.block_number;
|
||||||
@ -2423,7 +2444,7 @@ fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTr
|
|||||||
transaction_hash: transaction_hash,
|
transaction_hash: transaction_hash,
|
||||||
transaction_index: transaction_index,
|
transaction_index: transaction_index,
|
||||||
transaction_log_index: i,
|
transaction_log_index: i,
|
||||||
log_index: no_of_logs + i,
|
log_index: prior_no_of_logs + i,
|
||||||
}).collect(),
|
}).collect(),
|
||||||
log_bloom: receipt.log_bloom,
|
log_bloom: receipt.log_bloom,
|
||||||
outcome: receipt.outcome,
|
outcome: receipt.outcome,
|
||||||
@ -2471,6 +2492,33 @@ mod tests {
|
|||||||
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_block_receipts() {
|
||||||
|
use client::{BlockChainClient, BlockId, TransactionId};
|
||||||
|
use test_helpers::{generate_dummy_client_with_data};
|
||||||
|
|
||||||
|
let client = generate_dummy_client_with_data(2, 2, &[1.into(), 1.into()]);
|
||||||
|
let receipts = client.block_receipts(BlockId::Latest).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(receipts.len(), 2);
|
||||||
|
assert_eq!(receipts[0].transaction_index, 0);
|
||||||
|
assert_eq!(receipts[0].block_number, 2);
|
||||||
|
assert_eq!(receipts[0].cumulative_gas_used, 53_000.into());
|
||||||
|
assert_eq!(receipts[0].gas_used, 53_000.into());
|
||||||
|
|
||||||
|
assert_eq!(receipts[1].transaction_index, 1);
|
||||||
|
assert_eq!(receipts[1].block_number, 2);
|
||||||
|
assert_eq!(receipts[1].cumulative_gas_used, 106_000.into());
|
||||||
|
assert_eq!(receipts[1].gas_used, 53_000.into());
|
||||||
|
|
||||||
|
|
||||||
|
let receipt = client.transaction_receipt(TransactionId::Hash(receipts[0].transaction_hash));
|
||||||
|
assert_eq!(receipt, Some(receipts[0].clone()));
|
||||||
|
|
||||||
|
let receipt = client.transaction_receipt(TransactionId::Hash(receipts[1].transaction_hash));
|
||||||
|
assert_eq!(receipt, Some(receipts[1].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_log_index() {
|
fn should_return_correct_log_index() {
|
||||||
use hash::keccak;
|
use hash::keccak;
|
||||||
@ -2514,20 +2562,15 @@ mod tests {
|
|||||||
topics: vec![],
|
topics: vec![],
|
||||||
data: vec![],
|
data: vec![],
|
||||||
}];
|
}];
|
||||||
let receipts = vec![Receipt {
|
let receipt = Receipt {
|
||||||
outcome: TransactionOutcome::StateRoot(state_root),
|
|
||||||
gas_used: 5.into(),
|
|
||||||
log_bloom: Default::default(),
|
|
||||||
logs: vec![logs[0].clone()],
|
|
||||||
}, Receipt {
|
|
||||||
outcome: TransactionOutcome::StateRoot(state_root),
|
outcome: TransactionOutcome::StateRoot(state_root),
|
||||||
gas_used: gas_used,
|
gas_used: gas_used,
|
||||||
log_bloom: Default::default(),
|
log_bloom: Default::default(),
|
||||||
logs: logs.clone(),
|
logs: logs.clone(),
|
||||||
}];
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let receipt = transaction_receipt(&machine, transaction, receipts);
|
let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(receipt, LocalizedReceipt {
|
assert_eq!(receipt, LocalizedReceipt {
|
||||||
|
@ -687,6 +687,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
self.receipts.read().get(&id).cloned()
|
self.receipts.read().get(&id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_receipts(&self, _id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||||
|
Some(self.receipts.read().values().cloned().collect())
|
||||||
|
}
|
||||||
|
|
||||||
fn logs(&self, filter: Filter) -> Result<Vec<LocalizedLogEntry>, BlockId> {
|
fn logs(&self, filter: Filter) -> Result<Vec<LocalizedLogEntry>, BlockId> {
|
||||||
match self.error_on_logs.read().as_ref() {
|
match self.error_on_logs.read().as_ref() {
|
||||||
Some(id) => return Err(id.clone()),
|
Some(id) => return Err(id.clone()),
|
||||||
@ -786,7 +790,7 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||||
// starts with 'f' ?
|
// starts with 'f' ?
|
||||||
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
||||||
let receipt = BlockReceipts::new(vec![Receipt::new(
|
let receipt = BlockReceipts::new(vec![Receipt::new(
|
||||||
|
@ -281,6 +281,9 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
|
|||||||
/// Get transaction receipt with given hash.
|
/// Get transaction receipt with given hash.
|
||||||
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
|
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
|
||||||
|
|
||||||
|
/// Get localized receipts for all transaction in given block.
|
||||||
|
fn block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>>;
|
||||||
|
|
||||||
/// Get a tree route between `from` and `to`.
|
/// Get a tree route between `from` and `to`.
|
||||||
/// See `BlockChain::tree_route`.
|
/// See `BlockChain::tree_route`.
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
||||||
@ -292,7 +295,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
|
|||||||
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
/// Get raw block receipts data by block header hash.
|
/// Get raw block receipts data by block header hash.
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
/// Get block queue information.
|
/// Get block queue information.
|
||||||
fn queue_info(&self) -> BlockQueueInfo;
|
fn queue_info(&self) -> BlockQueueInfo;
|
||||||
|
@ -50,7 +50,7 @@ use executive::contract_address;
|
|||||||
use header::{Header, BlockNumber};
|
use header::{Header, BlockNumber};
|
||||||
use miner;
|
use miner;
|
||||||
use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache};
|
use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache};
|
||||||
use receipt::{Receipt, RichReceipt};
|
use receipt::RichReceipt;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use state::State;
|
use state::State;
|
||||||
use ethkey::Password;
|
use ethkey::Password;
|
||||||
@ -1039,19 +1039,17 @@ impl miner::MinerService for Miner {
|
|||||||
self.transaction_queue.status()
|
self.transaction_queue.status()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
fn pending_receipts(&self, best_block: BlockNumber) -> Option<Vec<RichReceipt>> {
|
||||||
self.map_existing_pending_block(|pending| {
|
self.map_existing_pending_block(|pending| {
|
||||||
let txs = pending.transactions();
|
let receipts = pending.receipts();
|
||||||
txs.iter()
|
pending.transactions()
|
||||||
.map(|t| t.hash())
|
.into_iter()
|
||||||
.position(|t| t == *hash)
|
.enumerate()
|
||||||
.map(|index| {
|
.map(|(index, tx)| {
|
||||||
let receipts = pending.receipts();
|
|
||||||
let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used };
|
let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used };
|
||||||
let tx = &txs[index];
|
|
||||||
let receipt = &receipts[index];
|
let receipt = &receipts[index];
|
||||||
RichReceipt {
|
RichReceipt {
|
||||||
transaction_hash: hash.clone(),
|
transaction_hash: tx.hash(),
|
||||||
transaction_index: index,
|
transaction_index: index,
|
||||||
cumulative_gas_used: receipt.gas_used,
|
cumulative_gas_used: receipt.gas_used,
|
||||||
gas_used: receipt.gas_used - prev_gas,
|
gas_used: receipt.gas_used - prev_gas,
|
||||||
@ -1067,15 +1065,7 @@ impl miner::MinerService for Miner {
|
|||||||
outcome: receipt.outcome.clone(),
|
outcome: receipt.outcome.clone(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, best_block).and_then(|x| x)
|
.collect()
|
||||||
}
|
|
||||||
|
|
||||||
fn pending_receipts(&self, best_block: BlockNumber) -> Option<BTreeMap<H256, Receipt>> {
|
|
||||||
self.map_existing_pending_block(|pending| {
|
|
||||||
let hashes = pending.transactions().iter().map(|t| t.hash());
|
|
||||||
let receipts = pending.receipts().iter().cloned();
|
|
||||||
|
|
||||||
hashes.zip(receipts).collect()
|
|
||||||
}, best_block)
|
}, best_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ use client::{
|
|||||||
};
|
};
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use header::{BlockNumber, Header};
|
use header::{BlockNumber, Header};
|
||||||
use receipt::{RichReceipt, Receipt};
|
use receipt::RichReceipt;
|
||||||
use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction};
|
use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction};
|
||||||
use state::StateInfo;
|
use state::StateInfo;
|
||||||
use ethkey::Password;
|
use ethkey::Password;
|
||||||
@ -95,10 +95,13 @@ pub trait MinerService : Send + Sync {
|
|||||||
// Pending block
|
// Pending block
|
||||||
|
|
||||||
/// Get a list of all pending receipts from pending block.
|
/// Get a list of all pending receipts from pending block.
|
||||||
fn pending_receipts(&self, best_block: BlockNumber) -> Option<BTreeMap<H256, Receipt>>;
|
fn pending_receipts(&self, best_block: BlockNumber) -> Option<Vec<RichReceipt>>;
|
||||||
|
|
||||||
/// Get a particular receipt from pending block.
|
/// Get a particular receipt from pending block.
|
||||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
|
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
||||||
|
let receipts = self.pending_receipts(best_block)?;
|
||||||
|
receipts.into_iter().find(|r| &r.transaction_hash == hash)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||||
fn pending_state(&self, latest_block_number: BlockNumber) -> Option<Self::State>;
|
fn pending_state(&self, latest_block_number: BlockNumber) -> Option<Self::State>;
|
||||||
|
@ -226,7 +226,7 @@ impl SyncSupplier {
|
|||||||
let mut added_receipts = 0usize;
|
let mut added_receipts = 0usize;
|
||||||
let mut data = Bytes::new();
|
let mut data = Bytes::new();
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
if let Some(mut receipts_bytes) = io.chain().block_receipts(&rlp.val_at::<H256>(i)?) {
|
if let Some(mut receipts_bytes) = io.chain().encoded_block_receipts(&rlp.val_at::<H256>(i)?) {
|
||||||
data.append(&mut receipts_bytes);
|
data.append(&mut receipts_bytes);
|
||||||
added_receipts += receipts_bytes.len();
|
added_receipts += receipts_bytes.len();
|
||||||
added_headers += 1;
|
added_headers += 1;
|
||||||
|
@ -29,7 +29,6 @@ use ethcore::account_provider::AccountProvider;
|
|||||||
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
|
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
|
||||||
use ethcore::filter::Filter as EthcoreFilter;
|
use ethcore::filter::Filter as EthcoreFilter;
|
||||||
use ethcore::header::{BlockNumber as EthBlockNumber};
|
use ethcore::header::{BlockNumber as EthBlockNumber};
|
||||||
use ethcore::log_entry::LogEntry;
|
|
||||||
use ethcore::miner::{self, MinerService};
|
use ethcore::miner::{self, MinerService};
|
||||||
use ethcore::snapshot::SnapshotService;
|
use ethcore::snapshot::SnapshotService;
|
||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
@ -419,11 +418,11 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> EthClient<C, SN, S
|
|||||||
pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||||
let receipts = miner.pending_receipts(best_block).unwrap_or_default();
|
let receipts = miner.pending_receipts(best_block).unwrap_or_default();
|
||||||
|
|
||||||
let pending_logs = receipts.into_iter()
|
receipts.into_iter()
|
||||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
.flat_map(|r| {
|
||||||
.collect::<Vec<(H256, LogEntry)>>();
|
let hash = r.transaction_hash;
|
||||||
|
r.logs.into_iter().map(move |l| (hash, l))
|
||||||
pending_logs.into_iter()
|
})
|
||||||
.filter(|pair| filter.matches(&pair.1))
|
.filter(|pair| filter.matches(&pair.1))
|
||||||
.map(|pair| {
|
.map(|pair| {
|
||||||
let mut log = Log::from(pair.1);
|
let mut log = Log::from(pair.1);
|
||||||
@ -673,16 +672,17 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture<Option<Receipt>> {
|
fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture<Option<Receipt>> {
|
||||||
let best_block = self.client.chain_info().best_block_number;
|
|
||||||
let hash: H256 = hash.into();
|
let hash: H256 = hash.into();
|
||||||
|
|
||||||
match (self.miner.pending_receipt(best_block, &hash), self.options.allow_pending_receipt_query) {
|
if self.options.allow_pending_receipt_query {
|
||||||
(Some(receipt), true) => Box::new(future::ok(Some(receipt.into()))),
|
let best_block = self.client.chain_info().best_block_number;
|
||||||
_ => {
|
if let Some(receipt) = self.miner.pending_receipt(best_block, &hash) {
|
||||||
let receipt = self.client.transaction_receipt(TransactionId::Hash(hash));
|
return Box::new(future::ok(Some(receipt.into())));
|
||||||
Box::new(future::ok(receipt.map(Into::into)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let receipt = self.client.transaction_receipt(TransactionId::Hash(hash));
|
||||||
|
Box::new(future::ok(receipt.map(Into::into)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture<Option<RichBlock>> {
|
fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture<Option<RichBlock>> {
|
||||||
|
@ -46,7 +46,7 @@ use v1::helpers::{SyncPollFilter, PollManager};
|
|||||||
use v1::helpers::light_fetch::{self, LightFetch};
|
use v1::helpers::light_fetch::{self, LightFetch};
|
||||||
use v1::traits::Eth;
|
use v1::traits::Eth;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
RichBlock, Block, BlockTransactions, BlockNumber, LightBlockNumber, Bytes, SyncStatus, SyncInfo,
|
||||||
Transaction, CallRequest, Index, Filter, Log, Receipt, Work,
|
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,
|
||||||
};
|
};
|
||||||
@ -67,23 +67,6 @@ pub struct EthClient<T> {
|
|||||||
gas_price_percentile: usize,
|
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> {
|
impl<T> Clone for EthClient<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// each instance should have its own poll manager.
|
// each instance should have its own poll manager.
|
||||||
@ -285,7 +268,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
|
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
|
||||||
Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default()))
|
Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().to_block_id())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()))
|
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,11 +281,11 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>> {
|
fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>> {
|
||||||
Box::new(self.rich_block(Self::num_to_id(num), include_txs).map(Some))
|
Box::new(self.rich_block(num.to_block_id(), include_txs).map(Some))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
|
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256> {
|
||||||
Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default()))
|
Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().to_block_id())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()))
|
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +308,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
|
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| {
|
Box::new(self.fetcher().header(num.to_block_id()).and_then(move |hdr| {
|
||||||
if hdr.transactions_root() == KECCAK_NULL_RLP {
|
if hdr.transactions_root() == KECCAK_NULL_RLP {
|
||||||
Either::A(future::ok(Some(U256::from(0).into())))
|
Either::A(future::ok(Some(U256::from(0).into())))
|
||||||
} else {
|
} else {
|
||||||
@ -357,7 +340,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
|
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| {
|
Box::new(self.fetcher().header(num.to_block_id()).and_then(move |hdr| {
|
||||||
if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP {
|
if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP {
|
||||||
Either::B(future::ok(Some(U256::from(0).into())))
|
Either::B(future::ok(Some(U256::from(0).into())))
|
||||||
} else {
|
} else {
|
||||||
@ -371,7 +354,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn code_at(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<Bytes> {
|
fn code_at(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<Bytes> {
|
||||||
Box::new(self.fetcher().code(address.into(), Self::num_to_id(num.unwrap_or_default())).map(Into::into))
|
Box::new(self.fetcher().code(address.into(), num.unwrap_or_default().to_block_id()).map(Into::into))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256> {
|
fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256> {
|
||||||
@ -438,7 +421,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<Transaction>> {
|
fn transaction_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<Transaction>> {
|
||||||
Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| {
|
Box::new(self.fetcher().block(num.to_block_id()).map(move |block| {
|
||||||
light_fetch::extract_transaction_at_index(block, idx.value())
|
light_fetch::extract_transaction_at_index(block, idx.value())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -482,7 +465,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
|
|||||||
|
|
||||||
fn uncle_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<RichBlock>> {
|
fn uncle_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture<Option<RichBlock>> {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| {
|
Box::new(self.fetcher().block(num.to_block_id()).map(move |block| {
|
||||||
extract_uncle_at_index(block, idx, client)
|
extract_uncle_at_index(block, idx, client)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ use ethstore::random_phrase;
|
|||||||
use sync::LightSyncProvider;
|
use sync::LightSyncProvider;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore_logger::RotatingLogger;
|
use ethcore_logger::RotatingLogger;
|
||||||
use ethcore::ids::BlockId;
|
|
||||||
|
|
||||||
use light::client::LightChainClient;
|
use light::client::LightChainClient;
|
||||||
|
|
||||||
@ -42,9 +41,9 @@ use v1::types::{
|
|||||||
Bytes, U256, U64, H160, H256, H512, CallRequest,
|
Bytes, U256, U64, H160, H256, H512, CallRequest,
|
||||||
Peers, Transaction, RpcSettings, Histogram,
|
Peers, Transaction, RpcSettings, Histogram,
|
||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, LightBlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, ChainStatus,
|
OperationsInfo, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo, Header, RichHeader,
|
AccountInfo, HwAccountInfo, Header, RichHeader, Receipt,
|
||||||
};
|
};
|
||||||
use Host;
|
use Host;
|
||||||
|
|
||||||
@ -403,18 +402,15 @@ impl Parity for ParityClient {
|
|||||||
extra_info: extra_info,
|
extra_info: extra_info,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
// Note: Here we treat `Pending` as `Latest`.
|
let id = number.unwrap_or_default().to_block_id();
|
||||||
// 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).and_then(from_encoded))
|
Box::new(self.fetcher().header(id).and_then(from_encoded))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_receipts(&self, number: Trailing<BlockNumber>) -> BoxFuture<Vec<Receipt>> {
|
||||||
|
let id = number.unwrap_or_default().to_block_id();
|
||||||
|
Box::new(self.fetcher().receipts(id).and_then(|receipts| Ok(receipts.into_iter().map(Into::into).collect())))
|
||||||
|
}
|
||||||
|
|
||||||
fn ipfs_cid(&self, content: Bytes) -> Result<String> {
|
fn ipfs_cid(&self, content: Bytes) -> Result<String> {
|
||||||
ipfs::cid(content)
|
ipfs::cid(content)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, ChainStatus,
|
OperationsInfo, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo, RichHeader,
|
AccountInfo, HwAccountInfo, RichHeader, Receipt,
|
||||||
block_number_to_id
|
block_number_to_id
|
||||||
};
|
};
|
||||||
use Host;
|
use Host;
|
||||||
@ -332,7 +332,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
|
|
||||||
fn ws_url(&self) -> Result<String> {
|
fn ws_url(&self) -> Result<String> {
|
||||||
helpers::to_url(&self.ws_address)
|
helpers::to_url(&self.ws_address)
|
||||||
.ok_or_else(|| errors::ws_disabled())
|
.ok_or_else(errors::ws_disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_nonce(&self, address: H160) -> BoxFuture<U256> {
|
fn next_nonce(&self, address: H160) -> BoxFuture<U256> {
|
||||||
@ -387,7 +387,8 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
|
|
||||||
let (header, extra) = if number == BlockNumber::Pending {
|
let (header, extra) = if number == BlockNumber::Pending {
|
||||||
let info = self.client.chain_info();
|
let info = self.client.chain_info();
|
||||||
let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::unknown_block()));
|
let header =
|
||||||
|
try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::unknown_block));
|
||||||
|
|
||||||
(header.encoded(), None)
|
(header.encoded(), None)
|
||||||
} else {
|
} else {
|
||||||
@ -398,7 +399,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
BlockNumber::Pending => unreachable!(), // Already covered
|
BlockNumber::Pending => unreachable!(), // Already covered
|
||||||
};
|
};
|
||||||
|
|
||||||
let header = try_bf!(self.client.block_header(id.clone()).ok_or(errors::unknown_block()));
|
let header = try_bf!(self.client.block_header(id.clone()).ok_or_else(errors::unknown_block));
|
||||||
let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF);
|
let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF);
|
||||||
|
|
||||||
(header, Some(info))
|
(header, Some(info))
|
||||||
@ -410,6 +411,27 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_receipts(&self, number: Trailing<BlockNumber>) -> BoxFuture<Vec<Receipt>> {
|
||||||
|
let number = number.unwrap_or_default();
|
||||||
|
|
||||||
|
let id = match number {
|
||||||
|
BlockNumber::Pending => {
|
||||||
|
let info = self.client.chain_info();
|
||||||
|
let receipts = try_bf!(self.miner.pending_receipts(info.best_block_number).ok_or_else(errors::unknown_block));
|
||||||
|
return Box::new(future::ok(receipts
|
||||||
|
.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect()
|
||||||
|
))
|
||||||
|
},
|
||||||
|
BlockNumber::Num(num) => BlockId::Number(num),
|
||||||
|
BlockNumber::Earliest => BlockId::Earliest,
|
||||||
|
BlockNumber::Latest => BlockId::Latest,
|
||||||
|
};
|
||||||
|
let receipts = try_bf!(self.client.block_receipts(id).ok_or_else(errors::unknown_block));
|
||||||
|
Box::new(future::ok(receipts.into_iter().map(Into::into).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
fn ipfs_cid(&self, content: Bytes) -> Result<String> {
|
fn ipfs_cid(&self, content: Bytes) -> Result<String> {
|
||||||
ipfs::cid(content)
|
ipfs::cid(content)
|
||||||
}
|
}
|
||||||
@ -427,8 +449,8 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
|
|
||||||
let (mut state, header) = if num == BlockNumber::Pending {
|
let (mut state, header) = if num == BlockNumber::Pending {
|
||||||
let info = self.client.chain_info();
|
let info = self.client.chain_info();
|
||||||
let state = self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned())?;
|
let state = self.miner.pending_state(info.best_block_number).ok_or_else(errors::state_pruned)?;
|
||||||
let header = self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned())?;
|
let header = self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::state_pruned)?;
|
||||||
|
|
||||||
(state, header)
|
(state, header)
|
||||||
} else {
|
} else {
|
||||||
@ -439,8 +461,8 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
|||||||
BlockNumber::Pending => unreachable!(), // Already covered
|
BlockNumber::Pending => unreachable!(), // Already covered
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = self.client.state_at(id).ok_or(errors::state_pruned())?;
|
let state = self.client.state_at(id).ok_or_else(errors::state_pruned)?;
|
||||||
let header = self.client.block_header(id).ok_or(errors::state_pruned())?.decode().map_err(errors::decode)?;
|
let header = self.client.block_header(id).ok_or_else(errors::state_pruned)?.decode().map_err(errors::decode)?;
|
||||||
|
|
||||||
(state, header)
|
(state, header)
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,7 @@ use ethcore::error::Error;
|
|||||||
use ethcore::header::{BlockNumber, Header};
|
use ethcore::header::{BlockNumber, Header};
|
||||||
use ethcore::ids::BlockId;
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::miner::{self, MinerService, AuthoringParams};
|
use ethcore::miner::{self, MinerService, AuthoringParams};
|
||||||
use ethcore::receipt::{Receipt, RichReceipt};
|
use ethcore::receipt::RichReceipt;
|
||||||
use ethereum_types::{H256, U256, Address};
|
use ethereum_types::{H256, U256, Address};
|
||||||
use miner::pool::local_transactions::Status as LocalTransactionStatus;
|
use miner::pool::local_transactions::Status as LocalTransactionStatus;
|
||||||
use miner::pool::{verifier, VerifiedTransaction, QueueStatus};
|
use miner::pool::{verifier, VerifiedTransaction, QueueStatus};
|
||||||
@ -46,7 +46,7 @@ pub struct TestMinerService {
|
|||||||
/// Pre-existed local transactions
|
/// Pre-existed local transactions
|
||||||
pub local_transactions: Mutex<BTreeMap<H256, LocalTransactionStatus>>,
|
pub local_transactions: Mutex<BTreeMap<H256, LocalTransactionStatus>>,
|
||||||
/// Pre-existed pending receipts
|
/// Pre-existed pending receipts
|
||||||
pub pending_receipts: Mutex<BTreeMap<H256, Receipt>>,
|
pub pending_receipts: Mutex<Vec<RichReceipt>>,
|
||||||
/// Next nonces.
|
/// Next nonces.
|
||||||
pub next_nonces: RwLock<HashMap<Address, U256>>,
|
pub next_nonces: RwLock<HashMap<Address, U256>>,
|
||||||
/// Password held by Engine.
|
/// Password held by Engine.
|
||||||
@ -58,11 +58,11 @@ pub struct TestMinerService {
|
|||||||
impl Default for TestMinerService {
|
impl Default for TestMinerService {
|
||||||
fn default() -> TestMinerService {
|
fn default() -> TestMinerService {
|
||||||
TestMinerService {
|
TestMinerService {
|
||||||
imported_transactions: Mutex::new(Vec::new()),
|
imported_transactions: Default::default(),
|
||||||
pending_transactions: Mutex::new(HashMap::new()),
|
pending_transactions: Default::default(),
|
||||||
local_transactions: Mutex::new(BTreeMap::new()),
|
local_transactions: Default::default(),
|
||||||
pending_receipts: Mutex::new(BTreeMap::new()),
|
pending_receipts: Default::default(),
|
||||||
next_nonces: RwLock::new(HashMap::new()),
|
next_nonces: Default::default(),
|
||||||
password: RwLock::new("".into()),
|
password: RwLock::new("".into()),
|
||||||
authoring_params: RwLock::new(AuthoringParams {
|
authoring_params: RwLock::new(AuthoringParams {
|
||||||
author: Address::zero(),
|
author: Address::zero(),
|
||||||
@ -230,23 +230,7 @@ impl MinerService for TestMinerService {
|
|||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_receipt(&self, _best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
fn pending_receipts(&self, _best_block: BlockNumber) -> Option<Vec<RichReceipt>> {
|
||||||
// Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested.
|
|
||||||
self.pending_receipts(0).unwrap().get(hash).map(|r|
|
|
||||||
RichReceipt {
|
|
||||||
transaction_hash: Default::default(),
|
|
||||||
transaction_index: Default::default(),
|
|
||||||
cumulative_gas_used: r.gas_used.clone(),
|
|
||||||
gas_used: r.gas_used.clone(),
|
|
||||||
contract_address: None,
|
|
||||||
logs: r.logs.clone(),
|
|
||||||
log_bloom: r.log_bloom,
|
|
||||||
outcome: r.outcome.clone(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pending_receipts(&self, _best_block: BlockNumber) -> Option<BTreeMap<H256, Receipt>> {
|
|
||||||
Some(self.pending_receipts.lock().clone())
|
Some(self.pending_receipts.lock().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::{TestBlockChainClient, Executed};
|
use ethcore::client::{TestBlockChainClient, Executed, TransactionId};
|
||||||
|
use ethcore::receipt::{LocalizedReceipt, TransactionOutcome};
|
||||||
use ethcore_logger::RotatingLogger;
|
use ethcore_logger::RotatingLogger;
|
||||||
use ethereum_types::{Address, U256, H256};
|
use ethereum_types::{Address, U256, H256};
|
||||||
use ethstore::ethkey::{Generator, Random};
|
use ethstore::ethkey::{Generator, Random};
|
||||||
@ -531,3 +532,34 @@ fn rpc_parity_call() {
|
|||||||
|
|
||||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_parity_block_receipts() {
|
||||||
|
let deps = Dependencies::new();
|
||||||
|
deps.client.receipts.write()
|
||||||
|
.insert(TransactionId::Hash(1.into()), LocalizedReceipt {
|
||||||
|
transaction_hash: 1.into(),
|
||||||
|
transaction_index: 0,
|
||||||
|
block_hash: 3.into(),
|
||||||
|
block_number: 0,
|
||||||
|
cumulative_gas_used: 21_000.into(),
|
||||||
|
gas_used: 21_000.into(),
|
||||||
|
contract_address: None,
|
||||||
|
logs: vec![],
|
||||||
|
log_bloom: 1.into(),
|
||||||
|
outcome: TransactionOutcome::Unknown,
|
||||||
|
to: None,
|
||||||
|
from: 9.into(),
|
||||||
|
});
|
||||||
|
let io = deps.default_client();
|
||||||
|
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "parity_getBlockReceipts",
|
||||||
|
"params": [],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","root":null,"status":null,"to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
@ -27,7 +27,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, ChainStatus,
|
OperationsInfo, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo, RichHeader,
|
AccountInfo, HwAccountInfo, RichHeader, Receipt,
|
||||||
};
|
};
|
||||||
|
|
||||||
build_rpc_trait! {
|
build_rpc_trait! {
|
||||||
@ -211,6 +211,12 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "parity_getBlockHeaderByNumber")]
|
#[rpc(name = "parity_getBlockHeaderByNumber")]
|
||||||
fn block_header(&self, Trailing<BlockNumber>) -> BoxFuture<RichHeader>;
|
fn block_header(&self, Trailing<BlockNumber>) -> BoxFuture<RichHeader>;
|
||||||
|
|
||||||
|
/// Get block receipts.
|
||||||
|
/// Allows you to fetch receipts from the entire block at once.
|
||||||
|
/// If no parameter is provided defaults to `latest`.
|
||||||
|
#[rpc(name = "parity_getBlockReceipts")]
|
||||||
|
fn block_receipts(&self, Trailing<BlockNumber>) -> BoxFuture<Vec<Receipt>>;
|
||||||
|
|
||||||
/// Get IPFS CIDv0 given protobuf encoded bytes.
|
/// Get IPFS CIDv0 given protobuf encoded bytes.
|
||||||
#[rpc(name = "parity_cidV0")]
|
#[rpc(name = "parity_cidV0")]
|
||||||
fn ipfs_cid(&self, Bytes) -> Result<String>;
|
fn ipfs_cid(&self, Bytes) -> Result<String>;
|
||||||
|
@ -54,6 +54,31 @@ impl BlockNumber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BlockNumber to BlockId conversion
|
||||||
|
///
|
||||||
|
/// NOTE use only for light clients.
|
||||||
|
pub trait LightBlockNumber {
|
||||||
|
/// Convert block number to block id.
|
||||||
|
fn to_block_id(self) -> BlockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightBlockNumber for BlockNumber {
|
||||||
|
fn to_block_id(self) -> 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 self {
|
||||||
|
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 Serialize for BlockNumber {
|
impl Serialize for BlockNumber {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -49,7 +49,7 @@ pub mod pubsub;
|
|||||||
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo};
|
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo};
|
||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
||||||
pub use self::block_number::{BlockNumber, block_number_to_id};
|
pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id};
|
||||||
pub use self::call_request::CallRequest;
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::confirmations::{
|
pub use self::confirmations::{
|
||||||
ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken,
|
ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken,
|
||||||
|
Loading…
Reference in New Issue
Block a user