diff --git a/hapi/hedera-protobufs/services/auxiliary/tss/tss_share_signature.proto b/hapi/hedera-protobufs/services/auxiliary/tss/tss_share_signature.proto new file mode 100644 index 000000000000..8ff4b14f1989 --- /dev/null +++ b/hapi/hedera-protobufs/services/auxiliary/tss/tss_share_signature.proto @@ -0,0 +1,88 @@ +/** + * # Tss Share Signature + * Represents a transaction that submits a node's share signature on a block hash + * during the TSS (Threshold Signature Scheme) process. + * + * ### Keywords + * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + * document are to be interpreted as described in + * [RFC2119](https://www.ietf.org/rfc/rfc2119) and clarified in + * [RFC8174](https://www.ietf.org/rfc/rfc8174). + */ +syntax = "proto3"; + +package com.hedera.hapi.services.auxiliary.tss; + +/* + * Copyright (C) 2024 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +option java_package = "com.hedera.hapi.services.auxiliary.tss.legacy"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +/** + * A TSS Share Signature transaction Body.
+ * This transaction body communicates a node's signature of a block hash + * using its private share within the TSS process. + * This transaction MUST be prioritized for low latency gossip transmission. + * + * ### Block Stream Effects + * This transaction body will be present in the block stream. This will not have + * any state changes or transaction output or transaction result. + */ +message TssShareSignatureTransactionBody { + /** + * A SHA2-384 Hash.
+ * This is the hash of the roster that includes the node whose + * share produced this share signature. + *

+ * This value is REQUIRED.
+ * This value MUST identify the network roster active at the time this + * share signature was produced.
+ * This share signature MUST be produced from a share distributed during + * the re-keying process for the identified roster. + */ + bytes roster_hash = 1; + + /** + * An index of the share from the node private shares.
+ * This is the index of the share that produced this share signature. + *

+ * This value is REQUIRED.
+ * The share referred to by this index MUST exist.
+ * The share index MUST be greater than or equal to 0. + */ + uint64 share_index = 2; + + /** + * A SHA2-384 hash.
+ * This is the hash of the message that was signed. + *

+ * This value is REQUIRED.
+ * The message signed MUST be a block hash. + */ + bytes message_hash = 3; + + /** + * The signature bytes.
+ * This is the signature generated by signing the block hash with the node's private share. + *

+ * This value is REQUIRED.
+ * This value MUST be a valid signature of the message hash with the node's private share. + */ + bytes share_signature = 4; +} diff --git a/hapi/hedera-protobufs/services/basic_types.proto b/hapi/hedera-protobufs/services/basic_types.proto index 70281f01a383..2e30bba15e26 100644 --- a/hapi/hedera-protobufs/services/basic_types.proto +++ b/hapi/hedera-protobufs/services/basic_types.proto @@ -1259,6 +1259,11 @@ enum HederaFunctionality { * Submit a vote as part of the Threshold Signature Scheme (TSS) processing. */ TssVote = 97; + + /** + * Submit a node signature as part of the Threshold Signature Scheme (TSS) processing. + */ + TssShareSignature = 98; } /** diff --git a/hapi/hedera-protobufs/services/transaction_body.proto b/hapi/hedera-protobufs/services/transaction_body.proto index 75557d43e6b7..46cb78695852 100644 --- a/hapi/hedera-protobufs/services/transaction_body.proto +++ b/hapi/hedera-protobufs/services/transaction_body.proto @@ -91,6 +91,7 @@ import "node_delete.proto"; import "auxiliary/tss/tss_message.proto"; import "auxiliary/tss/tss_vote.proto"; +import "auxiliary/tss/tss_share_signature.proto"; /** * A single transaction. All transaction types are possible here. @@ -431,5 +432,10 @@ message TransactionBody { * A transaction body for a `tssVote` request. */ com.hedera.hapi.services.auxiliary.tss.TssVoteTransactionBody tssVote = 62; + + /** + * A transaction body for a 'tssShareSignature` request + */ + com.hedera.hapi.services.auxiliary.tss.TssShareSignatureTransactionBody tssShareSignature = 63; } } diff --git a/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java b/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java index 3f3f6637e4ce..ae0f95fb981e 100644 --- a/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java +++ b/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java @@ -236,6 +236,7 @@ public static HederaFunctionality functionOf(final TransactionBody txn) throws U case TOKEN_CLAIM_AIRDROP -> HederaFunctionality.TOKEN_CLAIM_AIRDROP; case TSS_MESSAGE -> HederaFunctionality.TSS_MESSAGE; case TSS_VOTE -> HederaFunctionality.TSS_VOTE; + case TSS_SHARE_SIGNATURE -> HederaFunctionality.TSS_SHARE_SIGNATURE; case UNSET -> throw new UnknownHederaFunctionality(); }; } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceScopeLookup.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceScopeLookup.java index 4dcafe4a7e51..f0e5f0a5857e 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceScopeLookup.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceScopeLookup.java @@ -121,7 +121,7 @@ public String getServiceName(@NonNull final TransactionBody txBody) { }; case NODE_CREATE, NODE_DELETE, NODE_UPDATE -> AddressBookService.NAME; - case TSS_MESSAGE, TSS_VOTE -> TssBaseService.NAME; + case TSS_MESSAGE, TSS_VOTE, TSS_SHARE_SIGNATURE -> TssBaseService.NAME; default -> NON_EXISTING_SERVICE; }; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceComponent.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceComponent.java index 1adabf482203..5b140cecd7b4 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceComponent.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceComponent.java @@ -19,6 +19,7 @@ import com.hedera.node.app.spi.AppContext; import com.hedera.node.app.tss.api.TssLibrary; import com.hedera.node.app.tss.handlers.TssMessageHandler; +import com.hedera.node.app.tss.handlers.TssShareSignatureHandler; import com.hedera.node.app.tss.handlers.TssSubmissions; import com.hedera.node.app.tss.handlers.TssVoteHandler; import com.swirlds.metrics.api.Metrics; @@ -46,5 +47,7 @@ TssBaseServiceComponent create( TssVoteHandler tssVoteHandler(); + TssShareSignatureHandler tssShareSignatureHandler(); + TssSubmissions tssSubmissions(); } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceImpl.java index 43f72ffc412d..4809974a18e2 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/TssBaseServiceImpl.java @@ -107,7 +107,8 @@ public TssBaseServiceImpl( final var component = DaggerTssBaseServiceComponent.factory() .create(tssLibrary, appContext.gossip(), submissionExecutor, tssLibraryExecutor, metrics); this.tssMetrics = component.tssMetrics(); - this.tssHandlers = new TssHandlers(component.tssMessageHandler(), component.tssVoteHandler()); + this.tssHandlers = new TssHandlers( + component.tssMessageHandler(), component.tssVoteHandler(), component.tssShareSignatureHandler()); this.tssSubmissions = component.tssSubmissions(); } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssHandlers.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssHandlers.java index 8c20a0418f14..341d8baae9f2 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssHandlers.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssHandlers.java @@ -16,7 +16,9 @@ package com.hedera.node.app.tss.handlers; -import com.hedera.node.app.spi.workflows.TransactionHandler; import edu.umd.cs.findbugs.annotations.NonNull; -public record TssHandlers(@NonNull TransactionHandler tssMessageHandler, @NonNull TransactionHandler tssVoteHandler) {} +public record TssHandlers( + @NonNull TssMessageHandler tssMessageHandler, + @NonNull TssVoteHandler tssVoteHandler, + @NonNull TssShareSignatureHandler tssShareSignatureHandler) {} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssShareSignatureHandler.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssShareSignatureHandler.java new file mode 100644 index 000000000000..16d190d7acbf --- /dev/null +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/tss/handlers/TssShareSignatureHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hedera.node.app.tss.handlers; + +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.HandleException; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.app.spi.workflows.PreHandleContext; +import com.hedera.node.app.spi.workflows.TransactionHandler; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Handles TSS share signature transactions. + * This is yet to be implemented. + */ +@Singleton +public class TssShareSignatureHandler implements TransactionHandler { + + @Inject + public TssShareSignatureHandler() {} + + @Override + public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException { + requireNonNull(context); + // TODO: Implement this in the later PRS + } + + @Override + public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { + requireNonNull(txn); + } + + @Override + public void handle(@NonNull final HandleContext context) throws HandleException { + requireNonNull(context); + } +} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java index d76e927191e2..0e42ccdfebfc 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java @@ -224,6 +224,7 @@ private TransactionHandler getHandler(@NonNull final TransactionBody txBody) { case TSS_MESSAGE -> handlers.tssMessageHandler(); case TSS_VOTE -> handlers.tssVoteHandler(); + case TSS_SHARE_SIGNATURE -> handlers.tssShareSignatureHandler(); default -> throw new UnsupportedOperationException(TYPE_NOT_SUPPORTED); }; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionHandlers.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionHandlers.java index a69b6601b854..68fec8ddf662 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionHandlers.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionHandlers.java @@ -70,7 +70,9 @@ import com.hedera.node.app.service.token.impl.handlers.TokenUpdateHandler; import com.hedera.node.app.service.token.impl.handlers.TokenUpdateNftsHandler; import com.hedera.node.app.service.util.impl.handlers.UtilPrngHandler; -import com.hedera.node.app.spi.workflows.TransactionHandler; +import com.hedera.node.app.tss.handlers.TssMessageHandler; +import com.hedera.node.app.tss.handlers.TssShareSignatureHandler; +import com.hedera.node.app.tss.handlers.TssVoteHandler; import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -132,5 +134,6 @@ public record TransactionHandlers( @NonNull NodeDeleteHandler nodeDeleteHandler, @NonNull TokenClaimAirdropHandler tokenClaimAirdropHandler, @NonNull UtilPrngHandler utilPrngHandler, - @NonNull TransactionHandler tssMessageHandler, - @NonNull TransactionHandler tssVoteHandler) {} + @NonNull TssMessageHandler tssMessageHandler, + @NonNull TssVoteHandler tssVoteHandler, + @NonNull TssShareSignatureHandler tssShareSignatureHandler) {} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflowModule.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflowModule.java index 08f009647c98..c8df8742ef6a 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflowModule.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflowModule.java @@ -155,6 +155,7 @@ static TransactionHandlers provideTransactionHandlers( tokenHandlers.tokenClaimAirdropHandler(), utilHandlers.prngHandler(), tssHandlers.get().tssMessageHandler(), - tssHandlers.get().tssVoteHandler()); + tssHandlers.get().tssVoteHandler(), + tssHandlers.get().tssShareSignatureHandler()); } } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java index 34c2017462ea..5f05fe33fe9c 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java @@ -43,10 +43,12 @@ import com.hedera.node.app.signature.AppSignatureVerifier; import com.hedera.node.app.signature.impl.SignatureExpanderImpl; import com.hedera.node.app.signature.impl.SignatureVerifierImpl; -import com.hedera.node.app.spi.workflows.TransactionHandler; import com.hedera.node.app.state.recordcache.RecordCacheService; import com.hedera.node.app.tss.TssBaseService; import com.hedera.node.app.tss.handlers.TssHandlers; +import com.hedera.node.app.tss.handlers.TssMessageHandler; +import com.hedera.node.app.tss.handlers.TssShareSignatureHandler; +import com.hedera.node.app.tss.handlers.TssVoteHandler; import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -79,7 +81,13 @@ class IngestComponentTest { private TssBaseService tssBaseService; @Mock - private TransactionHandler transactionHandler; + private TssMessageHandler tssMessageHandler; + + @Mock + private TssVoteHandler tssVoteHandler; + + @Mock + private TssShareSignatureHandler tssShareSignatureHandler; private HederaInjectionComponent app; @@ -105,7 +113,8 @@ void setUp() { new SignatureExpanderImpl(), new SignatureVerifierImpl(CryptographyHolder.get())), UNAVAILABLE_GOSSIP); - given(tssBaseService.tssHandlers()).willReturn(new TssHandlers(transactionHandler, transactionHandler)); + given(tssBaseService.tssHandlers()) + .willReturn(new TssHandlers(tssMessageHandler, tssVoteHandler, tssShareSignatureHandler)); app = DaggerHederaInjectionComponent.builder() .configProviderImpl(configProvider) .bootstrapConfigProviderImpl(new BootstrapConfigProviderImpl()) diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowModuleTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowModuleTest.java index e5524cfbe383..ff25c446f82c 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowModuleTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowModuleTest.java @@ -78,6 +78,7 @@ import com.hedera.node.app.service.util.impl.handlers.UtilPrngHandler; import com.hedera.node.app.tss.handlers.TssHandlers; import com.hedera.node.app.tss.handlers.TssMessageHandler; +import com.hedera.node.app.tss.handlers.TssShareSignatureHandler; import com.hedera.node.app.tss.handlers.TssVoteHandler; import com.hedera.node.app.workflows.dispatcher.TransactionHandlers; import org.junit.jupiter.api.Test; @@ -264,6 +265,9 @@ class HandleWorkflowModuleTest { @Mock private TssVoteHandler tssVoteHandler; + @Mock + private TssShareSignatureHandler tssShareSignatureHandler; + @Test void usesComponentsToGetHandlers() { given(consensusHandlers.consensusCreateTopicHandler()).willReturn(consensusCreateTopicHandler); @@ -321,7 +325,7 @@ void usesComponentsToGetHandlers() { consensusHandlers, fileHandlers, () -> contractHandlers, - () -> new TssHandlers(tssMessageHandler, tssVoteHandler), + () -> new TssHandlers(tssMessageHandler, tssVoteHandler, tssShareSignatureHandler), scheduleHandlers, tokenHandlers, utilHandlers, diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java index 3d00f6119f12..706665cfa4bc 100644 --- a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java @@ -86,6 +86,7 @@ import static com.hedera.hapi.node.base.HederaFunctionality.TRANSACTION_GET_RECEIPT; import static com.hedera.hapi.node.base.HederaFunctionality.TRANSACTION_GET_RECORD; import static com.hedera.hapi.node.base.HederaFunctionality.TSS_MESSAGE; +import static com.hedera.hapi.node.base.HederaFunctionality.TSS_SHARE_SIGNATURE; import static com.hedera.hapi.node.base.HederaFunctionality.TSS_VOTE; import static com.hedera.hapi.node.base.HederaFunctionality.UTIL_PRNG; @@ -263,7 +264,8 @@ public record ApiPermissionConfig( @ConfigProperty(defaultValue = "2-55") PermissionedAccountsRange updateNode, @ConfigProperty(defaultValue = "2-55") PermissionedAccountsRange deleteNode, @ConfigProperty(defaultValue = "0-0") PermissionedAccountsRange tssMessage, - @ConfigProperty(defaultValue = "0-0") PermissionedAccountsRange tssVote) { + @ConfigProperty(defaultValue = "0-0") PermissionedAccountsRange tssVote, + @ConfigProperty(defaultValue = "0-0") PermissionedAccountsRange tssShareSignature) { private static final EnumMap> permissionKeys = new EnumMap<>(HederaFunctionality.class); @@ -343,6 +345,7 @@ public record ApiPermissionConfig( permissionKeys.put(NODE_DELETE, c -> c.deleteNode); permissionKeys.put(TSS_MESSAGE, c -> c.tssMessage); permissionKeys.put(TSS_VOTE, c -> c.tssVote); + permissionKeys.put(TSS_SHARE_SIGNATURE, c -> c.tssShareSignature); } /** diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TssConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TssConfig.java index 4dee3429b2df..55219d8b0078 100644 --- a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TssConfig.java +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TssConfig.java @@ -23,10 +23,19 @@ /** * Configuration for the TSS service. - * @param maxSharesPerNode The maximum number of shares that can be assigned to a node. - * @param timesToTrySubmission The number of times to retry a submission on getting an {@link IllegalStateException} - * @param distinctTxnIdsToTry The number of distinct transaction IDs to try in the event of a duplicate id - * @param keyActiveRoster A test-only configuration; set this to true to enable the process that will key the candidate roster with TSS key material. + * + * @param maxSharesPerNode The maximum number of shares that can be assigned to a node. + * @param timesToTrySubmission The number of times to retry a submission on getting an {@link IllegalStateException} + * @param retryDelay The delay between retries + * @param distinctTxnIdsToTry The number of distinct transaction IDs to try in the event of a duplicate id + * @param keyCandidateRoster A feature flag for TSS; set this to true to enable the process that will key + * @param keyActiveRoster A test-only configuration; set this to true to enable the process that will + * key the candidate roster with TSS key material, without waiting for upgrade + * boundary. + * @param signatureLivenessPeriodMinutes The amount of time a share signature is held in memory before being + * discarded in minutes + * @param ledgerSignatureFailureThreshold The number of consecutive failures to produce a ledger signature before + * logging an error */ @ConfigData("tss") public record TssConfig( @@ -36,4 +45,5 @@ public record TssConfig( @ConfigProperty(defaultValue = "10") @NetworkProperty int distinctTxnIdsToTry, @ConfigProperty(defaultValue = "false") @NetworkProperty boolean keyCandidateRoster, @ConfigProperty(defaultValue = "false") @NetworkProperty boolean keyActiveRoster, - @ConfigProperty(defaultValue = "false") @NetworkProperty boolean enableLedgerId) {} + @ConfigProperty(defaultValue = "5") @NetworkProperty int signatureLivenessPeriodMinutes, + @ConfigProperty(defaultValue = "2") @NetworkProperty int ledgerSignatureFailureThreshold) {}