Skip to content

Commit

Permalink
chore: Calling StateSignatureTransaction callbacks (#17226)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Heinrichs <[email protected]>
  • Loading branch information
netopyr authored Jan 8, 2025
1 parent 9934e12 commit c26a5a4
Show file tree
Hide file tree
Showing 25 changed files with 514 additions and 92 deletions.
3 changes: 2 additions & 1 deletion hapi/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@
exports com.hedera.hapi.block.stream.protoc;
exports com.hedera.hapi.block;
exports com.hedera.hapi.services.auxiliary.tss.legacy;
exports com.hedera.hapi.platform.event.legacy;

requires transitive com.hedera.pbj.runtime;
requires transitive com.google.common;
requires transitive com.google.protobuf;
requires transitive com.hedera.pbj.runtime;
requires transitive io.grpc.stub;
requires transitive io.grpc;
requires io.grpc.protobuf;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import com.hedera.hapi.node.state.blockstream.BlockStreamInfo;
import com.hedera.hapi.node.transaction.ThrottleDefinitions;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.hapi.platform.event.StateSignatureTransaction;
import com.hedera.hapi.platform.state.PlatformState;
import com.hedera.hapi.util.HapiUtils;
import com.hedera.hapi.util.UnknownHederaFunctionality;
Expand Down Expand Up @@ -128,6 +129,7 @@
import com.swirlds.common.platform.NodeId;
import com.swirlds.config.api.Configuration;
import com.swirlds.metrics.api.Metrics;
import com.swirlds.platform.components.transaction.system.ScopedSystemTransaction;
import com.swirlds.platform.config.AddressBookConfig;
import com.swirlds.platform.listeners.PlatformStatusChangeListener;
import com.swirlds.platform.listeners.PlatformStatusChangeNotification;
Expand Down Expand Up @@ -166,6 +168,7 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -821,7 +824,10 @@ public void shutdown() {
/**
* Invoked by the platform to handle pre-consensus events. This only happens after {@link #run()} has been called.
*/
public void onPreHandle(@NonNull final Event event, @NonNull final State state) {
public void onPreHandle(
@NonNull final Event event,
@NonNull final State state,
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTxnCallback) {
final var readableStoreFactory = new ReadableStoreFactory(state);
final var creator =
daggerApp.networkInfo().nodeInfo(event.getCreatorId().id());
Expand All @@ -837,9 +843,20 @@ public void onPreHandle(@NonNull final Event event, @NonNull final State state)
return;
}

final Consumer<StateSignatureTransaction> simplifiedStateSignatureTxnCallback = txn -> {
final var scopedTxn = new ScopedSystemTransaction<>(event.getCreatorId(), event.getSoftwareVersion(), txn);
stateSignatureTxnCallback.accept(scopedTxn);
};

final var transactions = new ArrayList<Transaction>(1000);
event.forEachTransaction(transactions::add);
daggerApp.preHandleWorkflow().preHandle(readableStoreFactory, creator.accountId(), transactions.stream());
daggerApp
.preHandleWorkflow()
.preHandle(
readableStoreFactory,
creator.accountId(),
transactions.stream(),
simplifiedStateSignatureTxnCallback);
}

public void onNewRecoveredState(@NonNull final State recoveredState) {
Expand All @@ -862,9 +879,12 @@ public static boolean shouldDump(@NonNull final InitTrigger trigger, @NonNull fi
* Invoked by the platform to handle a round of consensus events. This only happens after {@link #run()} has been
* called.
*/
public void onHandleConsensusRound(@NonNull final Round round, @NonNull final State state) {
public void onHandleConsensusRound(
@NonNull final Round round,
@NonNull final State state,
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTxnCallback) {
daggerApp.workingStateAccessor().setState(state);
daggerApp.handleWorkflow().handleRound(state, round);
daggerApp.handleWorkflow().handleRound(state, round, stateSignatureTxnCallback);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

import static java.util.Objects.requireNonNull;

import com.hedera.hapi.platform.event.StateSignatureTransaction;
import com.hedera.node.app.Hedera;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.platform.components.transaction.system.ScopedSystemTransaction;
import com.swirlds.platform.state.StateLifecycles;
import com.swirlds.platform.system.InitTrigger;
import com.swirlds.platform.system.Platform;
Expand All @@ -30,6 +32,7 @@
import com.swirlds.state.State;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.function.Consumer;

/**
* Implements the major lifecycle events for Hedera Services by delegating to a Hedera instance.
Expand All @@ -42,13 +45,19 @@ public StateLifecyclesImpl(@NonNull final Hedera hedera) {
}

@Override
public void onPreHandle(@NonNull final Event event, @NonNull final State state) {
hedera.onPreHandle(event, state);
public void onPreHandle(
@NonNull final Event event,
@NonNull final State state,
@NonNull Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTransactionCallback) {
hedera.onPreHandle(event, state, stateSignatureTransactionCallback);
}

@Override
public void onHandleConsensusRound(@NonNull final Round round, @NonNull final State state) {
hedera.onHandleConsensusRound(round, state);
public void onHandleConsensusRound(
@NonNull final Round round,
@NonNull final State state,
@NonNull Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTxnCallback) {
hedera.onHandleConsensusRound(round, state, stateSignatureTxnCallback);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@
import com.hedera.hapi.block.stream.input.EventHeader;
import com.hedera.hapi.block.stream.input.RoundHeader;
import com.hedera.hapi.block.stream.output.StateChanges;
import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.base.SemanticVersion;
import com.hedera.hapi.node.base.Transaction;
import com.hedera.hapi.node.state.blockrecords.BlockInfo;
import com.hedera.hapi.node.transaction.ExchangeRateSet;
import com.hedera.hapi.platform.event.StateSignatureTransaction;
import com.hedera.hapi.util.HapiUtils;
import com.hedera.node.app.blocks.BlockStreamManager;
import com.hedera.node.app.blocks.impl.BlockStreamBuilder;
Expand Down Expand Up @@ -90,12 +92,14 @@
import com.hedera.node.app.workflows.handle.steps.StakePeriodChanges;
import com.hedera.node.app.workflows.handle.steps.UserTxn;
import com.hedera.node.app.workflows.handle.steps.UserTxnFactory;
import com.hedera.node.app.workflows.prehandle.PreHandleResult;
import com.hedera.node.config.ConfigProvider;
import com.hedera.node.config.data.BlockStreamConfig;
import com.hedera.node.config.data.ConsensusConfig;
import com.hedera.node.config.data.SchedulingConfig;
import com.hedera.node.config.types.StreamMode;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.swirlds.platform.components.transaction.system.ScopedSystemTransaction;
import com.swirlds.platform.system.InitTrigger;
import com.swirlds.platform.system.Round;
import com.swirlds.platform.system.transaction.ConsensusTransaction;
Expand All @@ -109,6 +113,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -215,8 +220,12 @@ public HandleWorkflow(
*
* @param state the writable {@link State} that this round will work on
* @param round the next {@link Round} that needs to be processed
* @param stateSignatureTxnCallback A callback to be called when encountering a {@link StateSignatureTransaction}
*/
public void handleRound(@NonNull final State state, @NonNull final Round round) {
public void handleRound(
@NonNull final State state,
@NonNull final Round round,
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTxnCallback) {
logStartRound(round);
cacheWarmer.warm(state, round);
if (streamMode != RECORDS) {
Expand All @@ -234,7 +243,7 @@ public void handleRound(@NonNull final State state, @NonNull final Round round)
}
recordCache.resetRoundReceipts();
try {
handleEvents(state, round);
handleEvents(state, round, stateSignatureTxnCallback);
} finally {
// Even if there is an exception somewhere, we need to commit the receipts of any handled transactions
// to the state so these transactions cannot be replayed in future rounds
Expand All @@ -248,8 +257,12 @@ public void handleRound(@NonNull final State state, @NonNull final Round round)
*
* @param state the state to apply the effects to
* @param round the round to apply the effects of
* @param stateSignatureTxnCallback A callback to be called when encountering a {@link StateSignatureTransaction}
*/
private void handleEvents(@NonNull final State state, @NonNull final Round round) {
private void handleEvents(
@NonNull final State state,
@NonNull final Round round,
@NonNull final Consumer<ScopedSystemTransaction<StateSignatureTransaction>> stateSignatureTxnCallback) {
boolean userTransactionsHandled = false;
for (final var event : round) {
if (streamMode != RECORDS) {
Expand All @@ -275,6 +288,13 @@ private void handleEvents(@NonNull final State state, @NonNull final Round round
}
continue;
}

final Consumer<StateSignatureTransaction> simplifiedStateSignatureTxnCallback = txn -> {
final var scopedTxn =
new ScopedSystemTransaction<>(event.getCreatorId(), event.getSoftwareVersion(), txn);
stateSignatureTxnCallback.accept(scopedTxn);
};

// log start of event to transaction state log
logStartEvent(event, creator);
// handle each transaction of the event
Expand All @@ -283,8 +303,12 @@ private void handleEvents(@NonNull final State state, @NonNull final Round round
try {
// skip system transactions
if (!platformTxn.isSystem()) {
userTransactionsHandled = true;
handlePlatformTransaction(state, creator, platformTxn, event.getSoftwareVersion());
userTransactionsHandled |= handlePlatformTransaction(
state,
creator,
platformTxn,
event.getSoftwareVersion(),
simplifiedStateSignatureTxnCallback);
}
} catch (final Exception e) {
logger.fatal(
Expand Down Expand Up @@ -316,14 +340,21 @@ private void handleEvents(@NonNull final State state, @NonNull final Round round
* @param creator the {@link NodeInfo} of the creator of the transaction
* @param txn the {@link ConsensusTransaction} to be handled
* @param txnVersion the software version for the event containing the transaction
* @return {@code true} if the transaction was a user transaction, {@code false} if a system transaction
*/
private void handlePlatformTransaction(
private boolean handlePlatformTransaction(
@NonNull final State state,
@NonNull final NodeInfo creator,
@NonNull final ConsensusTransaction txn,
@NonNull final SemanticVersion txnVersion) {
@NonNull final SemanticVersion txnVersion,
@NonNull final Consumer<StateSignatureTransaction> stateSignatureTxnCallback) {
final var handleStart = System.nanoTime();

// Temporary check until we can deprecate StateSignatureTransaction
if (stateSignatureTransactionEncountered(txn, stateSignatureTxnCallback)) {
return false;
}

// Always use platform-assigned time for user transaction, c.f. https://hips.hedera.com/hip/hip-993
final var consensusNow = txn.getConsensusTimestamp();
var type = ORDINARY_TRANSACTION;
Expand Down Expand Up @@ -380,6 +411,19 @@ private void handlePlatformTransaction(
blockStreamManager.setLastIntervalProcessTime(userTxn.consensusNow());
}
}
return true;
}

private boolean stateSignatureTransactionEncountered(
@NonNull final ConsensusTransaction txn,
@NonNull final Consumer<StateSignatureTransaction> stateSignatureTxnCallback) {
if (txn.getMetadata() instanceof PreHandleResult preHandleResult
&& preHandleResult.txInfo() != null
&& preHandleResult.txInfo().functionality() == HederaFunctionality.STATE_SIGNATURE_TRANSACTION) {
stateSignatureTxnCallback.accept(preHandleResult.txInfo().txBody().stateSignatureTransactionOrThrow());
return true;
}
return false;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
* Copyright (C) 2022-2025 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -236,4 +236,13 @@ public static PreHandleResult preHandleFailure(
null,
UNKNOWN_VERSION);
}

/**
* Creates a new {@link PreHandleResult} when encountering a {@link com.hedera.hapi.platform.event.StateSignatureTransaction}.
*/
@NonNull
public static PreHandleResult stateSignatureTransactionEncountered(@NonNull final TransactionInfo txInfo) {
return new PreHandleResult(
null, null, Status.SO_FAR_SO_GOOD, UNKNOWN, txInfo, null, null, null, null, null, UNKNOWN_VERSION);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
* Copyright (C) 2022-2025 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package com.hedera.node.app.workflows.prehandle;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.platform.event.StateSignatureTransaction;
import com.hedera.node.app.service.token.ReadableAccountStore;
import com.hedera.node.app.store.ReadableStoreFactory;
import com.swirlds.platform.system.events.Event;
Expand All @@ -25,6 +26,7 @@
import com.swirlds.state.lifecycle.info.NodeInfo;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -39,12 +41,14 @@ public interface PreHandleWorkflow {
* @param readableStoreFactory the {@link ReadableStoreFactory} that is used for looking up stores
* @param creator The {@link AccountID} of the node that created these transactions
* @param transactions An {@link Stream} over all transactions to pre-handle
* @param stateSignatureTxnCallback A callback to be called when encountering a {@link StateSignatureTransaction}
* @throws NullPointerException if one of the arguments is {@code null}
*/
void preHandle(
@NonNull final ReadableStoreFactory readableStoreFactory,
@NonNull final AccountID creator,
@NonNull final Stream<Transaction> transactions);
@NonNull final Stream<Transaction> transactions,
@NonNull final Consumer<StateSignatureTransaction> stateSignatureTxnCallback);

/**
* A convenience method to start the pre-handle transaction workflow for a single
Expand All @@ -54,14 +58,16 @@ void preHandle(
* @param storeFactory The {@link ReadableStoreFactory} based on the current state
* @param accountStore The {@link ReadableAccountStore} based on the current state
* @param platformTx The {@link Transaction} to pre-handle
* @param stateSignatureTxnCallback A callback to be called when encountering a {@link StateSignatureTransaction}
* @return The {@link PreHandleResult} of running pre-handle
*/
default @NonNull PreHandleResult preHandleTransaction(
@NonNull AccountID creator,
@NonNull ReadableStoreFactory storeFactory,
@NonNull ReadableAccountStore accountStore,
@NonNull Transaction platformTx) {
return preHandleTransaction(creator, storeFactory, accountStore, platformTx, null);
@NonNull Transaction platformTx,
@NonNull Consumer<StateSignatureTransaction> stateSignatureTxnCallback) {
return preHandleTransaction(creator, storeFactory, accountStore, platformTx, null, stateSignatureTxnCallback);
}

/**
Expand All @@ -74,6 +80,7 @@ void preHandle(
* @param accountStore The {@link ReadableAccountStore} based on the current state
* @param platformTx The {@link Transaction} to pre-handle
* @param maybeReusableResult The result of a previous call to the same method that may,
* @param stateSignatureTxnCallback A callback to be called when encountering a {@link StateSignatureTransaction}
* depending on changes in state, be reusable for this call
* @return The {@link PreHandleResult} of running pre-handle
*/
Expand All @@ -83,7 +90,8 @@ PreHandleResult preHandleTransaction(
@NonNull ReadableStoreFactory storeFactory,
@NonNull ReadableAccountStore accountStore,
@NonNull Transaction platformTx,
@Nullable PreHandleResult maybeReusableResult);
@Nullable PreHandleResult maybeReusableResult,
@NonNull Consumer<StateSignatureTransaction> stateSignatureTxnCallback);

/**
* This method gets all the verification data for the current transaction. If pre-handle was previously ran
Expand All @@ -100,7 +108,7 @@ PreHandleResult preHandleTransaction(
default PreHandleResult getCurrentPreHandleResult(
@NonNull final NodeInfo creator,
@NonNull final ConsensusTransaction platformTxn,
final ReadableStoreFactory storeFactory) {
@NonNull final ReadableStoreFactory storeFactory) {
final var metadata = platformTxn.getMetadata();
final PreHandleResult previousResult;
if (metadata instanceof PreHandleResult result) {
Expand All @@ -122,6 +130,7 @@ default PreHandleResult getCurrentPreHandleResult(
storeFactory,
storeFactory.getStore(ReadableAccountStore.class),
platformTxn,
previousResult);
previousResult,
txns -> {});
}
}
Loading

0 comments on commit c26a5a4

Please sign in to comment.