Skip to content

Commit 18cc64c

Browse files
netopyrpoulok
andauthored
chore: Add check for prehandle-handle consistency (#21285)
Signed-off-by: Michael Heinrichs <[email protected]> Signed-off-by: Kelly Greco <[email protected]> Co-authored-by: Kelly Greco <[email protected]> Co-authored-by: Kelly Greco <[email protected]>
1 parent 06a3c78 commit 18cc64c

File tree

16 files changed

+187
-65
lines changed

16 files changed

+187
-65
lines changed

platform-sdk/consensus-otter-docker-app/src/testFixtures/java/org/hiero/consensus/otter/docker/app/platform/ConsensusNodeManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import edu.umd.cs.findbugs.annotations.Nullable;
3737
import java.nio.file.Path;
3838
import java.util.List;
39+
import java.util.Random;
3940
import java.util.concurrent.CopyOnWriteArrayList;
4041
import java.util.concurrent.Executor;
4142
import org.apache.logging.log4j.LogManager;
@@ -135,7 +136,7 @@ public ConsensusNodeManager(
135136
final MerkleNodeState state = initialState.get().getState();
136137

137138
final RosterHistory rosterHistory = RosterUtils.createRosterHistory(state);
138-
executionCallback = new OtterExecutionLayer(metrics);
139+
executionCallback = new OtterExecutionLayer(new Random(), metrics);
139140
final PlatformBuilder builder = PlatformBuilder.create(
140141
OtterApp.APP_NAME,
141142
OtterApp.SWIRLD_NAME,

platform-sdk/consensus-otter-tests/docs/turtle-environment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ void testDeterministicBehavior(@NonNull final TestEnvironment env) throws Interr
368368
network.newConsensusResults().results().getFirst().lastRoundNum();
369369

370370
// This assertion will always pass with seed=42
371-
assertThat(lastRound).isEqualTo(46L);
371+
assertThat(lastRound).isEqualTo(47L);
372372
}
373373
```
374374

platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/TransactionFactory.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,51 @@ private TransactionFactory() {}
2222
/**
2323
* Creates a new empty transaction.
2424
*
25+
* @param nonce the nonce for the empty transaction
2526
* @return an empty transaction
2627
*/
27-
public static OtterTransaction createEmptyTransaction(final int nonce) {
28+
public static OtterTransaction createEmptyTransaction(final long nonce) {
2829
final EmptyTransaction emptyTransaction = EmptyTransaction.newBuilder().build();
2930
return OtterTransaction.newBuilder()
31+
.setNonce(nonce)
3032
.setEmptyTransaction(emptyTransaction)
3133
.build();
3234
}
3335

3436
/**
3537
* Creates a freeze transaction with the specified freeze time.
3638
*
39+
* @param nonce the nonce for the transaction
3740
* @param freezeTime the freeze time for the transaction
3841
* @return a FreezeTransaction with the provided freeze time
3942
*/
40-
public static OtterTransaction createFreezeTransaction(@NonNull final Instant freezeTime) {
43+
public static OtterTransaction createFreezeTransaction(final long nonce, @NonNull final Instant freezeTime) {
4144
final Timestamp timestamp = CommonPbjConverters.fromPbj(CommonUtils.toPbjTimestamp(freezeTime));
4245
final OtterFreezeTransaction freezeTransaction =
4346
OtterFreezeTransaction.newBuilder().setFreezeTime(timestamp).build();
4447
return OtterTransaction.newBuilder()
48+
.setNonce(nonce)
4549
.setFreezeTransaction(freezeTransaction)
4650
.build();
4751
}
4852

4953
/**
5054
* Creates a transaction with the specified inner StateSignatureTransaction.
5155
*
56+
* @param nonce the nonce for the transaction
5257
* @param innerTxn the StateSignatureTransaction
5358
* @return a TurtleTransaction with the specified inner transaction
5459
*/
55-
public static OtterTransaction createStateSignatureTransaction(@NonNull final StateSignatureTransaction innerTxn) {
60+
public static OtterTransaction createStateSignatureTransaction(
61+
final long nonce, @NonNull final StateSignatureTransaction innerTxn) {
5662
final com.hedera.hapi.platform.event.legacy.StateSignatureTransaction legacyInnerTxn =
5763
com.hedera.hapi.platform.event.legacy.StateSignatureTransaction.newBuilder()
5864
.setRound(innerTxn.round())
5965
.setSignature(ByteString.copyFrom(innerTxn.signature().toByteArray()))
6066
.setHash(ByteString.copyFrom(innerTxn.hash().toByteArray()))
6167
.build();
6268
return OtterTransaction.newBuilder()
69+
.setNonce(nonce)
6370
.setStateSignatureTransaction(legacyInnerTxn)
6471
.build();
6572
}

platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterApp.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,34 @@
1111
import com.swirlds.platform.system.Platform;
1212
import edu.umd.cs.findbugs.annotations.NonNull;
1313
import edu.umd.cs.findbugs.annotations.Nullable;
14+
import java.io.IOException;
1415
import java.util.Iterator;
1516
import java.util.List;
1617
import java.util.concurrent.atomic.AtomicLong;
1718
import java.util.function.Consumer;
19+
import org.apache.logging.log4j.LogManager;
20+
import org.apache.logging.log4j.Logger;
1821
import org.hiero.consensus.model.event.ConsensusEvent;
1922
import org.hiero.consensus.model.event.Event;
2023
import org.hiero.consensus.model.hashgraph.Round;
2124
import org.hiero.consensus.model.roster.AddressBook;
2225
import org.hiero.consensus.model.transaction.ConsensusTransaction;
2326
import org.hiero.consensus.model.transaction.ScopedSystemTransaction;
27+
import org.hiero.consensus.model.transaction.Transaction;
2428
import org.hiero.otter.fixtures.app.services.consistency.ConsistencyService;
2529
import org.hiero.otter.fixtures.app.services.platform.PlatformStateService;
2630
import org.hiero.otter.fixtures.app.services.roster.RosterService;
2731
import org.hiero.otter.fixtures.app.state.OtterStateInitializer;
2832

2933
/**
30-
* The main entry point for the Otter application. This class is instantiated by the platform when the
31-
* application is started. It creates the services that make up the application and routes events and rounds
32-
* to those services.
34+
* The main entry point for the Otter application. This class is instantiated by the platform when the application is
35+
* started. It creates the services that make up the application and routes events and rounds to those services.
3336
*/
3437
@SuppressWarnings("removal")
3538
public class OtterApp implements ConsensusStateEventHandler<OtterAppState> {
3639

40+
private static final Logger log = LogManager.getLogger();
41+
3742
public static final String APP_NAME = "org.hiero.otter.fixtures.app.OtterApp";
3843
public static final String SWIRLD_NAME = "123";
3944

@@ -91,7 +96,24 @@ public void onPreHandle(
9196
@NonNull final Event event,
9297
@NonNull final OtterAppState state,
9398
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> callback) {
94-
consistencyService.recordPreHandleTransactions(event);
99+
for (final OtterService service : allServices) {
100+
service.preHandleEvent(event);
101+
}
102+
for (final Iterator<Transaction> transactionIterator = event.transactionIterator();
103+
transactionIterator.hasNext(); ) {
104+
try {
105+
final OtterTransaction transaction = OtterTransaction.parseFrom(
106+
transactionIterator.next().getApplicationTransaction().toInputStream());
107+
for (final OtterService service : allServices) {
108+
service.preHandleTransaction(event, transaction, callback);
109+
}
110+
} catch (final IOException ex) {
111+
log.error(
112+
"Unable to parse OtterTransaction created by node {}",
113+
event.getCreatorId().id(),
114+
ex);
115+
}
116+
}
95117
}
96118

97119
/**
@@ -103,20 +125,30 @@ public void onHandleConsensusRound(
103125
@NonNull final OtterAppState state,
104126
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> callback) {
105127
for (final OtterService service : allServices) {
106-
service.onRound(state.getWritableStates(service.name()), round);
128+
service.handleRound(state.getWritableStates(service.name()), round);
107129
}
108130

109131
for (final ConsensusEvent consensusEvent : round) {
110132
for (final OtterService service : allServices) {
111-
service.onEvent(state.getWritableStates(service.name()), consensusEvent);
133+
service.handleEvent(state.getWritableStates(service.name()), consensusEvent);
112134
}
113135
for (final Iterator<ConsensusTransaction> transactionIterator =
114136
consensusEvent.consensusTransactionIterator();
115137
transactionIterator.hasNext(); ) {
116-
final ConsensusTransaction transaction = transactionIterator.next();
117-
for (final OtterService service : allServices) {
118-
service.onTransaction(
119-
state.getWritableStates(service.name()), consensusEvent, transaction, callback);
138+
try {
139+
final OtterTransaction transaction = OtterTransaction.parseFrom(transactionIterator
140+
.next()
141+
.getApplicationTransaction()
142+
.toInputStream());
143+
for (final OtterService service : allServices) {
144+
service.handleTransaction(
145+
state.getWritableStates(service.name()), consensusEvent, transaction, callback);
146+
}
147+
} catch (final IOException ex) {
148+
log.error(
149+
"Unable to parse OtterTransaction created by node {}",
150+
consensusEvent.getCreatorId().id(),
151+
ex);
120152
}
121153
}
122154
}

platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterExecutionLayer.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package org.hiero.otter.fixtures.app;
33

4+
import static java.util.Objects.requireNonNull;
5+
46
import com.hedera.hapi.platform.event.StateSignatureTransaction;
57
import com.hedera.pbj.runtime.io.buffer.Bytes;
68
import com.swirlds.metrics.api.Metrics;
79
import com.swirlds.platform.builder.ExecutionLayer;
810
import edu.umd.cs.findbugs.annotations.NonNull;
911
import java.time.Duration;
1012
import java.util.List;
13+
import java.util.Random;
1114
import org.hiero.consensus.model.status.PlatformStatus;
1215
import org.hiero.consensus.transaction.TransactionPoolNexus;
1316
import org.hiero.otter.fixtures.TransactionFactory;
@@ -22,14 +25,27 @@ public class OtterExecutionLayer implements ExecutionLayer {
2225
/** the transaction pool, stores transactions that should be sumbitted to the network */
2326
private final TransactionPoolNexus transactionPool;
2427

25-
public OtterExecutionLayer(@NonNull final Metrics metrics) {
28+
private final Random random;
29+
30+
/**
31+
* Constructs a new OtterExecutionLayer.
32+
*
33+
* @param random the source of randomness for populating signature transaction nonce values.
34+
* @param metrics the metrics system to use
35+
*/
36+
public OtterExecutionLayer(@NonNull final Random random, @NonNull final Metrics metrics) {
37+
this.random = requireNonNull(random);
2638
transactionPool = new TransactionPoolNexus(getTransactionLimits(), TX_QUEUE_SIZE, metrics);
2739
}
2840

41+
/**
42+
* {@inheritDoc}
43+
*/
2944
@Override
3045
public void submitStateSignature(@NonNull final StateSignatureTransaction transaction) {
31-
transactionPool.submitPriorityTransaction(Bytes.wrap(
32-
TransactionFactory.createStateSignatureTransaction(transaction).toByteArray()));
46+
transactionPool.submitPriorityTransaction(
47+
Bytes.wrap(TransactionFactory.createStateSignatureTransaction(random.nextLong(), transaction)
48+
.toByteArray()));
3349
}
3450

3551
/**
@@ -41,22 +57,34 @@ public boolean submitApplicationTransaction(@NonNull final byte[] transaction) {
4157
return transactionPool.submitApplicationTransaction(Bytes.wrap(transaction));
4258
}
4359

60+
/**
61+
* {@inheritDoc}
62+
*/
4463
@NonNull
4564
@Override
4665
public List<Bytes> getTransactionsForEvent() {
4766
return transactionPool.getTransactionsForEvent();
4867
}
4968

69+
/**
70+
* {@inheritDoc}
71+
*/
5072
@Override
5173
public boolean hasBufferedSignatureTransactions() {
5274
return transactionPool.hasBufferedSignatureTransactions();
5375
}
5476

77+
/**
78+
* {@inheritDoc}
79+
*/
5580
@Override
5681
public void newPlatformStatus(@NonNull final PlatformStatus platformStatus) {
5782
transactionPool.updatePlatformStatus(platformStatus);
5883
}
5984

85+
/**
86+
* {@inheritDoc}
87+
*/
6088
@Override
6189
public void reportUnhealthyDuration(@NonNull final Duration duration) {
6290
transactionPool.reportUnhealthyDuration(duration);

platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterService.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,26 @@ public interface OtterService {
3535
Schema genesisSchema(@NonNull SemanticVersion version);
3636

3737
/**
38-
* Called when a new round of consensus has been received. The service should only do actions
39-
* for the whole round in this method. For actions on individual events, use {@link #onEvent(WritableStates, Event)}.
40-
* For actions on individual transactions, use {@link #onTransaction(WritableStates, Event, Transaction, Consumer)}.
38+
* Called when a new round of consensus has been received. The service should only do actions for the whole round in
39+
* this method. For actions on individual events, use {@link #handleEvent(WritableStates, Event)}. For actions on
40+
* individual transactions, use {@link #handleTransaction(WritableStates, Event, Transaction, Consumer)}.
4141
*
4242
* @param writableStates the {@link WritableStates} to use to modify state
43-
* @param round
43+
* @param round the round to handle
4444
*/
45-
default void onRound(@NonNull final WritableStates writableStates, @NonNull final Round round) {
45+
default void handleRound(@NonNull final WritableStates writableStates, @NonNull final Round round) {
4646
// Default implementation does nothing
4747
}
4848

4949
/**
5050
* Called when a new event has been received. The service should only do actions for the whole event in this method.
51-
* For actions on individual transactions, use {@link #onTransaction(WritableStates, Event, Transaction, Consumer)}.
51+
* For actions on individual transactions, use
52+
* {@link #handleTransaction(WritableStates, Event, OtterTransaction, Consumer)}.
5253
*
5354
* @param writableStates the {@link WritableStates} to use to modify state
5455
* @param event the event to handle
5556
*/
56-
default void onEvent(@NonNull final WritableStates writableStates, @NonNull final Event event) {
57+
default void handleEvent(@NonNull final WritableStates writableStates, @NonNull final Event event) {
5758
// Default implementation does nothing
5859
}
5960

@@ -65,10 +66,35 @@ default void onEvent(@NonNull final WritableStates writableStates, @NonNull fina
6566
* @param transaction the transaction to handle
6667
* @param callback a callback to pass any system transactions to be handled by the platform
6768
*/
68-
default void onTransaction(
69+
default void handleTransaction(
6970
@NonNull final WritableStates writableStates,
7071
@NonNull final Event event,
71-
@NonNull final Transaction transaction,
72+
@NonNull final OtterTransaction transaction,
73+
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> callback) {
74+
// Default implementation does nothing
75+
}
76+
77+
/**
78+
* Called when an event is being pre-handled. This is called before any transactions in the event are pre-handled.
79+
* The service should only do actions for the whole event in this method. For actions on individual transactions,
80+
* use {@link #preHandleTransaction(Event, OtterTransaction, Consumer)}.
81+
*
82+
* @param event the event being pre-handled
83+
*/
84+
default void preHandleEvent(@NonNull final Event event) {
85+
// Default implementation does nothing
86+
}
87+
88+
/**
89+
* Called when a transaction is being pre-handled.
90+
*
91+
* @param event the event that contains the transaction
92+
* @param transaction the transaction being pre-handled
93+
* @param callback a callback to pass any system transactions to be handled by the platform
94+
*/
95+
default void preHandleTransaction(
96+
@NonNull final Event event,
97+
@NonNull final OtterTransaction transaction,
7298
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> callback) {
7399
// Default implementation does nothing
74100
}

0 commit comments

Comments
 (0)