Skip to content

Commit 83f8d67

Browse files
mbaxtermacfarla
authored andcommitted
[Issue-6301] Add bad block events (besu-eth#6848)
Signed-off-by: mbaxter <mbaxter.dev@gmail.com>
1 parent c8f5b30 commit 83f8d67

File tree

12 files changed

+261
-32
lines changed

12 files changed

+261
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- Add `tx-pool-blob-price-bump` option to configure the price bump percentage required to replace blob transactions (by default 100%) [#6874](https://github.com/hyperledger/besu/pull/6874)
3939
- Log detailed timing of block creation steps [#6880](https://github.com/hyperledger/besu/pull/6880)
4040
- Expose transaction count by type metrics for the layered txpool [#6903](https://github.com/hyperledger/besu/pull/6903)
41+
- Expose bad block events via the BesuEvents plugin API [#6848](https://github.com/hyperledger/besu/pull/6848)
4142

4243
### Bug fixes
4344
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ public void startNode(final BesuNode node) {
313313
besuController.getProtocolContext().getBlockchain(),
314314
besuController.getProtocolManager().getBlockBroadcaster(),
315315
besuController.getTransactionPool(),
316-
besuController.getSyncState()));
316+
besuController.getSyncState(),
317+
besuController.getProtocolContext().getBadBlockManager()));
317318
besuPluginContext.startPlugins();
318319

319320
runner.startEthereumMainLoop();

besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,8 @@ private void startPlugins() {
12931293
besuController.getProtocolContext().getBlockchain(),
12941294
besuController.getProtocolManager().getBlockBroadcaster(),
12951295
besuController.getTransactionPool(),
1296-
besuController.getSyncState()));
1296+
besuController.getSyncState(),
1297+
besuController.getProtocolContext().getBadBlockManager()));
12971298
besuPluginContext.addService(MetricsSystem.class, getMetricsSystem());
12981299

12991300
besuPluginContext.addService(

besu/src/main/java/org/hyperledger/besu/services/BesuEventsImpl.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.hyperledger.besu.datatypes.Address;
2020
import org.hyperledger.besu.ethereum.api.query.LogsQuery;
21+
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
2122
import org.hyperledger.besu.ethereum.chain.Blockchain;
2223
import org.hyperledger.besu.ethereum.core.BlockBody;
2324
import org.hyperledger.besu.ethereum.core.Difficulty;
@@ -44,6 +45,7 @@ public class BesuEventsImpl implements BesuEvents {
4445
private final BlockBroadcaster blockBroadcaster;
4546
private final TransactionPool transactionPool;
4647
private final SyncState syncState;
48+
private final BadBlockManager badBlockManager;
4749

4850
/**
4951
* Constructor for BesuEventsImpl
@@ -52,16 +54,19 @@ public class BesuEventsImpl implements BesuEvents {
5254
* @param blockBroadcaster An instance of BlockBroadcaster
5355
* @param transactionPool An instance of TransactionPool
5456
* @param syncState An instance of SyncState
57+
* @param badBlockManager A cache of bad blocks encountered on the network
5558
*/
5659
public BesuEventsImpl(
5760
final Blockchain blockchain,
5861
final BlockBroadcaster blockBroadcaster,
5962
final TransactionPool transactionPool,
60-
final SyncState syncState) {
63+
final SyncState syncState,
64+
final BadBlockManager badBlockManager) {
6165
this.blockchain = blockchain;
6266
this.blockBroadcaster = blockBroadcaster;
6367
this.transactionPool = transactionPool;
6468
this.syncState = syncState;
69+
this.badBlockManager = badBlockManager;
6570
}
6671

6772
@Override
@@ -166,6 +171,16 @@ public void removeLogListener(final long listenerIdentifier) {
166171
blockchain.removeObserver(listenerIdentifier);
167172
}
168173

174+
@Override
175+
public long addBadBlockListener(final BadBlockListener listener) {
176+
return badBlockManager.subscribeToBadBlocks(listener);
177+
}
178+
179+
@Override
180+
public void removeBadBlockListener(final long listenerIdentifier) {
181+
badBlockManager.unsubscribeFromBadBlocks(listenerIdentifier);
182+
}
183+
169184
private static PropagatedBlockContext blockPropagatedContext(
170185
final Supplier<BlockHeader> blockHeaderSupplier,
171186
final Supplier<BlockBody> blockBodySupplier,

besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.hyperledger.besu.datatypes.Transaction;
2828
import org.hyperledger.besu.datatypes.Wei;
2929
import org.hyperledger.besu.ethereum.ProtocolContext;
30+
import org.hyperledger.besu.ethereum.chain.BadBlockCause;
31+
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
3032
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
3133
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
3234
import org.hyperledger.besu.ethereum.core.Block;
@@ -113,6 +115,7 @@ public class BesuEventsImplTest {
113115
private BesuEventsImpl serviceImpl;
114116
private MutableBlockchain blockchain;
115117
private final BlockDataGenerator gen = new BlockDataGenerator();
118+
private final BadBlockManager badBlockManager = new BadBlockManager();
116119

117120
@BeforeEach
118121
public void setUp() {
@@ -171,7 +174,9 @@ public void setUp() {
171174
new BlobCache(),
172175
MiningParameters.newDefault());
173176

174-
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
177+
serviceImpl =
178+
new BesuEventsImpl(
179+
blockchain, blockBroadcaster, transactionPool, syncState, badBlockManager);
175180
}
176181

177182
@Test
@@ -504,6 +509,85 @@ public void logEventDoesNotFireAfterUnsubscribe() {
504509
assertThat(result).isEmpty();
505510
}
506511

512+
@Test
513+
public void badBlockEventFiresAfterSubscribe_badBlockAdded() {
514+
// Track bad block events
515+
final AtomicReference<org.hyperledger.besu.plugin.data.BlockHeader> badBlockResult =
516+
new AtomicReference<>();
517+
final AtomicReference<org.hyperledger.besu.plugin.data.BadBlockCause> badBlockCauseResult =
518+
new AtomicReference<>();
519+
520+
serviceImpl.addBadBlockListener(
521+
(badBlock, cause) -> {
522+
badBlockResult.set(badBlock);
523+
badBlockCauseResult.set(cause);
524+
});
525+
526+
// Add bad block
527+
final BadBlockCause blockCause = BadBlockCause.fromValidationFailure("failed");
528+
final Block block = gen.block(new BlockDataGenerator.BlockOptions());
529+
badBlockManager.addBadBlock(block, blockCause);
530+
531+
// Check we caught the bad block
532+
assertThat(badBlockResult.get()).isEqualTo(block.getHeader());
533+
assertThat(badBlockCauseResult.get()).isEqualTo(blockCause);
534+
}
535+
536+
@Test
537+
public void badBlockEventFiresAfterSubscribe_badBlockHeaderAdded() {
538+
// Track bad block events
539+
final AtomicReference<org.hyperledger.besu.plugin.data.BlockHeader> badBlockResult =
540+
new AtomicReference<>();
541+
final AtomicReference<org.hyperledger.besu.plugin.data.BadBlockCause> badBlockCauseResult =
542+
new AtomicReference<>();
543+
544+
serviceImpl.addBadBlockListener(
545+
(badBlock, cause) -> {
546+
badBlockResult.set(badBlock);
547+
badBlockCauseResult.set(cause);
548+
});
549+
550+
// Add bad block header
551+
final BadBlockCause cause = BadBlockCause.fromValidationFailure("oops");
552+
final Block badBlock = gen.block(new BlockDataGenerator.BlockOptions());
553+
badBlockManager.addBadHeader(badBlock.getHeader(), cause);
554+
555+
// Check we caught the bad block
556+
assertThat(badBlockResult.get()).isEqualTo(badBlock.getHeader());
557+
assertThat(badBlockCauseResult.get()).isEqualTo(cause);
558+
}
559+
560+
@Test
561+
public void badBlockEventDoesNotFireAfterUnsubscribe() {
562+
// Track bad block events
563+
final AtomicReference<org.hyperledger.besu.plugin.data.BlockHeader> badBlockResult =
564+
new AtomicReference<>();
565+
final AtomicReference<org.hyperledger.besu.plugin.data.BadBlockCause> badBlockCauseResult =
566+
new AtomicReference<>();
567+
568+
final long listenerId =
569+
serviceImpl.addBadBlockListener(
570+
(badBlock, cause) -> {
571+
badBlockResult.set(badBlock);
572+
badBlockCauseResult.set(cause);
573+
});
574+
// Unsubscribe
575+
serviceImpl.removeBadBlockListener(listenerId);
576+
577+
// Add bad block
578+
final BadBlockCause blockCause = BadBlockCause.fromValidationFailure("failed");
579+
final Block block = gen.block(new BlockDataGenerator.BlockOptions());
580+
badBlockManager.addBadBlock(block, blockCause);
581+
// Add bad block header
582+
final BadBlockCause headerCause = BadBlockCause.fromValidationFailure("oops");
583+
final Block headerBlock = gen.block(new BlockDataGenerator.BlockOptions());
584+
badBlockManager.addBadHeader(headerBlock.getHeader(), headerCause);
585+
586+
// Check we did not process any events
587+
assertThat(badBlockResult.get()).isNull();
588+
assertThat(badBlockCauseResult.get()).isNull();
589+
}
590+
507591
private Block generateBlock() {
508592
final BlockBody body = new BlockBody(Collections.emptyList(), Collections.emptyList());
509593
return new Block(new BlockHeaderTestFixture().buildHeader(), body);

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockCause.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
*/
1616
package org.hyperledger.besu.ethereum.chain;
1717

18-
import static org.hyperledger.besu.ethereum.chain.BadBlockReason.DESCENDS_FROM_BAD_BLOCK;
19-
import static org.hyperledger.besu.ethereum.chain.BadBlockReason.SPEC_VALIDATION_FAILURE;
18+
import static org.hyperledger.besu.plugin.data.BadBlockCause.BadBlockReason.DESCENDS_FROM_BAD_BLOCK;
19+
import static org.hyperledger.besu.plugin.data.BadBlockCause.BadBlockReason.SPEC_VALIDATION_FAILURE;
2020

2121
import org.hyperledger.besu.ethereum.core.Block;
2222

2323
import com.google.common.base.MoreObjects;
2424

25-
public class BadBlockCause {
25+
public class BadBlockCause implements org.hyperledger.besu.plugin.data.BadBlockCause {
2626

2727
private final BadBlockReason reason;
2828
private final String description;
@@ -42,10 +42,12 @@ private BadBlockCause(final BadBlockReason reason, final String description) {
4242
this.description = description;
4343
}
4444

45+
@Override
4546
public BadBlockReason getReason() {
4647
return reason;
4748
}
4849

50+
@Override
4951
public String getDescription() {
5052
return description;
5153
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockManager.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import org.hyperledger.besu.datatypes.Hash;
1818
import org.hyperledger.besu.ethereum.core.Block;
1919
import org.hyperledger.besu.ethereum.core.BlockHeader;
20+
import org.hyperledger.besu.plugin.services.BesuEvents.BadBlockListener;
21+
import org.hyperledger.besu.util.Subscribers;
2022

2123
import java.util.Collection;
2224
import java.util.Optional;
@@ -37,6 +39,7 @@ public class BadBlockManager {
3739
CacheBuilder.newBuilder().maximumSize(MAX_BAD_BLOCKS_SIZE).concurrencyLevel(1).build();
3840
private final Cache<Hash, Hash> latestValidHashes =
3941
CacheBuilder.newBuilder().maximumSize(MAX_BAD_BLOCKS_SIZE).concurrencyLevel(1).build();
42+
private final Subscribers<BadBlockListener> badBlockSubscribers = Subscribers.create(true);
4043

4144
/**
4245
* Add a new invalid block.
@@ -45,9 +48,9 @@ public class BadBlockManager {
4548
* @param cause the cause detailing why the block is considered invalid
4649
*/
4750
public void addBadBlock(final Block badBlock, final BadBlockCause cause) {
48-
// TODO(#6301) Expose bad block with cause through BesuEvents
4951
LOG.debug("Register bad block {} with cause: {}", badBlock.toLogString(), cause);
5052
this.badBlocks.put(badBlock.getHash(), badBlock);
53+
badBlockSubscribers.forEach(s -> s.onBadBlockAdded(badBlock.getHeader(), cause));
5154
}
5255

5356
public void reset() {
@@ -81,9 +84,9 @@ public Optional<Block> getBadBlock(final Hash hash) {
8184
}
8285

8386
public void addBadHeader(final BlockHeader header, final BadBlockCause cause) {
84-
// TODO(#6301) Expose bad block header with cause through BesuEvents
8587
LOG.debug("Register bad block header {} with cause: {}", header.toLogString(), cause);
8688
badHeaders.put(header.getHash(), header);
89+
badBlockSubscribers.forEach(s -> s.onBadBlockAdded(header, cause));
8790
}
8891

8992
public boolean isBadBlock(final Hash blockHash) {
@@ -97,4 +100,12 @@ public void addLatestValidHash(final Hash blockHash, final Hash latestValidHash)
97100
public Optional<Hash> getLatestValidHash(final Hash blockHash) {
98101
return Optional.ofNullable(latestValidHashes.getIfPresent(blockHash));
99102
}
103+
104+
public long subscribeToBadBlocks(final BadBlockListener listener) {
105+
return badBlockSubscribers.subscribe(listener);
106+
}
107+
108+
public void unsubscribeFromBadBlocks(final long id) {
109+
badBlockSubscribers.unsubscribe(id);
110+
}
100111
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/BadBlockReason.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/chain/BadBlockManagerTest.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@
2020
import org.hyperledger.besu.ethereum.core.Block;
2121
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
2222

23+
import java.util.concurrent.atomic.AtomicInteger;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
2326
import org.junit.jupiter.api.Test;
2427

2528
public class BadBlockManagerTest {
2629

2730
final BlockchainSetupUtil chainUtil = BlockchainSetupUtil.forMainnet();
2831
final Block block = chainUtil.getBlock(1);
32+
final Block block2 = chainUtil.getBlock(2);
2933
final BadBlockManager badBlockManager = new BadBlockManager();
3034

3135
@Test
@@ -66,4 +70,64 @@ public void isBadBlock_trueForBadBlock() {
6670

6771
assertThat(badBlockManager.isBadBlock(block.getHash())).isTrue();
6872
}
73+
74+
@Test
75+
public void subscribeToBadBlocks_listenerReceivesBadBlockEvent() {
76+
77+
final AtomicReference<org.hyperledger.besu.plugin.data.BlockHeader> badBlockResult =
78+
new AtomicReference<>();
79+
final AtomicReference<org.hyperledger.besu.plugin.data.BadBlockCause> badBlockCauseResult =
80+
new AtomicReference<>();
81+
82+
badBlockManager.subscribeToBadBlocks(
83+
(badBlock, cause) -> {
84+
badBlockResult.set(badBlock);
85+
badBlockCauseResult.set(cause);
86+
});
87+
88+
final BadBlockCause cause = BadBlockCause.fromValidationFailure("fail");
89+
badBlockManager.addBadBlock(block, cause);
90+
91+
// Check event was emitted
92+
assertThat(badBlockResult.get()).isEqualTo(block.getHeader());
93+
assertThat(badBlockCauseResult.get()).isEqualTo(cause);
94+
}
95+
96+
@Test
97+
public void subscribeToBadBlocks_listenerReceivesBadHeaderEvent() {
98+
99+
final AtomicReference<org.hyperledger.besu.plugin.data.BlockHeader> badBlockResult =
100+
new AtomicReference<>();
101+
final AtomicReference<org.hyperledger.besu.plugin.data.BadBlockCause> badBlockCauseResult =
102+
new AtomicReference<>();
103+
104+
badBlockManager.subscribeToBadBlocks(
105+
(badBlock, cause) -> {
106+
badBlockResult.set(badBlock);
107+
badBlockCauseResult.set(cause);
108+
});
109+
110+
final BadBlockCause cause = BadBlockCause.fromValidationFailure("fail");
111+
badBlockManager.addBadHeader(block.getHeader(), cause);
112+
113+
// Check event was emitted
114+
assertThat(badBlockResult.get()).isEqualTo(block.getHeader());
115+
assertThat(badBlockCauseResult.get()).isEqualTo(cause);
116+
}
117+
118+
@Test
119+
public void unsubscribeFromBadBlocks_listenerReceivesNoEvents() {
120+
121+
final AtomicInteger eventCount = new AtomicInteger(0);
122+
final long subscribeId =
123+
badBlockManager.subscribeToBadBlocks((block, cause) -> eventCount.incrementAndGet());
124+
badBlockManager.unsubscribeFromBadBlocks(subscribeId);
125+
126+
final BadBlockCause cause = BadBlockCause.fromValidationFailure("fail");
127+
badBlockManager.addBadBlock(block, cause);
128+
badBlockManager.addBadHeader(block2.getHeader(), cause);
129+
130+
// Check no events fired
131+
assertThat(eventCount.get()).isEqualTo(0);
132+
}
69133
}

plugin-api/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Calculated : ${currentHash}
6969
tasks.register('checkAPIChanges', FileStateChecker) {
7070
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
7171
files = sourceSets.main.allJava.files
72-
knownHash = 'lsBecdCyK9rIi5FIjURF2uPwKzXgqHCayMcLyOOl4fE='
72+
knownHash = '0mJiCGsToqx5aAJEvwnT3V0R8o4PXBYWiB0wT6CMpuo='
7373
}
7474
check.dependsOn('checkAPIChanges')
7575

0 commit comments

Comments
 (0)