From 4d198eb909ec639fb05548a60d47130ec99f8568 Mon Sep 17 00:00:00 2001 From: David Mechler Date: Mon, 2 Mar 2020 10:49:51 -0500 Subject: [PATCH] =?UTF-8?q?Updates=20made=20to=20send=20notifications=20fo?= =?UTF-8?q?r=20new=20fork=20headers=20when=20the=20chai=E2=80=A6=20(#403)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Mechler --- .../NewBlockHeadersSubscriptionService.java | 51 ++-- ...ewBlockHeadersSubscriptionServiceTest.java | 236 ++++++++++-------- .../besu/ethereum/chain/BlockAddedEvent.java | 26 +- .../ethereum/chain/DefaultBlockchain.java | 3 +- 4 files changed, 189 insertions(+), 127 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionService.java index d5ebf35b6e..dfbf5803ad 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionService.java @@ -22,8 +22,12 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; import org.hyperledger.besu.ethereum.chain.BlockAddedObserver; import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Hash; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; import com.google.common.base.Suppliers; @@ -43,28 +47,43 @@ public class NewBlockHeadersSubscriptionService implements BlockAddedObserver { @Override public void onBlockAdded(final BlockAddedEvent event, final Blockchain blockchain) { if (event.isNewCanonicalHead()) { - subscriptionManager.notifySubscribersOnWorkerThread( - SubscriptionType.NEW_BLOCK_HEADERS, - NewBlockHeadersSubscription.class, - subscribers -> { - final Hash newBlockHash = event.getBlock().getHash(); + final List blocks = new ArrayList<>(); + Block blockPtr = event.getBlock(); - // memoize - final Supplier blockWithTx = - Suppliers.memoize(() -> blockWithCompleteTransaction(newBlockHash)); - final Supplier blockWithoutTx = - Suppliers.memoize(() -> blockWithTransactionHash(newBlockHash)); + while (!blockPtr.getHash().equals(event.getCommonAncestorHash())) { + blocks.add(blockPtr); - for (final NewBlockHeadersSubscription subscription : subscribers) { - final BlockResult newBlock = - subscription.getIncludeTransactions() ? blockWithTx.get() : blockWithoutTx.get(); + blockPtr = + blockchain + .getBlockByHash(blockPtr.getHeader().getParentHash()) + .orElseThrow(() -> new IllegalStateException("The block was on a orphaned chain.")); + } - subscriptionManager.sendMessage(subscription.getSubscriptionId(), newBlock); - } - }); + Collections.reverse(blocks); + blocks.forEach(b -> notifySubscribers(b.getHash())); } } + private void notifySubscribers(final Hash newBlockHash) { + subscriptionManager.notifySubscribersOnWorkerThread( + SubscriptionType.NEW_BLOCK_HEADERS, + NewBlockHeadersSubscription.class, + subscribers -> { + // memoize + final Supplier blockWithTx = + Suppliers.memoize(() -> blockWithCompleteTransaction(newBlockHash)); + final Supplier blockWithoutTx = + Suppliers.memoize(() -> blockWithTransactionHash(newBlockHash)); + + for (final NewBlockHeadersSubscription subscription : subscribers) { + final BlockResult newBlock = + subscription.getIncludeTransactions() ? blockWithTx.get() : blockWithoutTx.get(); + + subscriptionManager.sendMessage(subscription.getSubscriptionId(), newBlock); + } + }); + } + private BlockResult blockWithCompleteTransaction(final Hash hash) { return blockchainQueries.blockByHash(hash).map(blockResult::transactionComplete).orElse(null); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java index 38c051c44d..692d4d23a5 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/blockheaders/NewBlockHeadersSubscriptionServiceTest.java @@ -15,138 +15,191 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.blockheaders; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; -import org.hyperledger.besu.crypto.SECP256K1.KeyPair; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResult; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.JsonRpcResult; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; -import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; -import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; +import org.hyperledger.besu.ethereum.chain.BlockchainStorage; +import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; 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.BlockHeaderTestFixture; -import org.hyperledger.besu.ethereum.core.Difficulty; -import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.TransactionTestFixture; - -import java.util.ArrayList; -import java.util.Collections; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; + import java.util.List; -import java.util.Optional; import java.util.function.Consumer; -import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class NewBlockHeadersSubscriptionServiceTest { - private NewBlockHeadersSubscriptionService newBlockHeadersSubscriptionService; - @Captor ArgumentCaptor subscriptionIdCaptor; @Captor ArgumentCaptor responseCaptor; - @Mock private SubscriptionManager subscriptionManager; - @Mock private BlockchainQueries blockchainQueries; - - private final BlockHeaderTestFixture blockHeaderTestFixture = new BlockHeaderTestFixture(); - private final TransactionTestFixture txTestFixture = new TransactionTestFixture(); - private final BlockHeader blockHeader = blockHeaderTestFixture.buildHeader(); private final BlockResultFactory blockResultFactory = new BlockResultFactory(); + private final BlockDataGenerator gen = new BlockDataGenerator(); + private final BlockchainStorage blockchainStorage = + new KeyValueStoragePrefixedKeyBlockchainStorage( + new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()); + private final Block genesisBlock = gen.genesisBlock(); + private final MutableBlockchain blockchain = + DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, new NoOpMetricsSystem()); + + @Spy + private final SubscriptionManager subscriptionManagerSpy = + new SubscriptionManager(new NoOpMetricsSystem()); + + @Spy + private final BlockchainQueries blockchainQueriesSpy = + Mockito.spy(new BlockchainQueries(blockchain, createInMemoryWorldStateArchive())); @Before public void before() { - newBlockHeadersSubscriptionService = - new NewBlockHeadersSubscriptionService(subscriptionManager, blockchainQueries); + final NewBlockHeadersSubscriptionService newBlockHeadersSubscriptionService = + new NewBlockHeadersSubscriptionService(subscriptionManagerSpy, blockchainQueriesSpy); + blockchain.observeBlockAdded(newBlockHeadersSubscriptionService); } @Test public void shouldSendMessageWhenBlockAddedOnCanonicalChain() { final NewBlockHeadersSubscription subscription = createSubscription(false); mockSubscriptionManagerNotifyMethod(subscription); - final BlockResult expectedNewBlock = expectedBlockWithTransactions(Collections.emptyList()); - - simulateAddingBlockOnCanonicalChain(); + final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); + final BlockResult expectedNewBlock = + blockResultFactory.transactionHash( + blockchainQueriesSpy.blockByHashWithTxHashes(testBlock.getHash()).orElse(null)); - verify(subscriptionManager) + verify(subscriptionManagerSpy) .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); - assertThat(responseCaptor.getValue()) - .isEqualToComparingFieldByFieldRecursively(expectedNewBlock); + assertThat(responseCaptor.getValue()).usingRecursiveComparison().isEqualTo(expectedNewBlock); } @Test public void shouldNotSendMessageWhenBlockAddedIsNotOnCanonicalChain() { - simulateAddingBlockOnNonCanonicalChain(); + final NewBlockHeadersSubscription subscription = createSubscription(false); + mockSubscriptionManagerNotifyMethod(subscription); + + final Block canonicalBlock = appendBlockWithParent(blockchain, genesisBlock); + final BlockOptions options = + new BlockOptions() + .setBlockNumber(genesisBlock.getHeader().getNumber() + 1) + .setParentHash(genesisBlock.getHash()) + .setDifficulty(genesisBlock.getHeader().getDifficulty().divide(100L)); + appendBlockWithParent(blockchain, options); + final BlockResult expectedNewBlock = + blockResultFactory.transactionHash( + blockchainQueriesSpy.blockByHashWithTxHashes(canonicalBlock.getHash()).orElse(null)); + + verify(subscriptionManagerSpy, times(1)).notifySubscribersOnWorkerThread(any(), any(), any()); + verify(subscriptionManagerSpy, times(1)) + .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); + assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); + List capturedNewBlocks = responseCaptor.getAllValues(); + assertThat(capturedNewBlocks.size()).isEqualTo(1); + assertThat(capturedNewBlocks.get(0)).usingRecursiveComparison().isEqualTo(expectedNewBlock); + } + + @Test + public void shouldSendMessagesWhenReorgBlockAdded() { + final NewBlockHeadersSubscription subscription = createSubscription(false); + mockSubscriptionManagerNotifyMethod(subscription); - verifyZeroInteractions(subscriptionManager); + final Block canonicalBlock = appendBlockWithParent(blockchain, genesisBlock); + final BlockOptions options = + new BlockOptions() + .setBlockNumber(genesisBlock.getHeader().getNumber() + 1) + .setParentHash(genesisBlock.getHash()) + .setDifficulty(genesisBlock.getHeader().getDifficulty().divide(100L)); + final Block forkBlock = appendBlockWithParent(blockchain, options); + options.setDifficulty(forkBlock.getHeader().getDifficulty().divide(100L)); + appendBlockWithParent(blockchain, options); + options.setDifficulty(blockchain.getChainHeadBlock().getHeader().getDifficulty().multiply(2L)); + final Block forkBlock2 = appendBlockWithParent(blockchain, options); + final BlockResult expectedNewBlock = + blockResultFactory.transactionHash( + blockchainQueriesSpy.blockByHashWithTxHashes(canonicalBlock.getHash()).orElse(null)); + final BlockResult expectedNewBlock1 = + blockResultFactory.transactionHash( + blockchainQueriesSpy.blockByHashWithTxHashes(forkBlock2.getHash()).orElse(null)); + + verify(subscriptionManagerSpy, times(2)).notifySubscribersOnWorkerThread(any(), any(), any()); + verify(subscriptionManagerSpy, times(2)) + .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); + assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); + List capturedNewBlocks = responseCaptor.getAllValues(); + assertThat(capturedNewBlocks.size()).isEqualTo(2); + assertThat(capturedNewBlocks.get(0)).usingRecursiveComparison().isEqualTo(expectedNewBlock); + assertThat(capturedNewBlocks.get(1)).usingRecursiveComparison().isEqualTo(expectedNewBlock1); } @Test public void shouldReturnTxHashesWhenIncludeTransactionsFalse() { final NewBlockHeadersSubscription subscription = createSubscription(false); mockSubscriptionManagerNotifyMethod(subscription); - final List txHashList = transactionsWithHashOnly(); - final BlockResult expectedNewBlock = expectedBlockWithTransactions(txHashList); - simulateAddingBlockOnCanonicalChain(); + final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); + final BlockResult expectedNewBlock = + blockResultFactory.transactionHash( + blockchainQueriesSpy.blockByHashWithTxHashes(testBlock.getHash()).orElse(null)); - verify(subscriptionManager) + verify(subscriptionManagerSpy) .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); final Object actualBlock = responseCaptor.getValue(); assertThat(actualBlock).isInstanceOf(BlockResult.class); - assertThat(((BlockResult) actualBlock).getTransactions()).hasSize(txHashList.size()); - assertThat(actualBlock).isEqualToComparingFieldByFieldRecursively(expectedNewBlock); + assertThat(((BlockResult) actualBlock).getTransactions()) + .hasSize(expectedNewBlock.getTransactions().size()); + assertThat(actualBlock).usingRecursiveComparison().isEqualTo(expectedNewBlock); - verify(blockchainQueries, times(1)).blockByHashWithTxHashes(any()); - verify(blockchainQueries, times(0)).blockByHash(any()); + verify(blockchainQueriesSpy, times(2)).blockByHashWithTxHashes(any()); + verify(blockchainQueriesSpy, times(0)).blockByHash(any()); } @Test public void shouldReturnCompleteTxWhenParameterTrue() { final NewBlockHeadersSubscription subscription = createSubscription(true); mockSubscriptionManagerNotifyMethod(subscription); - final List txHashList = transactionsWithMetadata(); - final BlockWithMetadata testBlockWithMetadata = - new BlockWithMetadata<>( - blockHeader, txHashList, Collections.emptyList(), blockHeader.getDifficulty(), 0); - final BlockResult expectedNewBlock = - blockResultFactory.transactionComplete(testBlockWithMetadata); - when(blockchainQueries.blockByHash(testBlockWithMetadata.getHeader().getHash())) - .thenReturn(Optional.of(testBlockWithMetadata)); - simulateAddingBlockOnCanonicalChain(); + final Block testBlock = appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); + final BlockResult expectedNewBlock = + blockResultFactory.transactionComplete( + blockchainQueriesSpy.blockByHash(testBlock.getHeader().getHash()).orElse(null)); - verify(subscriptionManager) + verify(subscriptionManagerSpy) .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); assertThat(subscriptionIdCaptor.getValue()).isEqualTo(subscription.getSubscriptionId()); final Object actualBlock = responseCaptor.getValue(); assertThat(actualBlock).isInstanceOf(BlockResult.class); - assertThat(((BlockResult) actualBlock).getTransactions()).hasSize(txHashList.size()); - assertThat(actualBlock).isEqualToComparingFieldByFieldRecursively(expectedNewBlock); + assertThat(((BlockResult) actualBlock).getTransactions()) + .hasSize(testBlock.getBody().getTransactions().size()); + assertThat(actualBlock).usingRecursiveComparison().isEqualTo(expectedNewBlock); - verify(subscriptionManager, times(1)).sendMessage(any(), any()); - verify(blockchainQueries, times(0)).blockByHashWithTxHashes(any()); - verify(blockchainQueries, times(1)).blockByHash(any()); + verify(subscriptionManagerSpy, times(1)).sendMessage(any(), any()); + verify(blockchainQueriesSpy, times(0)).blockByHashWithTxHashes(any()); + verify(blockchainQueriesSpy, times(2)).blockByHash(any()); } @Test @@ -157,9 +210,9 @@ public class NewBlockHeadersSubscriptionServiceTest { final NewBlockHeadersSubscription subscription4 = createSubscription(false); mockSubscriptionManagerNotifyMethod(subscription1, subscription2, subscription3, subscription4); - simulateAddingBlockOnCanonicalChain(); + appendBlockWithParent(blockchain, blockchain.getChainHeadBlock()); - verify(subscriptionManager, times(4)) + verify(subscriptionManagerSpy, times(4)) .sendMessage(subscriptionIdCaptor.capture(), responseCaptor.capture()); assertThat(subscriptionIdCaptor.getAllValues()) .containsExactly( @@ -168,18 +221,8 @@ public class NewBlockHeadersSubscriptionServiceTest { subscription3.getSubscriptionId(), subscription4.getSubscriptionId()); - verify(blockchainQueries, times(1)).blockByHashWithTxHashes(any()); - verify(blockchainQueries, times(1)).blockByHash(any()); - } - - private BlockResult expectedBlockWithTransactions(final List objects) { - final BlockWithMetadata testBlockWithMetadata = - new BlockWithMetadata<>(blockHeader, objects, Collections.emptyList(), Difficulty.ONE, 1); - final BlockResult expectedNewBlock = blockResultFactory.transactionHash(testBlockWithMetadata); - - when(blockchainQueries.blockByHashWithTxHashes(testBlockWithMetadata.getHeader().getHash())) - .thenReturn(Optional.of(testBlockWithMetadata)); - return expectedNewBlock; + verify(blockchainQueriesSpy, times(1)).blockByHashWithTxHashes(any()); + verify(blockchainQueriesSpy, times(1)).blockByHash(any()); } private void mockSubscriptionManagerNotifyMethod( @@ -190,46 +233,29 @@ public class NewBlockHeadersSubscriptionServiceTest { consumer.accept(List.of(subscriptions)); return null; }) - .when(subscriptionManager) + .when(subscriptionManagerSpy) .notifySubscribersOnWorkerThread(any(), any(), any()); } - private void simulateAddingBlockOnCanonicalChain() { - final BlockBody blockBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); - final Block testBlock = new Block(blockHeader, blockBody); - newBlockHeadersSubscriptionService.onBlockAdded( - BlockAddedEvent.createForHeadAdvancement(testBlock, Collections.emptyList()), - blockchainQueries.getBlockchain()); - verify(blockchainQueries, times(1)).getBlockchain(); + private NewBlockHeadersSubscription createSubscription(final boolean includeTransactions) { + return new NewBlockHeadersSubscription(1L, "conn", includeTransactions); } - private void simulateAddingBlockOnNonCanonicalChain() { - final BlockBody blockBody = new BlockBody(Collections.emptyList(), Collections.emptyList()); - final Block testBlock = new Block(blockHeader, blockBody); - newBlockHeadersSubscriptionService.onBlockAdded( - BlockAddedEvent.createForFork(testBlock), blockchainQueries.getBlockchain()); - verify(blockchainQueries, times(1)).getBlockchain(); - } + private Block appendBlockWithParent(final MutableBlockchain blockchain, final Block parent) { + final BlockOptions options = + new BlockOptions() + .setBlockNumber(parent.getHeader().getNumber() + 1) + .setParentHash(parent.getHash()); - private List transactionsWithMetadata() { - final TransactionWithMetadata t1 = - new TransactionWithMetadata( - txTestFixture.createTransaction(KeyPair.generate()), 0L, Hash.ZERO, 0); - final TransactionWithMetadata t2 = - new TransactionWithMetadata( - txTestFixture.createTransaction(KeyPair.generate()), 1L, Hash.ZERO, 1); - return Lists.newArrayList(t1, t2); + return appendBlockWithParent(blockchain, options); } - private List transactionsWithHashOnly() { - final List hashes = new ArrayList<>(); - for (final TransactionWithMetadata transactionWithMetadata : transactionsWithMetadata()) { - hashes.add(transactionWithMetadata.getTransaction().getHash()); - } - return hashes; - } + private Block appendBlockWithParent( + final MutableBlockchain blockchain, final BlockOptions options) { + final Block newBlock = gen.block(options); + final List receipts = gen.receipts(newBlock); + blockchain.appendBlock(newBlock, receipts); - private NewBlockHeadersSubscription createSubscription(final boolean includeTransactions) { - return new NewBlockHeadersSubscription(1L, "conn", includeTransactions); + return newBlock; } } 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 1dadea7ee8..ff33b6cc0e 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 @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.chain; 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; @@ -28,6 +29,7 @@ public class BlockAddedEvent { private final List removedTransactions; private final EventType eventType; private final List logsWithMetadata; + private final Hash commonAncestorHash; public enum EventType { HEAD_ADVANCED, @@ -40,12 +42,14 @@ public class BlockAddedEvent { final Block block, final List addedTransactions, final List removedTransactions, - final List logsWithMetadata) { + final List logsWithMetadata, + final Hash commonAncestorHash) { this.eventType = eventType; this.block = block; this.addedTransactions = addedTransactions; this.removedTransactions = removedTransactions; this.logsWithMetadata = logsWithMetadata; + this.commonAncestorHash = commonAncestorHash; } public static BlockAddedEvent createForHeadAdvancement( @@ -55,16 +59,23 @@ public class BlockAddedEvent { block, block.getBody().getTransactions(), Collections.emptyList(), - logsWithMetadata); + logsWithMetadata, + block.getHeader().getParentHash()); } public static BlockAddedEvent createForChainReorg( final Block block, final List addedTransactions, final List removedTransactions, - final List logsWithMetadata) { + final List logsWithMetadata, + final Hash commonAncestorHash) { return new BlockAddedEvent( - EventType.CHAIN_REORG, block, addedTransactions, removedTransactions, logsWithMetadata); + EventType.CHAIN_REORG, + block, + addedTransactions, + removedTransactions, + logsWithMetadata, + commonAncestorHash); } public static BlockAddedEvent createForFork(final Block block) { @@ -73,7 +84,8 @@ public class BlockAddedEvent { block, Collections.emptyList(), Collections.emptyList(), - Collections.emptyList()); + Collections.emptyList(), + block.getHeader().getParentHash()); } public Block getBlock() { @@ -99,4 +111,8 @@ public class BlockAddedEvent { public List getLogsWithMetadata() { return logsWithMetadata; } + + public Hash getCommonAncestorHash() { + return commonAncestorHash; + } } 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 0602882c53..968954b7aa 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 @@ -402,7 +402,8 @@ public class DefaultBlockchain implements MutableBlockchain { newTransactions.values().stream().flatMap(Collection::stream).collect(toList()), removedTransactions, Stream.concat(removedLogsWithMetadata.stream(), addedLogsWithMetadata.stream()) - .collect(Collectors.toUnmodifiableList())); + .collect(Collectors.toUnmodifiableList()), + currentNewChainWithReceipts.getBlock().getHash()); } @Override