Skip to content

Commit 18e1107

Browse files
authored
chore: Add an API to submit Otter transactions to individual nodes (#21951)
Signed-off-by: Kelly Greco <[email protected]>
1 parent b2e262b commit 18e1107

File tree

8 files changed

+66
-33
lines changed

8 files changed

+66
-33
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static java.util.Objects.requireNonNull;
88
import static org.hiero.otter.fixtures.internal.helpers.Utils.createConfiguration;
99

10+
import com.google.protobuf.ByteString;
1011
import com.google.protobuf.Empty;
1112
import com.hedera.hapi.node.base.SemanticVersion;
1213
import com.hedera.hapi.node.state.roster.Roster;
@@ -205,10 +206,15 @@ public synchronized void submitTransaction(
205206
}
206207

207208
wrapWithErrorHandling(responseObserver, () -> {
208-
final boolean result =
209-
consensusNodeManager.submitTransaction(request.getPayload().toByteArray());
210-
responseObserver.onNext(
211-
TransactionRequestAnswer.newBuilder().setResult(result).build());
209+
int numFailed = 0;
210+
for (final ByteString payload : request.getPayloadList()) {
211+
if (!consensusNodeManager.submitTransaction(payload.toByteArray())) {
212+
numFailed++;
213+
}
214+
}
215+
responseObserver.onNext(TransactionRequestAnswer.newBuilder()
216+
.setNumFailed(numFailed)
217+
.build());
212218
responseObserver.onCompleted();
213219
});
214220
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Set;
1616
import org.hiero.consensus.model.quiescence.QuiescenceCommand;
1717
import org.hiero.consensus.model.status.PlatformStatus;
18+
import org.hiero.otter.fixtures.app.OtterTransaction;
1819
import org.hiero.otter.fixtures.internal.helpers.Utils;
1920
import org.hiero.otter.fixtures.network.Partition;
2021
import org.hiero.otter.fixtures.network.Topology;
@@ -281,6 +282,22 @@ default Partition createNetworkPartition(@NonNull final Node node0, @NonNull fin
281282
*/
282283
void freeze();
283284

285+
/**
286+
* Submits a single transaction to the first active node found in the network.
287+
*
288+
* @param transaction the transaction to submit
289+
*/
290+
default void submitTransaction(@NonNull final OtterTransaction transaction) {
291+
submitTransactions(List.of(transaction));
292+
}
293+
294+
/**
295+
* Submits the transactions to the first active node found in the network.
296+
*
297+
* @param transactions the transactions to submit
298+
*/
299+
void submitTransactions(@NonNull List<OtterTransaction> transactions);
300+
284301
/**
285302
* Triggers a catastrophic ISS. All nodes in the network will calculate different hashes for an upcoming round.
286303
*/

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
import edu.umd.cs.findbugs.annotations.Nullable;
77
import java.nio.file.Path;
88
import java.time.Duration;
9+
import java.util.List;
910
import org.hiero.consensus.model.node.KeysAndCerts;
1011
import org.hiero.consensus.model.node.NodeId;
1112
import org.hiero.consensus.model.quiescence.QuiescenceCommand;
1213
import org.hiero.consensus.model.status.PlatformStatus;
14+
import org.hiero.otter.fixtures.app.OtterTransaction;
1315
import org.hiero.otter.fixtures.result.SingleNodeConsensusResult;
1416
import org.hiero.otter.fixtures.result.SingleNodeEventStreamResult;
1517
import org.hiero.otter.fixtures.result.SingleNodeLogResult;
@@ -102,7 +104,23 @@ default void startSyntheticBottleneck() {
102104
void sendQuiescenceCommand(@NonNull QuiescenceCommand command);
103105

104106
/**
105-
* Allows to override the default timeout for node operations.
107+
* Submits a transaction to the node.
108+
*
109+
* @param transaction the transaction to submit
110+
*/
111+
default void submitTransaction(@NonNull OtterTransaction transaction) {
112+
submitTransactions(List.of(transaction));
113+
}
114+
115+
/**
116+
* Submits transactions to the node.
117+
*
118+
* @param transactions the list of transactions to submit
119+
*/
120+
void submitTransactions(@NonNull List<OtterTransaction> transactions);
121+
122+
/**
123+
* Overrides the default timeout for node operations.
106124
*
107125
* @param timeout the duration to wait before considering the operation as failed
108126
* @return an instance of {@link AsyncNodeActions} that can be used to perform node actions

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.security.SecureRandom;
3535
import java.time.Duration;
3636
import java.time.Instant;
37+
import java.util.List;
3738
import java.util.Random;
3839
import java.util.concurrent.BlockingQueue;
3940
import java.util.concurrent.LinkedBlockingQueue;
@@ -329,22 +330,21 @@ protected void doSendQuiescenceCommand(@NonNull final QuiescenceCommand command,
329330
* {@inheritDoc}
330331
*/
331332
@Override
332-
public void submitTransaction(@NonNull final OtterTransaction transaction) {
333+
public void submitTransactions(@NonNull final List<OtterTransaction> transactions) {
333334
throwIfInLifecycle(INIT, "Node has not been started yet.");
334335
throwIfInLifecycle(SHUTDOWN, "Node has been shut down.");
335336
throwIfInLifecycle(DESTROYED, "Node has been destroyed.");
336337

337338
try {
338-
final TransactionRequest request = TransactionRequest.newBuilder()
339-
.setPayload(transaction.toByteString())
340-
.build();
341-
342-
final TransactionRequestAnswer answer = nodeCommBlockingStub.submitTransaction(request);
343-
if (!answer.getResult()) {
344-
fail("Failed to submit transaction for node %d.".formatted(selfId.id()));
339+
final TransactionRequest.Builder builder = TransactionRequest.newBuilder();
340+
transactions.forEach(t -> builder.addPayload(t.toByteString()));
341+
final TransactionRequestAnswer answer = nodeCommBlockingStub.submitTransaction(builder.build());
342+
if (answer.getNumFailed() > 0) {
343+
fail("%d out of %d transaction(s) failed to submit for node %d."
344+
.formatted(answer.getNumFailed(), transactions.size(), selfId.id()));
345345
}
346346
} catch (final Exception e) {
347-
fail("Failed to submit transaction to node %d".formatted(selfId.id()), e);
347+
fail("Failed to submit transaction(s) to node %d".formatted(selfId.id()), e);
348348
}
349349
}
350350

@@ -372,8 +372,8 @@ public ContainerNodeConfiguration configuration() {
372372
}
373373

374374
/**
375-
* Gets the container instance for this node. This allows direct access to the underlying
376-
* Testcontainers container for operations like retrieving console logs.
375+
* Gets the container instance for this node. This allows direct access to the underlying Testcontainers container
376+
* for operations like retrieving console logs.
377377
*
378378
* @return the container instance
379379
*/

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -596,17 +596,15 @@ private boolean allNodesInCheckingOrCatastrophicFailure() {
596596
}
597597

598598
/**
599-
* Submits the transaction to the first active node found in the network.
600-
*
601-
* @param transaction the transaction to submit
599+
* {@inheritDoc}
602600
*/
603-
private void submitTransaction(@NonNull final OtterTransaction transaction) {
601+
public void submitTransactions(@NonNull final List<OtterTransaction> transactions) {
604602
nodes().stream()
605603
.filter(Node::isActive)
606604
.findFirst()
607605
.map(node -> (AbstractNode) node)
608606
.orElseThrow(() -> new AssertionError("No active node found to send transaction to."))
609-
.submitTransaction(transaction);
607+
.submitTransactions(transactions);
610608
}
611609

612610
/**

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,6 @@ public void killImmediately() {
293293
*/
294294
protected abstract void doKillImmediately(@NonNull Duration timeout);
295295

296-
/**
297-
* Submit a transaction to the node.
298-
*
299-
* @param transaction the transaction to submit
300-
*/
301-
protected abstract void submitTransaction(@NonNull OtterTransaction transaction);
302-
303296
/**
304297
* {@inheritDoc}
305298
*/

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.nio.file.Path;
4242
import java.time.Duration;
4343
import java.time.Instant;
44+
import java.util.List;
4445
import java.util.Objects;
4546
import java.util.Random;
4647
import java.util.function.Consumer;
@@ -371,7 +372,7 @@ protected void doSendQuiescenceCommand(@NonNull final QuiescenceCommand command,
371372
* {@inheritDoc}
372373
*/
373374
@Override
374-
public void submitTransaction(@NonNull final OtterTransaction transaction) {
375+
public void submitTransactions(@NonNull final List<OtterTransaction> transactions) {
375376
try (final LoggingContextScope ignored = installNodeContext()) {
376377
throwIsNotInLifecycle(RUNNING, "Cannot submit transaction when the network is not running.");
377378
assert executionLayer != null; // executionLayer must be initialized if lifeCycle is STARTED
@@ -381,7 +382,7 @@ public void submitTransaction(@NonNull final OtterTransaction transaction) {
381382
return;
382383
}
383384

384-
executionLayer.submitApplicationTransaction(transaction.toByteArray());
385+
transactions.forEach(tx -> executionLayer.submitApplicationTransaction(tx.toByteArray()));
385386
}
386387
}
387388

platform-sdk/consensus-otter-tests/src/testFixtures/proto/node_communication.proto

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,13 @@ message EventMessage {
177177
// Wrapper for a transaction submission request.
178178
message TransactionRequest {
179179
// Serialized transaction data.
180-
bytes payload = 1;
180+
repeated bytes payload = 1;
181181
}
182182

183183
// Response to a transaction submission request.
184184
message TransactionRequestAnswer {
185-
// indicator if the platform accepted the transaction
186-
bool result = 1;
185+
// The number of transactions that failed submission to the node
186+
uint32 numFailed = 1;
187187
}
188188

189189
// Request to set the synthetic bottleneck.

0 commit comments

Comments
 (0)