From 061e24a5f409169ea78196a3f8472ffb0afc834b Mon Sep 17 00:00:00 2001 From: David Mechler Date: Thu, 2 Apr 2020 13:41:59 -0400 Subject: [PATCH] Added block add event. (#637) Added block reorg event. Added revert reason to both events. Added TransactionReceipt. Signed-off-by: David Mechler --- .../besu/services/BesuEventsImpl.java | 62 ++++++++- .../besu/services/BesuEventsImplTest.java | 127 ++++++++++++++++++ .../IbftMiningCoordinatorTest.java | 3 +- ...AutoTransactionLogBloomCachingService.java | 4 +- .../filter/FilterManagerLogFilterTest.java | 5 +- .../internal/filter/FilterManagerTest.java | 3 +- .../AbstractMiningCoordinatorTest.java | 24 +++- .../besu/ethereum/chain/BlockAddedEvent.java | 16 ++- .../ethereum/chain/ChainReorgObserver.java | 4 +- .../ethereum/chain/DefaultBlockchain.java | 10 +- .../ethereum/core/TransactionReceipt.java | 7 +- .../eth/sync/TrailingPeerLimiterTest.java | 2 + plugin-api/build.gradle | 2 +- .../besu/plugin/data/AddedBlockContext.java | 42 ++++++ .../besu/plugin/data/TransactionReceipt.java | 58 ++++++++ .../besu/plugin/services/BesuEvents.java | 53 ++++++++ 16 files changed, 397 insertions(+), 25 deletions(-) create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/data/AddedBlockContext.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionReceipt.java diff --git a/besu/src/main/java/org/hyperledger/besu/services/BesuEventsImpl.java b/besu/src/main/java/org/hyperledger/besu/services/BesuEventsImpl.java index ee0bb5dc85..2c3f6a2144 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/BesuEventsImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/BesuEventsImpl.java @@ -22,9 +22,11 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.LogTopic; import org.hyperledger.besu.ethereum.core.LogWithMetadata; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; +import org.hyperledger.besu.plugin.data.AddedBlockContext; import org.hyperledger.besu.plugin.data.Address; import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.data.PropagatedBlockContext; @@ -66,6 +68,38 @@ public class BesuEventsImpl implements BesuEvents { blockBroadcaster.unsubscribePropagateNewBlocks(listenerIdentifier); } + @Override + public long addBlockAddedListener(final BlockAddedListener listener) { + return blockchain.observeBlockAdded( + (event, chain) -> + listener.onBlockAdded( + blockAddedContext( + event.getBlock()::getHeader, + event.getBlock()::getBody, + event::getTransactionReceipts))); + } + + @Override + public void removeBlockAddedListener(final long listenerIdentifier) { + blockchain.removeObserver(listenerIdentifier); + } + + @Override + public long addBlockReorgListener(final BlockReorgListener listener) { + return blockchain.observeChainReorg( + (blockWithReceipts, chain) -> + listener.onBlockReorg( + blockAddedContext( + blockWithReceipts::getHeader, + blockWithReceipts.getBlock()::getBody, + blockWithReceipts::getReceipts))); + } + + @Override + public void removeBlockReorgListener(final long listenerIdentifier) { + blockchain.removeObserver(listenerIdentifier); + } + @Override public long addTransactionAddedListener(final TransactionAddedListener listener) { return transactionPool.subscribePendingTransactions(listener::onTransactionAdded); @@ -109,11 +143,7 @@ public class BesuEventsImpl implements BesuEvents { .collect(toUnmodifiableList()); final List> besuTopics = topics.stream() - .map( - subList -> - subList.stream() - .map(bytes -> LogTopic.wrap(bytes)) - .collect(toUnmodifiableList())) + .map(subList -> subList.stream().map(LogTopic::wrap).collect(toUnmodifiableList())) .collect(toUnmodifiableList()); final LogsQuery logsQuery = new LogsQuery(besuAddresses, besuTopics); @@ -152,4 +182,26 @@ public class BesuEventsImpl implements BesuEvents { } }; } + + private static AddedBlockContext blockAddedContext( + final Supplier blockHeaderSupplier, + final Supplier blockBodySupplier, + final Supplier> transactionReceiptsSupplier) { + return new AddedBlockContext() { + @Override + public BlockHeader getBlockHeader() { + return blockHeaderSupplier.get(); + } + + @Override + public BlockBody getBlockBody() { + return blockBodySupplier.get(); + } + + @Override + public List getTransactionReceipts() { + return transactionReceiptsSupplier.get(); + } + }; + } } diff --git a/besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java b/besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java index d78e120049..589249bcaf 100644 --- a/besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java +++ b/besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.WorldState; @@ -50,6 +51,7 @@ import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.data.AddedBlockContext; import org.hyperledger.besu.plugin.data.LogWithMetadata; import org.hyperledger.besu.plugin.data.PropagatedBlockContext; import org.hyperledger.besu.plugin.data.SyncStatus; @@ -228,6 +230,131 @@ public class BesuEventsImplTest { serviceImpl.removeBlockPropagatedListener(5L); } + @Test + public void addedBlockEventFiresAfterSubscribe() { + final AtomicReference result = new AtomicReference<>(); + serviceImpl.addBlockAddedListener(result::set); + assertThat(result.get()).isNull(); + + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash())); + List transactionReceipts = gen.receipts(block); + blockchain.appendBlock(block, transactionReceipts); + assertThat(result.get()).isNotNull(); + assertThat(result.get().getBlockHeader()).isEqualTo(block.getHeader()); + assertThat(result.get().getTransactionReceipts()).isEqualTo(transactionReceipts); + } + + @Test + public void addedBlockEventDoesNotFireAfterUnsubscribe() { + final AtomicReference result = new AtomicReference<>(); + final long id = serviceImpl.addBlockAddedListener(result::set); + assertThat(result.get()).isNull(); + + serviceImpl.removeBlockAddedListener(id); + result.set(null); + + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash())); + blockchain.appendBlock(block, gen.receipts(block)); + assertThat(result.get()).isNull(); + } + + @Test + public void additionWithoutSubscriptionsCompletes() { + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash())); + blockchain.appendBlock(block, gen.receipts(block)); + } + + @Test + public void addedBlockEventUselessUnsubscribesCompletes() { + serviceImpl.removeBlockAddedListener(5); + serviceImpl.removeBlockAddedListener(5L); + } + + @Test + public void reorgedBlockEventFiresAfterSubscribe() { + final AtomicReference result = new AtomicReference<>(); + serviceImpl.addBlockReorgListener(result::set); + assertThat(result.get()).isNull(); + + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1)); + blockchain.appendBlock(block, gen.receipts(block)); + assertThat(result.get()).isNull(); + + final var reorgBlock = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 2)); + List transactionReceipts = gen.receipts(reorgBlock); + blockchain.appendBlock(reorgBlock, transactionReceipts); + assertThat(result.get()).isNotNull(); + assertThat(result.get().getBlockHeader()).isEqualTo(reorgBlock.getHeader()); + assertThat(result.get().getTransactionReceipts()).isEqualTo(transactionReceipts); + } + + @Test + public void reorgedBlockEventDoesNotFireAfterUnsubscribe() { + final AtomicReference result = new AtomicReference<>(); + final long id = serviceImpl.addBlockReorgListener(result::set); + assertThat(result.get()).isNull(); + + serviceImpl.removeBlockReorgListener(id); + result.set(null); + + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1)); + blockchain.appendBlock(block, gen.receipts(block)); + assertThat(result.get()).isNull(); + + final var reorgBlock = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1)); + blockchain.appendBlock(reorgBlock, gen.receipts(reorgBlock)); + assertThat(result.get()).isNull(); + } + + @Test + public void reorgWithoutSubscriptionsCompletes() { + final var block = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1)); + blockchain.appendBlock(block, gen.receipts(block)); + + final var reorgBlock = + gen.block( + new BlockDataGenerator.BlockOptions() + .setParentHash(blockchain.getGenesisBlock().getHash()) + .setBlockNumber(blockchain.getGenesisBlock().getHeader().getNumber() + 1)); + List transactionReceipts = gen.receipts(reorgBlock); + blockchain.appendBlock(reorgBlock, transactionReceipts); + } + + @Test + public void reorgedBlockEventUselessUnsubscribesCompletes() { + serviceImpl.removeBlockReorgListener(5); + serviceImpl.removeBlockReorgListener(5L); + } + @Test public void transactionAddedEventFiresAfterSubscribe() { final AtomicReference result = new AtomicReference<>(); diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/IbftMiningCoordinatorTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/IbftMiningCoordinatorTest.java index f23fde1ab4..f5f0b382da 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/IbftMiningCoordinatorTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/IbftMiningCoordinatorTest.java @@ -103,7 +103,8 @@ public class IbftMiningCoordinatorTest { @Test public void addsNewChainHeadEventWhenNewCanonicalHeadBlockEventReceived() throws Exception { BlockAddedEvent headAdvancement = - BlockAddedEvent.createForHeadAdvancement(block, Collections.emptyList()); + BlockAddedEvent.createForHeadAdvancement( + block, Collections.emptyList(), Collections.emptyList()); ibftMiningCoordinator.onBlockAdded(headAdvancement, blockChain); assertThat(eventQueue.size()).isEqualTo(1); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java index a500720a32..047ae2eed8 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java @@ -58,9 +58,9 @@ public class AutoTransactionLogBloomCachingService { chainReorgSubscriptionId = OptionalLong.of( blockchain.observeChainReorg( - (header, __) -> + (blockWithReceipts, __) -> transactionLogBloomCacher.cacheLogsBloomForBlockHeader( - header, Optional.empty(), true))); + blockWithReceipts.getHeader(), Optional.empty(), true))); transactionLogBloomCacher .getScheduler() diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerLogFilterTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerLogFilterTest.java index 23387bba51..0791357d2c 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerLogFilterTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerLogFilterTest.java @@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.LogWithMetadata; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -149,7 +150,9 @@ public class FilterManagerLogFilterTest { final Block block = gen.block(); filterManager.recordBlockEvent( BlockAddedEvent.createForHeadAdvancement( - block, LogWithMetadata.generate(block, gen.receipts(block), false)), + block, + LogWithMetadata.generate(block, gen.receipts(block), false), + Collections.emptyList()), blockchainQueries.getBlockchain()); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerTest.java index 31e12ee6fd..745c05529d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/filter/FilterManagerTest.java @@ -233,7 +233,8 @@ public class FilterManagerTest { new BlockDataGenerator.BlockOptions().setBlockNumber(blockNumber).setParentHash(parentHash); currentBlock = blockGenerator.block(options); filterManager.recordBlockEvent( - BlockAddedEvent.createForHeadAdvancement(currentBlock, Collections.emptyList()), + BlockAddedEvent.createForHeadAdvancement( + currentBlock, Collections.emptyList(), Collections.emptyList()), blockchainQueries.getBlockchain()); return currentBlock.getHash(); } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMiningCoordinatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMiningCoordinatorTest.java index 989579732b..e2adaa76d9 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMiningCoordinatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractMiningCoordinatorTest.java @@ -106,7 +106,9 @@ public class AbstractMiningCoordinatorTest { miningCoordinator.start(); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verifyNoMoreInteractions(minerExecutor, blockMiner); } @@ -118,7 +120,9 @@ public class AbstractMiningCoordinatorTest { miningCoordinator.start(); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verify(blockMiner).cancel(); verify(minerExecutor, times(2)).startAsyncMining(any(), any(), any()); @@ -175,7 +179,9 @@ public class AbstractMiningCoordinatorTest { miningCoordinator.start(); when(syncState.isInSync()).thenReturn(true); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verifyNoMoreInteractions(minerExecutor, blockMiner); } @@ -185,7 +191,9 @@ public class AbstractMiningCoordinatorTest { miningCoordinator.enable(); when(syncState.isInSync()).thenReturn(true); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verifyNoMoreInteractions(minerExecutor, blockMiner); } @@ -198,7 +206,9 @@ public class AbstractMiningCoordinatorTest { when(syncState.isInSync()).thenReturn(true); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verifyNoMoreInteractions(minerExecutor, blockMiner); } @@ -213,7 +223,9 @@ public class AbstractMiningCoordinatorTest { when(syncState.isInSync()).thenReturn(true); miningCoordinator.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(BLOCK, Collections.emptyList()), blockchain); + BlockAddedEvent.createForHeadAdvancement( + BLOCK, Collections.emptyList(), Collections.emptyList()), + blockchain); verifyNoMoreInteractions(minerExecutor, blockMiner); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BlockAddedEvent.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BlockAddedEvent.java index ff33b6cc0e..9dbb0e4f19 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BlockAddedEvent.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BlockAddedEvent.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.LogWithMetadata; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; import java.util.Collections; import java.util.List; @@ -27,6 +28,7 @@ public class BlockAddedEvent { private final Block block; private final List addedTransactions; private final List removedTransactions; + private final List transactionReceipts; private final EventType eventType; private final List logsWithMetadata; private final Hash commonAncestorHash; @@ -42,23 +44,28 @@ public class BlockAddedEvent { final Block block, final List addedTransactions, final List removedTransactions, + final List transactionReceipts, final List logsWithMetadata, final Hash commonAncestorHash) { this.eventType = eventType; this.block = block; this.addedTransactions = addedTransactions; this.removedTransactions = removedTransactions; + this.transactionReceipts = transactionReceipts; this.logsWithMetadata = logsWithMetadata; this.commonAncestorHash = commonAncestorHash; } public static BlockAddedEvent createForHeadAdvancement( - final Block block, final List logsWithMetadata) { + final Block block, + final List logsWithMetadata, + final List transactionReceipts) { return new BlockAddedEvent( EventType.HEAD_ADVANCED, block, block.getBody().getTransactions(), Collections.emptyList(), + transactionReceipts, logsWithMetadata, block.getHeader().getParentHash()); } @@ -67,6 +74,7 @@ public class BlockAddedEvent { final Block block, final List addedTransactions, final List removedTransactions, + final List transactionReceipts, final List logsWithMetadata, final Hash commonAncestorHash) { return new BlockAddedEvent( @@ -74,6 +82,7 @@ public class BlockAddedEvent { block, addedTransactions, removedTransactions, + transactionReceipts, logsWithMetadata, commonAncestorHash); } @@ -85,6 +94,7 @@ public class BlockAddedEvent { Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), block.getHeader().getParentHash()); } @@ -108,6 +118,10 @@ public class BlockAddedEvent { return removedTransactions; } + public List getTransactionReceipts() { + return transactionReceipts; + } + public List getLogsWithMetadata() { return logsWithMetadata; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java index aeed141129..0e664dd93d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java @@ -14,9 +14,9 @@ */ package org.hyperledger.besu.ethereum.chain; -import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockWithReceipts; public interface ChainReorgObserver { - void onBlockAdded(BlockHeader blockHeader, Blockchain blockchain); + void onBlockAdded(BlockWithReceipts blockWithReceipts, Blockchain blockchain); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java index 968954b7aa..04bb52f2dc 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java @@ -288,7 +288,8 @@ public class DefaultBlockchain implements MutableBlockchain { return BlockAddedEvent.createForHeadAdvancement( newBlock, LogWithMetadata.generate( - blockWithReceipts.getBlock(), blockWithReceipts.getReceipts(), false)); + blockWithReceipts.getBlock(), blockWithReceipts.getReceipts(), false), + blockWithReceipts.getReceipts()); } else if (totalDifficulty.compareTo(blockchainStorage.getTotalDifficulty(chainHead).get()) > 0) { // New block represents a chain reorganization @@ -345,7 +346,7 @@ public class DefaultBlockchain implements MutableBlockchain { newTransactions.put( blockHash, currentNewChainWithReceipts.getBlock().getBody().getTransactions()); addAddedLogsWithMetadata(addedLogsWithMetadata, currentNewChainWithReceipts); - notifyChainReorgBlockAdded(currentNewChainWithReceipts.getHeader()); + notifyChainReorgBlockAdded(currentNewChainWithReceipts); currentNewChainWithReceipts = getParentBlockWithReceipts(currentNewChainWithReceipts); } @@ -401,6 +402,7 @@ public class DefaultBlockchain implements MutableBlockchain { newChainHeadWithReceipts.getBlock(), newTransactions.values().stream().flatMap(Collection::stream).collect(toList()), removedTransactions, + newChainHeadWithReceipts.getReceipts(), Stream.concat(removedLogsWithMetadata.stream(), addedLogsWithMetadata.stream()) .collect(Collectors.toUnmodifiableList()), currentNewChainWithReceipts.getBlock().getHash()); @@ -569,7 +571,7 @@ public class DefaultBlockchain implements MutableBlockchain { blockAddedObservers.forEach(observer -> observer.onBlockAdded(event, this)); } - private void notifyChainReorgBlockAdded(final BlockHeader blockHeader) { - blockReorgObservers.forEach(observer -> observer.onBlockAdded(blockHeader, this)); + private void notifyChainReorgBlockAdded(final BlockWithReceipts blockWithReceipts) { + blockReorgObservers.forEach(observer -> observer.onBlockAdded(blockWithReceipts, this)); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java index 27c367ed10..b568515fe7 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java @@ -37,7 +37,7 @@ import org.apache.tuweni.bytes.Bytes; * formats: logs, logs bloom, and cumulative gas used in the block. The TransactionReceiptType * attribute is the best way to check which format has been used. */ -public class TransactionReceipt { +public class TransactionReceipt implements org.hyperledger.besu.plugin.data.TransactionReceipt { private static final int NONEXISTENT = -1; @@ -228,6 +228,7 @@ public class TransactionReceipt { * * @return the total amount of gas consumed in the block after the transaction has been processed */ + @Override public long getCumulativeGasUsed() { return cumulativeGasUsed; } @@ -237,6 +238,7 @@ public class TransactionReceipt { * * @return the logs generated by the transaction */ + @Override public List getLogs() { return logs; } @@ -246,6 +248,7 @@ public class TransactionReceipt { * * @return the logs bloom filter for the logs generated by the transaction */ + @Override public LogsBloomFilter getBloomFilter() { return bloomFilter; } @@ -255,6 +258,7 @@ public class TransactionReceipt { * * @return the status code if the transaction receipt is status-encoded; otherwise {@code -1} */ + @Override public int getStatus() { return status; } @@ -263,6 +267,7 @@ public class TransactionReceipt { return transactionReceiptType; } + @Override public Optional getRevertReason() { return revertReason; } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiterTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiterTest.java index 8c0a737790..3d0900f1ed 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiterTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/TrailingPeerLimiterTest.java @@ -118,6 +118,7 @@ public class TrailingPeerLimiterTest { new Block( new BlockHeaderTestFixture().number(500).buildHeader(), new BlockBody(emptyList(), emptyList())), + Collections.emptyList(), Collections.emptyList()); trailingPeerLimiter.onBlockAdded(blockAddedEvent, blockchain); @@ -135,6 +136,7 @@ public class TrailingPeerLimiterTest { new Block( new BlockHeaderTestFixture().number(599).buildHeader(), new BlockBody(emptyList(), emptyList())), + Collections.emptyList(), Collections.emptyList()); trailingPeerLimiter.onBlockAdded(blockAddedEvent, blockchain); diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index fc7ecbcfed..643e9d93a2 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -65,7 +65,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'xsbRG+RsUybzMTULPcsLugarMHFTU7ohQBKw44x2/zQ=' + knownHash = 'fL4EQBDRXnw1hiUIUMscLxRG/uWznpZSNJGSIp9+mFo=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/AddedBlockContext.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/AddedBlockContext.java new file mode 100644 index 0000000000..ae56e58efe --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/AddedBlockContext.java @@ -0,0 +1,42 @@ +/* + * 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.plugin.data; + +import java.util.List; + +/** The minimum set of data for a AddedBlockContext. */ +public interface AddedBlockContext { + + /** + * A {@link BlockHeader} object. + * + * @return A {@link BlockHeader} + */ + BlockHeader getBlockHeader(); + + /** + * A {@link BlockHeader} object. + * + * @return A {@link BlockHeader} + */ + BlockBody getBlockBody(); + + /** + * A list of transaction receipts for the added block. + * + * @return A List of {@link TransactionReceipt} + */ + List getTransactionReceipts(); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionReceipt.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionReceipt.java new file mode 100644 index 0000000000..2931cac332 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionReceipt.java @@ -0,0 +1,58 @@ +/* + * 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.plugin.data; + +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; + +/** A transaction receipt, containing information pertaining a transaction execution. */ +public interface TransactionReceipt { + /** + * Returns the total amount of gas consumed in the block after the transaction has been processed. + * + * @return the total amount of gas consumed in the block after the transaction has been processed + */ + long getCumulativeGasUsed(); + + /** + * Returns the logs generated by the transaction. + * + * @return the logs generated by the transaction + */ + List getLogs(); + + /** + * Returns the Bytes for the logs generated by the transaction + * + * @return the Bytes for the logs generated by the transaction + */ + Bytes getBloomFilter(); + + /** + * Returns the status code for the status-encoded transaction receipt + * + * @return the status code if the transaction receipt is status-encoded; otherwise {@code -1} + */ + int getStatus(); + + /** + * Returns the ABI-encoded revert reason for the failed transaction (if applicable) + * + * @return the ABI-encoded revert reason for the failed transaction (if applicable) + */ + Optional getRevertReason(); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java index ec99625f2c..12a422b3ee 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.plugin.services; +import org.hyperledger.besu.plugin.data.AddedBlockContext; import org.hyperledger.besu.plugin.data.Address; import org.hyperledger.besu.plugin.data.LogWithMetadata; import org.hyperledger.besu.plugin.data.PropagatedBlockContext; @@ -57,6 +58,36 @@ public interface BesuEvents { */ void removeBlockPropagatedListener(long listenerIdentifier); + /** + * Add a listener watching for new blocks added. + * + * @param blockAddedListener The listener that will accept the Block object as the event. + * @return an id to be used as an identifier when de-registering the event. + */ + long addBlockAddedListener(BlockAddedListener blockAddedListener); + + /** + * Remove the block added listener from besu notifications. + * + * @param listenerIdentifier The id that was returned from addBlockAddedListener; + */ + void removeBlockAddedListener(long listenerIdentifier); + + /** + * Add a listener watching for new reorg blocks added. + * + * @param blockReorgListener The listener that will accept the reorg Block object as the event. + * @return an id to be used as an identifier when de-registering the event. + */ + long addBlockReorgListener(BlockReorgListener blockReorgListener); + + /** + * Remove the block reorg listener from besu notifications. + * + * @param listenerIdentifier The id that was returned from addBlockReorgListener; + */ + void removeBlockReorgListener(long listenerIdentifier); + /** * Add a listener watching new transactions added to the node. * @@ -138,6 +169,28 @@ public interface BesuEvents { void onBlockPropagated(PropagatedBlockContext propagatedBlockContext); } + /** The listener interface for receiving new block added events. */ + interface BlockAddedListener { + + /** + * Invoked when a new block has been evaluated and validated. + * + * @param addedBlockContext block being added. + */ + void onBlockAdded(AddedBlockContext addedBlockContext); + } + + /** The listener interface for receiving new block reorg events. */ + interface BlockReorgListener { + + /** + * Invoked when a reorg block has been evaluated and validated. + * + * @param addedBlockContext reorg block being added. + */ + void onBlockReorg(AddedBlockContext addedBlockContext); + } + /** The listener interface for receiving new transaction added events. */ interface TransactionAddedListener {