Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

RPC: parity_getBlockReceipts #9527

Merged
merged 11 commits into from
Sep 25, 2018
2 changes: 1 addition & 1 deletion ethcore/light/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}

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) })
}

Expand Down
5 changes: 0 additions & 5 deletions ethcore/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,6 @@ pub trait BlockProvider {
.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.
/// Returns None if block does not exist.
fn transactions(&self, hash: &H256) -> Option<Vec<LocalizedTransaction>> {
Expand Down
92 changes: 54 additions & 38 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1785,26 +1785,49 @@ impl BlockChainClient for Client {
}

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();
self.transaction_address(id)
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
let transaction = chain.block_body(&address.block_hash)
.and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index));

let previous_receipts = (0..address.index + 1)
.map(|index| {
let mut address = address.clone();
address.index = index;
chain.transaction_receipt(&address)
})
.collect();
match (transaction, previous_receipts) {
(Some(transaction), Some(previous_receipts)) => {
Some(transaction_receipt(self.engine().machine(), transaction, previous_receipts))
},
_ => None,
}
}))
let number = chain.block_number(&hash)?;
let body = chain.block_body(&hash)?;
let mut receipts = chain.block_receipts(&hash)?.receipts;
receipts.truncate(address.index + 1);

let transaction = body.view().localized_transaction_at(&hash, number, address.index)?;
let receipt = receipts.pop()?;
let gas_used = receipts.last().map_or_else(|| 0.into(), |r| r.gas_used);
let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand these 3 lines. why do we pop a receipt and then take a gas_used from the one before it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to actually generate a LocalizedReceipt we need 3 things:

  1. Actual receipt
  2. Gas used by that transaction
  3. Number of logs generate prior to this transaction (to calculate log_index_in_block).

So the logic is as follows:

  1. Fetch all receipts from 0 to index (inclusive)
  2. Take the last one (receipts.pop(); point 1.)
  3. Take the previous to last one to calculate (point 2.) (receipts only store cumulative gas, so we calculate current.gas_used - previous.gas_used to figure out what current gas usage of transaction was)
  4. Use all previous receipts to calculate total number of logs. (point 3.)


let receipt = transaction_receipt(self.engine().machine(), transaction, receipt, gas_used, no_of_logs);
Some(receipt)
}

fn block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method could probably use a test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added for both block_receipt and transaction_receipt

let hash = self.block_hash(id)?;

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> {
Expand All @@ -1823,7 +1846,7 @@ impl BlockChainClient for Client {
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())
}

Expand Down Expand Up @@ -2378,16 +2401,14 @@ impl Drop for Client {

/// Returns `LocalizedReceipt` given `LocalizedTransaction`
/// 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 {
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");

fn transaction_receipt(
machine: &::machine::EthereumMachine,
mut tx: LocalizedTransaction,
receipt: Receipt,
prior_gas_used: U256,
prior_no_of_logs: usize,
) -> LocalizedReceipt {
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 block_hash = tx.block_hash;
let block_number = tx.block_number;
Expand Down Expand Up @@ -2416,7 +2437,7 @@ fn transaction_receipt(machine: &::machine::EthereumMachine, mut tx: LocalizedTr
transaction_hash: transaction_hash,
transaction_index: transaction_index,
transaction_log_index: i,
log_index: no_of_logs + i,
log_index: prior_no_of_logs + i,
}).collect(),
log_bloom: receipt.log_bloom,
outcome: receipt.outcome,
Expand Down Expand Up @@ -2507,20 +2528,15 @@ mod tests {
topics: vec![],
data: vec![],
}];
let receipts = vec![Receipt {
outcome: TransactionOutcome::StateRoot(state_root),
gas_used: 5.into(),
log_bloom: Default::default(),
logs: vec![logs[0].clone()],
}, Receipt {
let receipt = Receipt {
outcome: TransactionOutcome::StateRoot(state_root),
gas_used: gas_used,
log_bloom: Default::default(),
logs: logs.clone(),
}];
};

// when
let receipt = transaction_receipt(&machine, transaction, receipts);
let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1);

// then
assert_eq!(receipt, LocalizedReceipt {
Expand Down
6 changes: 5 additions & 1 deletion ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,10 @@ impl BlockChainClient for TestBlockChainClient {
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> {
match self.error_on_logs.read().as_ref() {
Some(id) => return Err(id.clone()),
Expand Down Expand Up @@ -786,7 +790,7 @@ impl BlockChainClient for TestBlockChainClient {
None
}

fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes> {
// starts with 'f' ?
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
let receipt = BlockReceipts::new(vec![Receipt::new(
Expand Down
5 changes: 4 additions & 1 deletion ethcore/src/client/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
/// Get transaction receipt with given hash.
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`.
/// See `BlockChain::tree_route`.
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
Expand All @@ -292,7 +295,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
fn state_data(&self, hash: &H256) -> Option<Bytes>;

/// 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.
fn queue_info(&self) -> BlockQueueInfo;
Expand Down
28 changes: 9 additions & 19 deletions ethcore/src/miner/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use executive::contract_address;
use header::{Header, BlockNumber};
use miner;
use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache};
use receipt::{Receipt, RichReceipt};
use receipt::RichReceipt;
use spec::Spec;
use state::State;
use ethkey::Password;
Expand Down Expand Up @@ -1039,19 +1039,17 @@ impl miner::MinerService for Miner {
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| {
let txs = pending.transactions();
txs.iter()
.map(|t| t.hash())
.position(|t| t == *hash)
.map(|index| {
let receipts = pending.receipts();
let receipts = pending.receipts();
pending.transactions()
.into_iter()
.enumerate()
.map(|(index, tx)| {
let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used };
let tx = &txs[index];
let receipt = &receipts[index];
RichReceipt {
transaction_hash: hash.clone(),
transaction_hash: tx.hash(),
transaction_index: index,
cumulative_gas_used: receipt.gas_used,
gas_used: receipt.gas_used - prev_gas,
Expand All @@ -1067,15 +1065,7 @@ impl miner::MinerService for Miner {
outcome: receipt.outcome.clone(),
}
})
}, best_block).and_then(|x| x)
}

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()
.collect()
}, best_block)
}

Expand Down
9 changes: 6 additions & 3 deletions ethcore/src/miner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use client::{
};
use error::Error;
use header::{BlockNumber, Header};
use receipt::{RichReceipt, Receipt};
use receipt::RichReceipt;
use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction};
use state::StateInfo;
use ethkey::Password;
Expand Down Expand Up @@ -95,10 +95,13 @@ pub trait MinerService : Send + Sync {
// 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.
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.
fn pending_state(&self, latest_block_number: BlockNumber) -> Option<Self::State>;
Expand Down
4 changes: 2 additions & 2 deletions ethcore/src/views/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ impl<'a> BodyView<'a> {
/// ```
/// #[macro_use]
/// extern crate ethcore;
///
///
/// use ethcore::views::{BodyView};
///
///
/// fn main() {
/// let bytes : &[u8] = &[];
/// let body_view = view!(BodyView, bytes);
Expand Down
2 changes: 1 addition & 1 deletion ethcore/sync/src/chain/supplier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl SyncSupplier {
let mut added_receipts = 0usize;
let mut data = Bytes::new();
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);
added_receipts += receipts_bytes.len();
added_headers += 1;
Expand Down
24 changes: 12 additions & 12 deletions rpc/src/v1/impls/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ use ethcore::account_provider::AccountProvider;
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
use ethcore::filter::Filter as EthcoreFilter;
use ethcore::header::{BlockNumber as EthBlockNumber};
use ethcore::log_entry::LogEntry;
use ethcore::miner::{self, MinerService};
use ethcore::snapshot::SnapshotService;
use ethcore::encoded;
Expand Down Expand Up @@ -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 {
let receipts = miner.pending_receipts(best_block).unwrap_or_default();

let pending_logs = receipts.into_iter()
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
.collect::<Vec<(H256, LogEntry)>>();

pending_logs.into_iter()
receipts.into_iter()
.flat_map(|r| {
let hash = r.transaction_hash;
r.logs.into_iter().map(move |l| (hash.clone(), l))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move and clone() are probably redundant, cause hash implements Copy

})
.filter(|pair| filter.matches(&pair.1))
.map(|pair| {
let mut log = Log::from(pair.1);
Expand Down Expand Up @@ -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>> {
let best_block = self.client.chain_info().best_block_number;
let hash: H256 = hash.into();

match (self.miner.pending_receipt(best_block, &hash), self.options.allow_pending_receipt_query) {
(Some(receipt), true) => Box::new(future::ok(Some(receipt.into()))),
_ => {
let receipt = self.client.transaction_receipt(TransactionId::Hash(hash));
Box::new(future::ok(receipt.map(Into::into)))
if self.options.allow_pending_receipt_query {
let best_block = self.client.chain_info().best_block_number;
if let Some(receipt) = self.miner.pending_receipt(best_block, &hash) {
return Box::new(future::ok(Some(receipt.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>> {
Expand Down
15 changes: 14 additions & 1 deletion rpc/src/v1/impls/light/parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use v1::types::{
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
OperationsInfo, ChainStatus,
AccountInfo, HwAccountInfo, Header, RichHeader,
AccountInfo, HwAccountInfo, Header, RichHeader, Receipt,
};
use Host;

Expand Down Expand Up @@ -415,6 +415,19 @@ impl Parity for ParityClient {
Box::new(self.fetcher().header(id).and_then(from_encoded))
}

fn block_receipts(&self, number: Trailing<BlockNumber>) -> BoxFuture<Vec<Receipt>> {
// 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`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant extra spaces

let id = match number.unwrap_or_default() {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no Pending state, I'd say either return an empty vector or keep the deprecation log from num_to_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> {
ipfs::cid(content)
}
Expand Down
Loading