Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve all transaction receipts for a block in one request #6646

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,11 +15,14 @@
package org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.graphql.GraphQLContextType;
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -75,11 +78,19 @@ public Optional<UncleBlockAdapter> getOmmerAt(final DataFetchingEnvironment envi
return Optional.empty();
}

public List<TransactionAdapter> getTransactions() {
public List<TransactionAdapter> getTransactions(final DataFetchingEnvironment environment) {
final BlockchainQueries query = getBlockchainQueries(environment);
final Hash hash = blockWithMetaData.getHeader().getHash();
final ProtocolSchedule protocolSchedule =
environment.getGraphQlContext().get(GraphQLContextType.PROTOCOL_SCHEDULE);

final List<TransactionWithMetadata> trans = blockWithMetaData.getTransactions();
final List<TransactionReceiptWithMetadata> transReceipts =
query.transactionReceiptsByBlockHash(hash, protocolSchedule).get();

final List<TransactionAdapter> results = new ArrayList<>();
for (final TransactionWithMetadata tran : trans) {
results.add(new TransactionAdapter(tran));
for (int i = 0; i < trans.size(); i++) {
results.add(new TransactionAdapter(trans.get(i), transReceipts.get(i)));
}
return results;
}
Expand Down
Expand Up @@ -53,7 +53,7 @@ public List<TransactionAdapter> getTransactions() {
return transactionPool.getPendingTransactions().stream()
.map(PendingTransaction::getTransaction)
.map(TransactionWithMetadata::new)
.map(TransactionAdapter::new)
.map(tx -> new TransactionAdapter(tx, null))
.toList();
}

Expand Down
Expand Up @@ -33,6 +33,7 @@
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import graphql.schema.DataFetchingEnvironment;
import org.apache.tuweni.bytes.Bytes;
Expand All @@ -46,6 +47,13 @@ public TransactionAdapter(final @Nonnull TransactionWithMetadata transactionWith
this.transactionWithMetadata = transactionWithMetadata;
}

public TransactionAdapter(
final @Nonnull TransactionWithMetadata transactionWithMetadata,
final @Nullable TransactionReceiptWithMetadata transactionReceiptWithMetadata) {
this.transactionWithMetadata = transactionWithMetadata;
this.transactionReceiptWithMetadata = Optional.ofNullable(transactionReceiptWithMetadata);
}

private Optional<TransactionReceiptWithMetadata> getReceipt(
final DataFetchingEnvironment environment) {
if (transactionReceiptWithMetadata == null) {
Expand Down
Expand Up @@ -24,13 +24,11 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.TransactionReceiptStatusResult;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.TransactionReceiptType;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.base.Suppliers;
Expand Down Expand Up @@ -62,34 +60,21 @@ protected Object resultByBlockHash(final JsonRpcRequestContext request, final Ha
}

/*
* For a given transaction, get its receipt and if it exists, wrap in a transaction receipt of the correct type
* For a given block, get receipts of transactions in the block and if they exist, wrap in transaction receipts of the correct type
*/
private Optional<TransactionReceiptResult> txReceipt(final TransactionWithMetadata tx) {
Optional<TransactionReceiptWithMetadata> receipt =
blockchainQueries
.get()
.transactionReceiptByTransactionHash(tx.getTransaction().getHash(), protocolSchedule);
if (receipt.isPresent()) {
if (receipt.get().getReceipt().getTransactionReceiptType() == TransactionReceiptType.ROOT) {
return Optional.of(new TransactionReceiptRootResult(receipt.get()));
} else {
return Optional.of(new TransactionReceiptStatusResult(receipt.get()));
}
}
return Optional.empty();
}

private BlockReceiptsResult getBlockReceiptsResult(final Hash blockHash) {
final List<TransactionReceiptResult> receiptList =
blockchainQueries
.get()
.blockByHash(blockHash)
.transactionReceiptsByBlockHash(blockHash, protocolSchedule)
.orElse(new ArrayList<TransactionReceiptWithMetadata>())
.stream()
.map(
block ->
block.getTransactions().stream()
.map(tx -> txReceipt(tx).get())
.collect(Collectors.toList()))
.orElse(new ArrayList<>());
receipt ->
receipt.getReceipt().getTransactionReceiptType() == TransactionReceiptType.ROOT
? new TransactionReceiptRootResult(receipt)
: new TransactionReceiptStatusResult(receipt))
.collect(Collectors.toList());

return new BlockReceiptsResult(receiptList);
}
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.MinerDataResult.UncleRewardResult;
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionReceiptWithMetadata;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
Expand Down Expand Up @@ -82,19 +83,16 @@ public static MinerDataResult createMinerDataResult(
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
final Wei staticBlockReward = protocolSpec.getBlockReward();
final Wei transactionFee =
block.getTransactions().stream()
blockchainQueries
.transactionReceiptsByBlockHash(blockHeader.getHash(), protocolSchedule)
.orElse(new ArrayList<TransactionReceiptWithMetadata>())
.stream()
.map(
t ->
blockchainQueries
.transactionReceiptByTransactionHash(
t.getTransaction().getHash(), protocolSchedule)
.map(
receipt ->
receipt
.getTransaction()
.getEffectiveGasPrice(receipt.getBaseFee())
.multiply(receipt.getGasUsed()))
.orElse(Wei.ZERO))
receipt ->
receipt
.getTransaction()
.getEffectivePriorityFeePerGas(receipt.getBaseFee())
.multiply(receipt.getGasUsed()))
.reduce(Wei.ZERO, BaseUInt256Value::add);
final Wei uncleInclusionReward =
staticBlockReward.multiply(block.getOmmers().size()).divide(32);
Expand Down
Expand Up @@ -599,6 +599,63 @@ public Optional<TransactionLocation> transactionLocationByHash(final Hash transa
return blockchain.getTransactionLocation(transactionHash);
}

/**
* Returns the transaction receipts associated with the given block hash.
*
* @param blockHash The hash of the block that corresponds to the receipts to retrieve.
* @return The transaction receipts associated with the referenced block.
*/
public Optional<List<TransactionReceiptWithMetadata>> transactionReceiptsByBlockHash(
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to see a test for this method

final Hash blockHash, final ProtocolSchedule protocolSchedule) {
final Optional<Block> block = blockchain.getBlockByHash(blockHash);
if (block.isEmpty()) {
return Optional.empty();
}
final BlockHeader header = block.get().getHeader();
final List<Transaction> transactions = block.get().getBody().getTransactions();

final List<TransactionReceipt> transactionReceipts =
blockchain.getTxReceipts(blockHash).orElseThrow();

long cumulativeGasUsedUntilTx = 0;
int logIndexOffset = 0;

List<TransactionReceiptWithMetadata> receiptsResult =
new ArrayList<TransactionReceiptWithMetadata>(transactions.size());

for (int transactionIndex = 0; transactionIndex < transactions.size(); transactionIndex++) {
final Transaction transaction = transactions.get(transactionIndex);
final TransactionReceipt transactionReceipt = transactionReceipts.get(transactionIndex);
final Hash transactionHash = transaction.getHash();

long gasUsed = transactionReceipt.getCumulativeGasUsed() - cumulativeGasUsedUntilTx;

Optional<Long> maybeBlobGasUsed =
getBlobGasUsed(transaction, protocolSchedule.getByBlockHeader(header));

Optional<Wei> maybeBlobGasPrice =
getBlobGasPrice(transaction, header, protocolSchedule.getByBlockHeader(header));

receiptsResult.add(
TransactionReceiptWithMetadata.create(
transactionReceipt,
transaction,
transactionHash,
transactionIndex,
gasUsed,
header.getBaseFee(),
blockHash,
header.getNumber(),
maybeBlobGasUsed,
maybeBlobGasPrice,
logIndexOffset));

cumulativeGasUsedUntilTx = transactionReceipt.getCumulativeGasUsed();
logIndexOffset += transactionReceipt.getLogsList().size();
}
return Optional.of(receiptsResult);
}

/**
* Returns the transaction receipt associated with the given transaction hash.
*
Expand Down