Skip to content

Commit

Permalink
chore: Enable block signing test and add Metrics (#16632)
Browse files Browse the repository at this point in the history
Signed-off-by: Neeharika-Sompalli <[email protected]>
  • Loading branch information
Neeharika-Sompalli authored Nov 19, 2024
1 parent a136f11 commit baa7329
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ public Hedera(
new SignatureExpanderImpl(),
new SignatureVerifierImpl(CryptographyHolder.get())),
this,
bootstrapConfigProvider::getConfiguration,
() -> configProvider.getConfiguration(),
() -> daggerApp.networkInfo().selfNodeInfo());
tssBaseService = tssBaseServiceFactory.apply(appContext);
contractServiceImpl = new ContractServiceImpl(appContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
Expand All @@ -86,14 +85,14 @@ public class TssBaseServiceImpl implements TssBaseService {
private final TssHandlers tssHandlers;
private final TssSubmissions tssSubmissions;
private final Executor tssLibraryExecutor;
private final ExecutorService signingExecutor;
private final Executor signingExecutor;
private final TssKeysAccessor tssKeysAccessor;
private final TssDirectoryAccessor tssDirectoryAccessor;
private final AppContext appContext;

public TssBaseServiceImpl(
@NonNull final AppContext appContext,
@NonNull final ExecutorService signingExecutor,
@NonNull final Executor signingExecutor,
@NonNull final Executor submissionExecutor,
@NonNull final TssLibrary tssLibrary,
@NonNull final Executor tssLibraryExecutor,
Expand Down Expand Up @@ -197,7 +196,6 @@ public void setCandidateRoster(@NonNull final Roster candidateRoster, @NonNull f
@Override
public void requestLedgerSignature(final byte[] messageHash, final Instant lastUsedConsensusTime) {
requireNonNull(messageHash);
// (TSS-FUTURE) Initiate an asynchronous process of creating a ledger signature
final var mockSignature = noThrowSha384HashOf(messageHash);
CompletableFuture.runAsync(
() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ public class TssMetrics {
private static final LongGauge.Config TSS_ROSTER_LIFECYCLE_CONFIG = new LongGauge.Config(
"app", TSS_CANDIDATE_ROSTER_LIFECYCLE)
.withDescription(TSS_CANDIDATE_ROSTER_LIFECYCLE_DESC);

private static final String TSS_LEDGER_SIGNATURE_TIME = "tss_ledger_signature_time";
private static final String TSS_LEDGER_SIGNATURE_TIME_DESC =
"the time it takes to to get ledger signature from the time it is requested";
private static final LongGauge.Config TSS_LEDGER_SIGNATURE_TIME_CONFIG =
new LongGauge.Config("app", TSS_LEDGER_SIGNATURE_TIME).withDescription(TSS_LEDGER_SIGNATURE_TIME_DESC);
private final LongGauge tssLedgerSignatureTime;

private static final String TSS_LEDGER_SIGNATURE_FAILURES_COUNTER = "tss_ledger_signature_failures_counter";
private static final String TSS_LEDGER_SIGNATURE_FAILURES_COUNTER_DESC =
"The number of failures to generate a ledger signature";
final Counter.Config TSS_LEDGER_SIGN_FAILURE_COUNTER = new Counter.Config(
"app", TSS_LEDGER_SIGNATURE_FAILURES_COUNTER)
.withDescription(TSS_LEDGER_SIGNATURE_FAILURES_COUNTER_DESC);
final Counter ledgerSignatureFailuresCounter;

private final LongGauge tssCandidateRosterLifecycle;

// local variable to track the start of candidate roster's lifecycle
Expand All @@ -78,6 +94,8 @@ public TssMetrics(@NonNull final Metrics metrics) {
this.metrics = requireNonNull(metrics, "metrics must not be null");
tssCandidateRosterLifecycle = metrics.getOrCreate(TSS_ROSTER_LIFECYCLE_CONFIG);
tssSharesAggregationTime = metrics.getOrCreate(TSS_SHARES_AGGREGATION_CONFIG);
tssLedgerSignatureTime = metrics.getOrCreate(TSS_LEDGER_SIGNATURE_TIME_CONFIG);
ledgerSignatureFailuresCounter = metrics.getOrCreate(TSS_LEDGER_SIGN_FAILURE_COUNTER);
}

/**
Expand Down Expand Up @@ -156,6 +174,12 @@ public void updateAggregationTime(final long aggregationTime) {
tssSharesAggregationTime.set(aggregationTime);
}
}
/**
* Track the number of consecutive failures to generate a ledger signatures.
*/
public void updateLedgerSignatureFailures() {
ledgerSignatureFailuresCounter.increment();
}

/**
* @param targetRosterHash the {@link Bytes} of the candidate roster
Expand All @@ -175,6 +199,19 @@ public void updateAggregationTime(final long aggregationTime) {
return votesPerCandidateRoster.get(targetRosterHash);
}

/**
* The time it takes to get ledger signature from the time it is requested.
*
* @param time the time it takes to get ledger signature from the time it is requested
*/
public void updateLedgerSignatureTime(final long time) {
if (time < 0) {
log.warn("Received negative signature time: {}", time);
} else {
tssLedgerSignatureTime.set(time);
}
}

/**
* @return the aggregation time from the metric
*/
Expand All @@ -190,4 +227,17 @@ public long getAggregationTime() {
public long getCandidateRosterLifecycle() {
return tssCandidateRosterLifecycle.get();
}

/**
* @return the ledger signature time from the metric
*/
@VisibleForTesting
public long getTssLedgerSignatureTime() {
return tssLedgerSignatureTime.get();
}

@VisibleForTesting
public Counter getLedgerSignatureFailuresCounter() {
return ledgerSignatureFailuresCounter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.hedera.node.app.tss.TssBaseService;
import com.hedera.node.app.tss.TssBaseServiceImpl;
import com.hedera.node.app.tss.TssKeysAccessor;
import com.hedera.node.app.tss.TssMetrics;
import com.hedera.node.app.tss.api.TssLibrary;
import com.hedera.node.app.tss.api.TssShareId;
import com.hedera.node.app.tss.api.TssShareSignature;
Expand All @@ -37,6 +38,7 @@
import com.hedera.pbj.runtime.io.buffer.Bytes;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Map;
Expand All @@ -59,17 +61,20 @@ public class TssShareSignatureHandler implements TransactionHandler {
private final Map<Bytes, Map<Bytes, Set<TssShareSignature>>> signatures = new ConcurrentHashMap<>();
private Instant lastPurgeTime = Instant.EPOCH;
private TssBaseServiceImpl tssBaseService;
private final TssMetrics tssMetrics;

@Inject
public TssShareSignatureHandler(
@NonNull final TssLibrary tssLibrary,
@NonNull final InstantSource instantSource,
@NonNull final TssKeysAccessor rosterKeyMaterialAccessor,
@NonNull final TssBaseService tssBaseService) {
@NonNull final TssBaseService tssBaseService,
final TssMetrics tssMetrics) {
this.tssLibrary = tssLibrary;
this.instantSource = instantSource;
this.rosterKeyMaterialAccessor = rosterKeyMaterialAccessor;
this.tssBaseService = (TssBaseServiceImpl) tssBaseService;
this.tssMetrics = requireNonNull(tssMetrics);
}

@Override
Expand All @@ -94,8 +99,20 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx
// If message hash now has enough signatures to aggregate, do so and notify
// tssBaseService of sign the message hash with ledger signature
if (isThresholdMet(messageHash, rosterHash)) {
final var ledgerSignature = tssLibrary.aggregateSignatures(
tssShareSignatures.stream().toList());
final var aggregationStart = instantSource.instant();
final PairingSignature ledgerSignature;
try {
ledgerSignature = tssLibrary.aggregateSignatures(
tssShareSignatures.stream().toList());
} catch (Exception e) {
// TODO: Need to confirm if this is correct metric we want.
tssMetrics.updateLedgerSignatureFailures();
return;
}
final var aggregationEnd = instantSource.instant();
// Update the time it took to aggregate the signatures and generate ledger signature
tssMetrics.updateLedgerSignatureTime(
Duration.between(aggregationStart, aggregationEnd).toMillis());
tssBaseService.notifySignature(
messageHash.toByteArray(), ledgerSignature.signature().toBytes());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,6 @@ private HandleOutput execute(@NonNull final UserTxn userTxn) {
// (FUTURE) Once all genesis setup is done via dispatch, remove this method
systemSetup.externalizeInitSideEffects(
userTxn.tokenContextImpl(), exchangeRateManager.exchangeRates());
// Set the genesis roster in state
final var rosterStore = writableRosterStoreFactory.getStore(WritableRosterStore.class);
rosterStore.putActiveRoster(networkInfo.roster(), 1L);
} else if (userTxn.type() == POST_UPGRADE_TRANSACTION) {
final var writableStakingInfoStore =
new WritableStakingInfoStore(userTxn.stack().getWritableStates(TokenService.NAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ public void aggregationTimeGetUpdated() {
assertThat(tssMetrics.getAggregationTime()).isEqualTo(aggregationTime);
}

@Test
public void ledgerSignatureTimeGetsUpdated() {
final long aggregationTime = InstantSource.system().instant().getEpochSecond();
tssMetrics.updateLedgerSignatureTime(aggregationTime);
assertThat(tssMetrics.getTssLedgerSignatureTime()).isEqualTo(aggregationTime);
}

@Test
public void ledgerSignatureFailureGetsUpdated() {
tssMetrics.updateLedgerSignatureFailures();
assertThat(tssMetrics.getLedgerSignatureFailuresCounter().get()).isEqualTo(1L);
tssMetrics.updateLedgerSignatureFailures();
assertThat(tssMetrics.getLedgerSignatureFailuresCounter().get()).isEqualTo(2L);
}

@Test
public void candidateRosterLifecycleGetUpdated() {
final Instant candidateRosterLifecycleStart = InstantSource.system().instant();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.hedera.node.app.spi.workflows.PreHandleContext;
import com.hedera.node.app.tss.TssBaseServiceImpl;
import com.hedera.node.app.tss.TssKeysAccessor;
import com.hedera.node.app.tss.TssMetrics;
import com.hedera.node.app.tss.api.TssLibrary;
import com.hedera.node.app.tss.api.TssParticipantDirectory;
import com.hedera.node.app.tss.api.TssPrivateShare;
Expand Down Expand Up @@ -68,6 +69,9 @@ public class TssShareSignatureHandlerTest {
@Mock
private PreHandleContext context;

@Mock
private TssMetrics tssMetrics;

private TssShareSignatureHandler handler;

private static final SignatureSchema SIGNATURE_SCHEMA = SignatureSchema.create(new byte[] {1});
Expand All @@ -89,7 +93,8 @@ public class TssShareSignatureHandlerTest {
void setUp() {
given(rosterKeyMaterialAccessor.accessTssKeys()).willReturn(TSS_KEYS);
given(instantSource.instant()).willReturn(Instant.ofEpochSecond(1_234_567L));
handler = new TssShareSignatureHandler(tssLibrary, instantSource, rosterKeyMaterialAccessor, tssBaseService);
handler = new TssShareSignatureHandler(
tssLibrary, instantSource, rosterKeyMaterialAccessor, tssBaseService, tssMetrics);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ public record NetworkAdminConfig(
@ConfigProperty(defaultValue = "throttles.json") String upgradeThrottlesFile,
@ConfigProperty(defaultValue = "application-override.properties") String upgradePropertyOverridesFile,
@ConfigProperty(defaultValue = "api-permission-override.properties") String upgradePermissionOverridesFile,
@ConfigProperty(defaultValue = "TssMessage,TssVote") @NetworkProperty
@ConfigProperty(defaultValue = "TssMessage,TssVote,TssShareSignature") @NetworkProperty
HederaFunctionalitySet nodeTransactionsAllowList) {}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static com.swirlds.platform.system.transaction.TransactionWrapperUtils.createAppPayloadWrapper;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.SemanticVersion;
import com.hedera.pbj.runtime.io.buffer.BufferedData;
import com.hedera.pbj.runtime.io.buffer.Bytes;
Expand Down Expand Up @@ -93,13 +94,13 @@ public TransactionResponse submit(
new FakeEvent(nodeId, time.now(), semanticVersion, createAppPayloadWrapper(payload));
}
if (response.getNodeTransactionPrecheckCode() == OK) {
handleNextRound();
handleNextRound(false);
// If handling this transaction scheduled TSS work, do it synchronously as well
while (tssBaseService.hasTssSubmission()) {
platform.lastCreatedEvent = null;
tssBaseService.executeNextTssSubmission();
if (platform.lastCreatedEvent != null) {
handleNextRound();
handleNextRound(true);
}
}
}
Expand All @@ -120,8 +121,11 @@ public long lastRoundNo() {
return platform.lastRoundNo();
}

private void handleNextRound() {
private void handleNextRound(boolean skipsSignatureTxn) {
hedera.onPreHandle(platform.lastCreatedEvent, state);
if (skipsSignatureTxn && platform.lastCreatedEvent.function() == HederaFunctionality.TSS_SHARE_SIGNATURE) {
return;
}
final var round = platform.nextConsensusRound();
// Handle each transaction in own round
hedera.handleWorkflow().handleRound(state, round);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@

import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.SemanticVersion;
import com.hedera.hapi.platform.event.EventCore;
import com.hedera.hapi.util.HapiUtils;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.hedera.services.bdd.junit.support.translators.inputs.TransactionParts;
import com.swirlds.common.platform.NodeId;
import com.swirlds.platform.system.events.Event;
import com.swirlds.platform.system.transaction.Transaction;
Expand Down Expand Up @@ -96,4 +98,9 @@ public EventCore getEventCore() {
public Bytes getSignature() {
return FAKE_SHA_384_SIGNATURE;
}

@NonNull
public HederaFunctionality function() {
return TransactionParts.from(transaction.getApplicationTransaction()).function();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public enum Signing {
DELEGATE
}

private Signing signing = Signing.FAKE;
private Signing signing = Signing.DELEGATE;
private boolean ignoreRequests = false;

public FakeTssBaseService(@NonNull final AppContext appContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class FakeTssLibrary implements TssLibrary {
private static final SignatureSchema SIGNATURE_SCHEMA = SignatureSchema.create(new byte[] {1});
private static final PairingPrivateKey PRIVATE_KEY =
new PairingPrivateKey(new FakeFieldElement(BigInteger.valueOf(42L)), SIGNATURE_SCHEMA);
public static final PairingSignature FAKE_SIGNATURE =
new PairingSignature(new FakeGroupElement(BigInteger.valueOf(1L)), SIGNATURE_SCHEMA);

public interface DirectoryAssertion {
void assertExpected(@NonNull TssParticipantDirectory directory) throws AssertionError;
Expand Down Expand Up @@ -181,7 +183,7 @@ public boolean verifySignature(
@NonNull
@Override
public PairingSignature aggregateSignatures(@NonNull final List<TssShareSignature> partialSignatures) {
return new PairingSignature(new FakeGroupElement(BigInteger.valueOf(0L)), SIGNATURE_SCHEMA);
return FAKE_SIGNATURE;
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,24 @@ public static RekeyScenarioOp rekeyingScenario(
@NonNull final RekeyScenarioOp.DabEdits dabEdits,
@NonNull final LongUnaryOperator nodeStakes,
@NonNull final LongFunction<RekeyScenarioOp.TssMessageSim> tssMessageSims) {
return new RekeyScenarioOp(dabEdits, nodeStakes, tssMessageSims);
return new RekeyScenarioOp(
dabEdits, nodeStakes, tssMessageSims, RekeyScenarioOp.BlockSigningType.SIGN_WITH_FAKE);
}

/**
* Returns an operation that simulates a re-keying scenario in the context of a repeatable embedded test.
* @param dabEdits the edits to make before creating the candidate roster
* @param nodeStakes the node stakes to have in place at the stake period boundary
* @param tssMessageSims the TSS message simulations to apply
* @param blockSigningType the type of block signing to perform
* @return the operation that will simulate the re-keying scenario
*/
public static RekeyScenarioOp rekeyingScenario(
@NonNull final RekeyScenarioOp.DabEdits dabEdits,
@NonNull final LongUnaryOperator nodeStakes,
@NonNull final LongFunction<RekeyScenarioOp.TssMessageSim> tssMessageSims,
@NonNull final RekeyScenarioOp.BlockSigningType blockSigningType) {
return new RekeyScenarioOp(dabEdits, nodeStakes, tssMessageSims, blockSigningType);
}

/**
Expand Down
Loading

0 comments on commit baa7329

Please sign in to comment.