Skip to content

Commit 4fb8333

Browse files
abieslpetrovic05
andauthored
chore: Change IntakeAndConsensusTests to check both generation and birth round (#18607)
Signed-off-by: Artur Biesiadowski <[email protected]> Signed-off-by: Lazar Petrovic <[email protected]> Co-authored-by: Lazar Petrovic <[email protected]>
1 parent 053fb87 commit 4fb8333

File tree

1 file changed

+67
-39
lines changed

1 file changed

+67
-39
lines changed
+67-39
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,34 @@
99
import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder;
1010
import com.swirlds.platform.internal.EventImpl;
1111
import com.swirlds.platform.test.fixtures.consensus.TestIntake;
12-
import com.swirlds.platform.test.fixtures.consensus.framework.validation.RoundInternalEqualityValidation;
12+
import com.swirlds.platform.test.fixtures.consensus.framework.validation.ConsensusRoundValidator;
1313
import com.swirlds.platform.test.fixtures.event.generator.StandardGraphGenerator;
1414
import com.swirlds.platform.test.fixtures.event.source.EventSource;
1515
import com.swirlds.platform.test.fixtures.event.source.StandardEventSource;
1616
import com.swirlds.platform.test.fixtures.graph.OtherParentMatrixFactory;
17-
import java.util.Iterator;
17+
import java.util.Arrays;
1818
import java.util.LinkedList;
1919
import java.util.List;
2020
import java.util.stream.Stream;
21-
import org.hiero.consensus.model.event.EventConstants;
21+
import org.hiero.base.crypto.Hash;
22+
import org.hiero.consensus.config.EventConfig_;
23+
import org.hiero.consensus.model.event.PlatformEvent;
2224
import org.hiero.consensus.model.hashgraph.ConsensusRound;
23-
import org.junit.jupiter.api.Test;
25+
import org.hiero.consensus.model.hashgraph.EventWindow;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.ValueSource;
28+
29+
class AncientParentsTest {
30+
31+
public static final int FIRST_BATCH_SIZE = 5000;
32+
public static final int SECOND_BATCH_SIZE = 1000;
2433

25-
class IntakeAndConsensusTests {
2634
/**
27-
* Reproduces #5635
35+
* Tests the scenario where some stale events are added to different nodes at different points in consensus time.
36+
* Depending on the point in consensus time, an event might have ancient parents on one node, but not on the other.
37+
* Both nodes should have the same consensus output.
2838
* <p>
29-
* This test creates a graph with two partitions, where one partition is small enough that it is not needed for
39+
* This test creates a graph with two partitions, where one partition is small enough that it is not required for
3040
* consensus. Because the small partition does not affect consensus, we can delay inserting those events and still
3141
* reach consensus. We delay adding the small partition events until the first of these events becomes ancient. This
3242
* would lead to at least one subsequent small partition event being non-ancient, but not having only ancient
@@ -35,26 +45,24 @@ class IntakeAndConsensusTests {
3545
* that new events will be descendants of some small partition events. This means that the small partition events
3646
* will now be needed for consensus. If the small partition events are not inserted into one of the nodes correctly,
3747
* it will not be able to reach consensus.
38-
* <p>
39-
* Tests the workaround described in #5762
4048
*/
41-
@Test
42-
void nonAncientEventWithMissingParents() {
49+
@ParameterizedTest
50+
@ValueSource(booleans = {true, false})
51+
void nonAncientEventWithMissingParents(final boolean useBirthRounds) {
4352
final long seed = 0;
4453
final int numNodes = 10;
4554
final List<Integer> partitionNodes = List.of(0, 1);
4655

4756
final Configuration configuration = new TestConfigBuilder()
4857
.withValue(ConsensusConfig_.ROUNDS_NON_ANCIENT, 25)
4958
.withValue(ConsensusConfig_.ROUNDS_EXPIRED, 25)
59+
.withValue(EventConfig_.USE_BIRTH_ROUND_ANCIENT_THRESHOLD, Boolean.toString(useBirthRounds))
5060
.getOrCreateConfig();
5161

5262
final PlatformContext platformContext = TestPlatformContextBuilder.create()
5363
.withConfiguration(configuration)
5464
.build();
5565

56-
// the generated events are first fed into consensus so that round created is calculated before we start
57-
// using them
5866
final List<EventSource> eventSources = Stream.generate(StandardEventSource::new)
5967
.map(ses -> (EventSource) ses)
6068
.limit(numNodes)
@@ -63,10 +71,8 @@ void nonAncientEventWithMissingParents() {
6371
final TestIntake node1 = new TestIntake(platformContext, generator.getRoster());
6472
final TestIntake node2 = new TestIntake(platformContext, generator.getRoster());
6573

66-
// first we generate events regularly, until we have some ancient rounds
67-
final int firstBatchSize = 5000;
68-
List<EventImpl> batch = generator.generateEvents(firstBatchSize);
69-
for (final EventImpl event : batch) {
74+
// first, we generate events regularly, until we have some ancient rounds
75+
for (final EventImpl event : generator.generateEvents(FIRST_BATCH_SIZE)) {
7076
node1.addEvent(event.getBaseEvent().copyGossipedData());
7177
node2.addEvent(event.getBaseEvent().copyGossipedData());
7278
}
@@ -80,24 +86,31 @@ void nonAncientEventWithMissingParents() {
8086
// during the partition, we will not insert the minority partition events into consensus
8187
// we generate just enough events to make the first event of the partition ancient, but we don't insert the
8288
// last event into the second consensus
83-
long partitionMinGen = EventConstants.GENERATION_UNDEFINED;
84-
long partitionMaxGen = EventConstants.GENERATION_UNDEFINED;
8589
final List<EventImpl> partitionedEvents = new LinkedList<>();
8690
boolean succeeded = false;
8791
EventImpl lastEvent = null;
92+
EventImpl firstEventInPartition = null;
8893
while (!succeeded) {
89-
batch = generator.generateEvents(1);
90-
lastEvent = batch.getFirst();
94+
lastEvent = generator.generateEvents(1).getFirst();
9195
if (partitionNodes.contains((int) lastEvent.getCreatorId().id())) {
92-
partitionMinGen = partitionMinGen == EventConstants.GENERATION_UNDEFINED
93-
? lastEvent.getGeneration()
94-
: Math.min(partitionMinGen, lastEvent.getGeneration());
95-
partitionMaxGen = Math.max(partitionMaxGen, lastEvent.getGeneration());
96+
// we have generated an event in the minority partition
97+
98+
if (firstEventInPartition == null) {
99+
// this is the first event in the partition
100+
firstEventInPartition = lastEvent;
101+
}
102+
103+
// we don't add these events to consensus yet, we will add them later
96104
partitionedEvents.add(lastEvent);
97105
} else {
106+
// this is an event in the majority partition
107+
// we add it to node 1 always
98108
node1.addEvent(lastEvent.getBaseEvent().copyGossipedData());
99-
final long node1NonAncGen = node1.getOutput().getEventWindow().getAncientThreshold();
100-
if (partitionMaxGen > node1NonAncGen && partitionMinGen < node1NonAncGen) {
109+
110+
// if this event caused the first event in the partition to become ancient, then we exit this loop.
111+
// we will add this event to node 2 later, after we add the partitioned events
112+
final EventWindow node1Window = node1.getOutput().getEventWindow();
113+
if (firstEventInPartition != null && node1Window.isAncient(firstEventInPartition.getBaseEvent())) {
101114
succeeded = true;
102115
} else {
103116
node2.addEvent(lastEvent.getBaseEvent().copyGossipedData());
@@ -115,32 +128,47 @@ void nonAncientEventWithMissingParents() {
115128
node2.addEvent(lastEvent.getBaseEvent().copyGossipedData());
116129
final long consRoundBeforeLastBatch =
117130
node1.getConsensusRounds().getLast().getRoundNum();
131+
// we wanted the first event in the partition to become ancient, so it should never reach consensus
132+
assertEventDidNotReachConsensus(firstEventInPartition, node1, node2);
118133
assertConsensusEvents(node1, node2);
119134

120135
// now the partitions rejoin
121136
generator.setOtherParentAffinity(OtherParentMatrixFactory.createBalancedOtherParentMatrix(numNodes));
122137

123138
// now we generate more events and expect consensus to be the same
124-
final int secondBatchSize = 1000;
125-
batch = generator.generateEvents(secondBatchSize);
126-
for (final EventImpl event : batch) {
139+
for (final EventImpl event : generator.generateEvents(SECOND_BATCH_SIZE)) {
127140
node1.addEvent(event.getBaseEvent().copyGossipedData());
128141
node2.addEvent(event.getBaseEvent().copyGossipedData());
129142
}
130143
assertThat(node1.getConsensusRounds().getLast().getRoundNum())
131-
.isGreaterThan(consRoundBeforeLastBatch)
132-
.withFailMessage("consensus did not advance after the partition rejoined");
144+
.withFailMessage("consensus did not advance after the partition rejoined")
145+
.isGreaterThan(consRoundBeforeLastBatch);
146+
assertEventDidNotReachConsensus(firstEventInPartition, node1, node2);
133147
assertConsensusEvents(node1, node2);
134148
}
135149

136-
private static void assertConsensusEvents(final TestIntake node1, final TestIntake node2) {
137-
final RoundInternalEqualityValidation roundInternalEqualityValidation = new RoundInternalEqualityValidation();
150+
/**
151+
* Assert that the supplied event did not reach consensus in any of the nodes.
152+
*
153+
* @param event the event to check
154+
* @param nodes the nodes to check
155+
*/
156+
private static void assertEventDidNotReachConsensus(final EventImpl event, final TestIntake... nodes) {
157+
final Hash eventHash = event.getBaseHash();
158+
final boolean found = Arrays.stream(nodes)
159+
.map(TestIntake::getConsensusRounds)
160+
.flatMap(List::stream)
161+
.map(ConsensusRound::getConsensusEvents)
162+
.flatMap(List::stream)
163+
.map(PlatformEvent::getHash)
164+
.anyMatch(eventHash::equals);
165+
assertThat(found)
166+
.withFailMessage("Event was not supposed to reach consensus, but it did")
167+
.isFalse();
168+
}
138169

139-
final Iterator<ConsensusRound> iterator1 = node1.getConsensusRounds().iterator();
140-
final Iterator<ConsensusRound> iterator2 = node2.getConsensusRounds().iterator();
141-
while (iterator1.hasNext() && iterator2.hasNext()) {
142-
roundInternalEqualityValidation.validate(iterator1.next(), iterator2.next());
143-
}
170+
private static void assertConsensusEvents(final TestIntake node1, final TestIntake node2) {
171+
new ConsensusRoundValidator().validate(node1.getConsensusRounds(), node2.getConsensusRounds());
144172
node1.getConsensusRounds().clear();
145173
node2.getConsensusRounds().clear();
146174
}

0 commit comments

Comments
 (0)