mirror of https://github.com/hyperledger/besu
[PAN-3230] Merge BlockchainQueries classes (#101)
We had two mostly idenitcal classes used for GraphQL and JsonRPC/WS. This PR merges that to one class. * Move from org.hyperledger.besu.ethereum.api.json.internal.queries to org.hyperledger.besu.ethereum.api.query * Add one method from the GraphQL version (generateLogWithMetadataForTransaction) * Remove graphql version and point graphql to the shared version. Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>pull/107/head
parent
b926999af2
commit
1ca8a94af6
@ -1,325 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.graphql.internal; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.BlockWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.LogWithMetadata; |
||||
import org.hyperledger.besu.ethereum.api.LogsQuery; |
||||
import org.hyperledger.besu.ethereum.api.TransactionWithMetadata; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.chain.TransactionLocation; |
||||
import org.hyperledger.besu.ethereum.core.Block; |
||||
import org.hyperledger.besu.ethereum.core.BlockBody; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||
import org.hyperledger.besu.ethereum.core.WorldState; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
|
||||
public class BlockchainQuery { |
||||
|
||||
private final WorldStateArchive worldStateArchive; |
||||
private final Blockchain blockchain; |
||||
|
||||
public BlockchainQuery(final Blockchain blockchain, final WorldStateArchive worldStateArchive) { |
||||
this.blockchain = blockchain; |
||||
this.worldStateArchive = worldStateArchive; |
||||
} |
||||
|
||||
public Blockchain getBlockchain() { |
||||
return blockchain; |
||||
} |
||||
|
||||
public WorldStateArchive getWorldStateArchive() { |
||||
return worldStateArchive; |
||||
} |
||||
|
||||
/** |
||||
* Returns the ommer at the given index for the referenced block. |
||||
* |
||||
* @param blockHeaderHash The hash of the block to be queried. |
||||
* @param index The index of the ommer in the blocks ommers list. |
||||
* @return The ommer at the given index belonging to the referenced block. |
||||
*/ |
||||
public Optional<BlockHeader> getOmmer(final Hash blockHeaderHash, final int index) { |
||||
return blockchain.getBlockBody(blockHeaderHash).map(blockBody -> getOmmer(blockBody, index)); |
||||
} |
||||
|
||||
private BlockHeader getOmmer(final BlockBody blockBody, final int index) { |
||||
final List<BlockHeader> ommers = blockBody.getOmmers(); |
||||
if (ommers.size() > index) { |
||||
return ommers.get(index); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Given a block hash, returns the associated block augmented with metadata. |
||||
* |
||||
* @param blockHeaderHash The hash of the target block's header. |
||||
* @return The referenced block. |
||||
*/ |
||||
public Optional<BlockWithMetadata<TransactionWithMetadata, Hash>> blockByHash( |
||||
final Hash blockHeaderHash) { |
||||
return blockchain |
||||
.getBlockHeader(blockHeaderHash) |
||||
.flatMap( |
||||
header -> |
||||
blockchain |
||||
.getBlockBody(blockHeaderHash) |
||||
.flatMap( |
||||
body -> |
||||
blockchain |
||||
.getTotalDifficultyByHash(blockHeaderHash) |
||||
.map( |
||||
(td) -> { |
||||
final List<Transaction> txs = body.getTransactions(); |
||||
final List<TransactionWithMetadata> formattedTxs = |
||||
formatTransactions( |
||||
txs, header.getNumber(), blockHeaderHash); |
||||
final List<Hash> ommers = |
||||
body.getOmmers().stream() |
||||
.map(BlockHeader::getHash) |
||||
.collect(Collectors.toList()); |
||||
final int size = new Block(header, body).calculateSize(); |
||||
return new BlockWithMetadata<>( |
||||
header, formattedTxs, ommers, td, size); |
||||
}))); |
||||
} |
||||
|
||||
/** |
||||
* Given a block number, returns the associated block augmented with metadata. |
||||
* |
||||
* @param number The height of the target block. |
||||
* @return The referenced block. |
||||
*/ |
||||
public Optional<BlockWithMetadata<TransactionWithMetadata, Hash>> blockByNumber( |
||||
final long number) { |
||||
return blockchain.getBlockHashByNumber(number).flatMap(this::blockByHash); |
||||
} |
||||
|
||||
/** |
||||
* Returns the latest block augmented with metadata. |
||||
* |
||||
* @return The latest block. |
||||
*/ |
||||
public Optional<BlockWithMetadata<TransactionWithMetadata, Hash>> latestBlock() { |
||||
return this.blockByHash(blockchain.getChainHeadHash()); |
||||
} |
||||
|
||||
/** |
||||
* Given a transaction hash, returns the associated transaction. |
||||
* |
||||
* @param transactionHash The hash of the target transaction. |
||||
* @return The transaction associated with the given hash. |
||||
*/ |
||||
public Optional<TransactionWithMetadata> transactionByHash(final Hash transactionHash) { |
||||
final Optional<TransactionLocation> maybeLocation = |
||||
blockchain.getTransactionLocation(transactionHash); |
||||
if (!maybeLocation.isPresent()) { |
||||
return Optional.empty(); |
||||
} |
||||
final TransactionLocation loc = maybeLocation.get(); |
||||
final Hash blockHash = loc.getBlockHash(); |
||||
final BlockHeader header = blockchain.getBlockHeader(blockHash).get(); |
||||
final Transaction transaction = blockchain.getTransactionByHash(transactionHash).get(); |
||||
return Optional.of( |
||||
new TransactionWithMetadata( |
||||
transaction, header.getNumber(), blockHash, loc.getTransactionIndex())); |
||||
} |
||||
|
||||
/** |
||||
* Returns the transaction receipt associated with the given transaction hash. |
||||
* |
||||
* @param transactionHash The hash of the transaction that corresponds to the receipt to retrieve. |
||||
* @return The transaction receipt associated with the referenced transaction. |
||||
*/ |
||||
public Optional<TransactionReceiptWithMetadata> transactionReceiptByTransactionHash( |
||||
final Hash transactionHash) { |
||||
final Optional<TransactionLocation> maybeLocation = |
||||
blockchain.getTransactionLocation(transactionHash); |
||||
if (!maybeLocation.isPresent()) { |
||||
return Optional.empty(); |
||||
} |
||||
final TransactionLocation location = maybeLocation.get(); |
||||
final BlockBody blockBody = blockchain.getBlockBody(location.getBlockHash()).get(); |
||||
final Transaction transaction = blockBody.getTransactions().get(location.getTransactionIndex()); |
||||
|
||||
final Hash blockhash = location.getBlockHash(); |
||||
final BlockHeader header = blockchain.getBlockHeader(blockhash).get(); |
||||
final List<TransactionReceipt> transactionReceipts = blockchain.getTxReceipts(blockhash).get(); |
||||
final TransactionReceipt transactionReceipt = |
||||
transactionReceipts.get(location.getTransactionIndex()); |
||||
|
||||
long gasUsed = transactionReceipt.getCumulativeGasUsed(); |
||||
if (location.getTransactionIndex() > 0) { |
||||
gasUsed = |
||||
gasUsed |
||||
- transactionReceipts.get(location.getTransactionIndex() - 1).getCumulativeGasUsed(); |
||||
} |
||||
|
||||
return Optional.of( |
||||
new TransactionReceiptWithMetadata( |
||||
transactionReceipt, |
||||
transaction, |
||||
transactionHash, |
||||
location.getTransactionIndex(), |
||||
gasUsed, |
||||
blockhash, |
||||
header.getNumber())); |
||||
} |
||||
|
||||
/** |
||||
* Returns the world state for the corresponding block number |
||||
* |
||||
* @param blockNumber the block number |
||||
* @return the world state at the block number |
||||
*/ |
||||
public Optional<WorldState> getWorldState(final long blockNumber) { |
||||
final Optional<BlockHeader> header = blockchain.getBlockHeader(blockNumber); |
||||
return header |
||||
.map(BlockHeader::getStateRoot) |
||||
.flatMap(worldStateArchive::getMutable) |
||||
.map(mws -> mws); // to satisfy typing
|
||||
} |
||||
|
||||
private List<TransactionWithMetadata> formatTransactions( |
||||
final List<Transaction> txs, final long blockNumber, final Hash blockHash) { |
||||
final int count = txs.size(); |
||||
final List<TransactionWithMetadata> result = new ArrayList<>(count); |
||||
for (int i = 0; i < count; i++) { |
||||
result.add(new TransactionWithMetadata(txs.get(i), blockNumber, blockHash, i)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Retrieve logs from the range of blocks with optional filtering based on logger address and log |
||||
* topics. |
||||
* |
||||
* @param fromBlockNumber The block number defining the first block in the search range |
||||
* (inclusive). |
||||
* @param toBlockNumber The block number defining the last block in the search range (inclusive). |
||||
* @param query Constraints on required topics by topic index. For a given index if the set of |
||||
* topics is non-empty, the topic at this index must match one of the values in the set. |
||||
* @return The set of logs matching the given constraints. |
||||
*/ |
||||
public List<LogWithMetadata> matchingLogs( |
||||
final long fromBlockNumber, final long toBlockNumber, final LogsQuery query) { |
||||
if (fromBlockNumber > toBlockNumber || toBlockNumber > blockchain.getChainHeadBlockNumber()) { |
||||
return Lists.newArrayList(); |
||||
} |
||||
List<LogWithMetadata> matchingLogs = Lists.newArrayList(); |
||||
for (long blockNumber = fromBlockNumber; blockNumber <= toBlockNumber; blockNumber++) { |
||||
final Hash blockhash = blockchain.getBlockHashByNumber(blockNumber).get(); |
||||
final boolean logHasBeenRemoved = !blockchain.blockIsOnCanonicalChain(blockhash); |
||||
final List<TransactionReceipt> receipts = blockchain.getTxReceipts(blockhash).get(); |
||||
final List<Transaction> transaction = |
||||
blockchain.getBlockBody(blockhash).get().getTransactions(); |
||||
matchingLogs = |
||||
generateLogWithMetadata( |
||||
receipts, |
||||
blockNumber, |
||||
query, |
||||
blockhash, |
||||
matchingLogs, |
||||
transaction, |
||||
logHasBeenRemoved); |
||||
} |
||||
return matchingLogs; |
||||
} |
||||
|
||||
public List<LogWithMetadata> matchingLogs(final Hash blockhash, final LogsQuery query) { |
||||
final List<LogWithMetadata> matchingLogs = Lists.newArrayList(); |
||||
final Optional<BlockHeader> blockHeader = blockchain.getBlockHeader(blockhash); |
||||
if (!blockHeader.isPresent()) { |
||||
return matchingLogs; |
||||
} |
||||
final List<TransactionReceipt> receipts = blockchain.getTxReceipts(blockhash).get(); |
||||
final List<Transaction> transaction = |
||||
blockchain.getBlockBody(blockhash).get().getTransactions(); |
||||
final long number = blockHeader.get().getNumber(); |
||||
final boolean logHasBeenRemoved = !blockchain.blockIsOnCanonicalChain(blockhash); |
||||
return generateLogWithMetadata( |
||||
receipts, number, query, blockhash, matchingLogs, transaction, logHasBeenRemoved); |
||||
} |
||||
|
||||
private List<LogWithMetadata> generateLogWithMetadata( |
||||
final List<TransactionReceipt> receipts, |
||||
final long number, |
||||
final LogsQuery query, |
||||
final Hash blockhash, |
||||
final List<LogWithMetadata> matchingLogs, |
||||
final List<Transaction> transaction, |
||||
final boolean removed) { |
||||
for (int transactionIndex = 0; transactionIndex < receipts.size(); ++transactionIndex) { |
||||
final TransactionReceipt receipt = receipts.get(transactionIndex); |
||||
for (int logIndex = 0; logIndex < receipt.getLogs().size(); ++logIndex) { |
||||
if (query.matches(receipt.getLogs().get(logIndex))) { |
||||
final LogWithMetadata logWithMetaData = |
||||
new LogWithMetadata( |
||||
logIndex, |
||||
number, |
||||
blockhash, |
||||
transaction.get(transactionIndex).hash(), |
||||
transactionIndex, |
||||
receipts.get(transactionIndex).getLogs().get(logIndex).getLogger(), |
||||
receipts.get(transactionIndex).getLogs().get(logIndex).getData(), |
||||
receipts.get(transactionIndex).getLogs().get(logIndex).getTopics(), |
||||
removed); |
||||
matchingLogs.add(logWithMetaData); |
||||
} |
||||
} |
||||
} |
||||
return matchingLogs; |
||||
} |
||||
|
||||
public static List<LogWithMetadata> generateLogWithMetadataForTransaction( |
||||
final TransactionReceipt receipt, |
||||
final long number, |
||||
final Hash blockhash, |
||||
final Hash transactionHash, |
||||
final int transactionIndex, |
||||
final boolean removed) { |
||||
|
||||
final List<LogWithMetadata> logs = new ArrayList<>(); |
||||
for (int logIndex = 0; logIndex < receipt.getLogs().size(); ++logIndex) { |
||||
|
||||
final LogWithMetadata logWithMetaData = |
||||
new LogWithMetadata( |
||||
logIndex, |
||||
number, |
||||
blockhash, |
||||
transactionHash, |
||||
transactionIndex, |
||||
receipt.getLogs().get(logIndex).getLogger(), |
||||
receipt.getLogs().get(logIndex).getData(), |
||||
receipt.getLogs().get(logIndex).getTopics(), |
||||
removed); |
||||
logs.add(logWithMetaData); |
||||
} |
||||
|
||||
return logs; |
||||
} |
||||
} |
@ -1,76 +0,0 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.graphql.internal; |
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
||||
|
||||
public class TransactionReceiptWithMetadata { |
||||
private final TransactionReceipt receipt; |
||||
private final Hash transactionHash; |
||||
private final int transactionIndex; |
||||
private final long gasUsed; |
||||
private final long blockNumber; |
||||
private final Hash blockHash; |
||||
private final Transaction transaction; |
||||
|
||||
TransactionReceiptWithMetadata( |
||||
final TransactionReceipt receipt, |
||||
final Transaction transaction, |
||||
final Hash transactionHash, |
||||
final int transactionIndex, |
||||
final long gasUsed, |
||||
final Hash blockHash, |
||||
final long blockNumber) { |
||||
this.receipt = receipt; |
||||
this.transactionHash = transactionHash; |
||||
this.transactionIndex = transactionIndex; |
||||
this.gasUsed = gasUsed; |
||||
this.blockHash = blockHash; |
||||
this.blockNumber = blockNumber; |
||||
this.transaction = transaction; |
||||
} |
||||
|
||||
public TransactionReceipt getReceipt() { |
||||
return receipt; |
||||
} |
||||
|
||||
public Hash getTransactionHash() { |
||||
return transactionHash; |
||||
} |
||||
|
||||
public Transaction getTransaction() { |
||||
return transaction; |
||||
} |
||||
|
||||
public int getTransactionIndex() { |
||||
return transactionIndex; |
||||
} |
||||
|
||||
public Hash getBlockHash() { |
||||
return blockHash; |
||||
} |
||||
|
||||
public long getBlockNumber() { |
||||
return blockNumber; |
||||
} |
||||
|
||||
// The gas used for this particular transaction (as opposed to cumulativeGas which is included in
|
||||
// the receipt itself)
|
||||
public long getGasUsed() { |
||||
return gasUsed; |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue