From 875824c9463799198fc2bbd546e22dcb51b887d4 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 12:37:27 +0100 Subject: [PATCH 01/10] Implement /get_event_decryption_key Note: this is still an intermediate in regards to fixing #84. Also, the 'timestamp' in responses can not be set (see FIXME comment). --- internal/router/router.go | 1 + internal/service/crypto.go | 38 ++++++++++++ internal/usecase/eventtrigger.go | 99 ++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/internal/router/router.go b/internal/router/router.go index 8097892..6ce13f5 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -35,6 +35,7 @@ func NewRouter( api := router.Group("/api") { api.GET("/get_decryption_key", cryptoService.GetDecryptionKey) + api.GET("/get_event_decryption_key", cryptoService.GetEventDecryptionKey) api.GET("/get_data_for_encryption", cryptoService.GetDataForEncryption) api.POST("/register_identity", cryptoService.RegisterIdentity) api.POST("/compile_event_trigger_definition", cryptoService.CompileEventTriggerDefinition) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index b59d9ca..488cd44 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -39,6 +39,44 @@ func NewCryptoService( } } +// @BasePath /api +// +// GetEventDecryptionKey godoc +// +// @Summary Get decryption key. +// @Description Retrieves a decryption key for a given registered event based identity once it was triggered. Decryption key is 0x padded, clients need to remove the prefix when decrypting on their end. +// @Tags Crypto +// @Produce json +// @Param identity query string true "Identity associated with the decryption key." +// @Success 200 {object} usecase.GetDecryptionKeyResponse "Success." +// @Failure 400 {object} error.Http "Invalid Get decryption key request." +// @Failure 404 {object} error.Http "Decryption key not found for the associated identity." +// @Failure 429 {object} error.Http "Too many requests. Rate limited." +// @Failure 500 {object} error.Http "Internal server error." +// @Security BearerAuth +// @Router /get_decryption_key [get] +func (svc *CryptoService) GetEventDecryptionKey(ctx *gin.Context) { + identity, ok := ctx.GetQuery("identity") + if !ok { + err := sherror.NewHttpError( + "query parameter not found", + "identity query parameter is required", + http.StatusBadRequest, + ) + ctx.Error(err) + return + } + + data, err := svc.CryptoUsecase.GetEventDecryptionKey(ctx, identity) + if err != nil { + ctx.Error(err) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "message": data, + }) +} + // @BasePath /api // // GetDecryptionKey godoc diff --git a/internal/usecase/eventtrigger.go b/internal/usecase/eventtrigger.go index 80bffee..046f762 100644 --- a/internal/usecase/eventtrigger.go +++ b/internal/usecase/eventtrigger.go @@ -518,3 +518,102 @@ func (uc *CryptoUsecase) GetEventTriggerExpirationBlock(ctx context.Context, eon ExpirationBlockNumber: uint64(expirationBlockNumber), }, nil } + +func (uc *CryptoUsecase) GetEventDecryptionKey(ctx context.Context, identity string) (*GetDecryptionKeyResponse, *httpError.Http) { + identityBytes, err := hex.DecodeString(strings.TrimPrefix(string(identity), "0x")) + if err != nil { + log.Err(err).Msg("err encountered while decoding identity") + err := httpError.NewHttpError( + "error encountered while decoding identity", + "", + http.StatusBadRequest, + ) + return nil, &err + } + + if len(identityBytes) != 32 { + log.Err(err).Msg("identity should be of length 32") + err := httpError.NewHttpError( + "identity should be of length 32", + "", + http.StatusBadRequest, + ) + return nil, &err + } + + blockNumber, err := uc.ethClient.BlockNumber(ctx) + if err != nil { + log.Err(err).Msg("err encountered while querying for recent block") + metrics.TotalFailedRPCCalls.Inc() + err := httpError.NewHttpError( + "error encountered while querying for recent block", + "", + http.StatusInternalServerError, + ) + return nil, &err + } + + eon, err := uc.keyperSetManagerContract.GetKeyperSetIndexByBlock(nil, blockNumber) + if err != nil { + log.Err(err).Msg("err encountered while querying current eon") + metrics.TotalFailedRPCCalls.Inc() + err := httpError.NewHttpError( + "error encountered while querying current eon", + "", + http.StatusInternalServerError, + ) + return nil, &err + } + + arg := data.GetDecryptionKeyParams{ + EpochID: []byte(identityBytes), + Eon: int64(eon), + } + + var decryptionKey string + + decKey, err := uc.dbQuery.GetDecryptionKey(ctx, arg) + if err != nil { + if err == pgx.ErrNoRows { + // no data found try querying from other keyper via http + decKey, err := uc.getDecryptionKeyFromExternalKeyper(ctx, int64(arg.Eon), identity) + if err != nil { + err := httpError.NewHttpError( + err.Error(), + "", + http.StatusInternalServerError, + ) + return nil, &err + } + if decKey == "" { + err := httpError.NewHttpError( + "decryption key doesn't exist", + "", + http.StatusNotFound, + ) + return nil, &err + } + decryptionKey = decKey + } else { + log.Err(err).Msg("err encountered while querying db") + err := httpError.NewHttpError( + "error while querying db", + "", + http.StatusInternalServerError, + ) + return nil, &err + } + } else { + decryptionKey = common.PrefixWith0x(hex.EncodeToString(decKey.DecryptionKey)) + } + + if !strings.HasPrefix(identity, "0x") { + identity = common.PrefixWith0x(identity) + } + + return &GetDecryptionKeyResponse{ + DecryptionKey: decryptionKey, + Identity: identity, + DecryptionTimestamp: 0, // FIXME: ensure, we can fill timestamp for event based keys + }, nil +} From a6da64fd2772c7444307997d59ddcdf20334dd27 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 12:38:28 +0100 Subject: [PATCH 02/10] Generate swagger docs --- docs/docs.go | 17 ++++++----------- docs/swagger.json | 10 ++++++---- docs/swagger.yaml | 16 ++++++++++------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index edd21b0..ed14f21 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -106,7 +106,8 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "type": "integer" + "type": "integer", + "format": "int32" } } }, @@ -149,7 +150,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "description": "Ethereum address associated with the identity. If you are registering the identity yourself, pass the address of the account making the registration. If you want the API to register the identity on gnosis mainnet, pass the address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc. For chiado pass the address: 0xb9C303443c9af84777e60D5C987AbF0c43844918", + "description": "Ethereum address associated with the identity. Time‑based: use the address that will register the identity (your account if self‑registering, or the API signer address below if you are using the API register endpoint). Event‑based (triggerDefinition provided): users cannot self‑register because the registry is owner‑only, please use the API signer address below. Gnosis Mainnet API address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc Chiado API address: 0xb9C303443c9af84777e60D5C987AbF0c43844918", "name": "address", "in": "query", "required": true @@ -271,6 +272,7 @@ const docTemplate = `{ "parameters": [ { "type": "integer", + "format": "int64", "description": "Eon number associated with the event identity registration.", "name": "eon", "in": "query", @@ -282,13 +284,6 @@ const docTemplate = `{ "name": "identityPrefix", "in": "query", "required": true - }, - { - "type": "string", - "description": "Ethereum address associated with the identity. For gnosis mainnet, pass the address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc. For chiado pass the address: 0xb9C303443c9af84777e60D5C987AbF0c43844918", - "name": "address", - "in": "query", - "required": true } ], "responses": { @@ -586,8 +581,8 @@ const docTemplate = `{ "example": "amount" }, "number": { - "type": "integer", - "example": 25433 + "type": "string", + "example": "25433" }, "op": { "type": "string", diff --git a/docs/swagger.json b/docs/swagger.json index f85eb71..1483554 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -97,7 +97,8 @@ "schema": { "type": "array", "items": { - "type": "integer" + "type": "integer", + "format": "int32" } } }, @@ -140,7 +141,7 @@ "parameters": [ { "type": "string", - "description": "Ethereum address associated with the identity. If you are registering the identity yourself, pass the address of the account making the registration. If you want the API to register the identity on gnosis mainnet, pass the address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc. For chiado pass the address: 0xb9C303443c9af84777e60D5C987AbF0c43844918", + "description": "Ethereum address associated with the identity. Time‑based: use the address that will register the identity (your account if self‑registering, or the API signer address below if you are using the API register endpoint). Event‑based (triggerDefinition provided): users cannot self‑register because the registry is owner‑only, please use the API signer address below. Gnosis Mainnet API address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc Chiado API address: 0xb9C303443c9af84777e60D5C987AbF0c43844918", "name": "address", "in": "query", "required": true @@ -262,6 +263,7 @@ "parameters": [ { "type": "integer", + "format": "int64", "description": "Eon number associated with the event identity registration.", "name": "eon", "in": "query", @@ -570,8 +572,8 @@ "example": "amount" }, "number": { - "type": "integer", - "example": 25433 + "type": "string", + "example": "25433" }, "op": { "type": "string", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 188413c..d2fbdec 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -105,8 +105,8 @@ definitions: example: amount type: string number: - example: 25433 - type: integer + example: "25433" + type: string op: example: gte type: string @@ -185,6 +185,7 @@ paths: description: Success. schema: items: + format: int32 type: integer type: array "400": @@ -211,10 +212,12 @@ paths: If triggerDefinition is provided, the identity will be computed for event-based triggers. Otherwise, it uses time-based identity computation. parameters: - - description: 'Ethereum address associated with the identity. If you are registering - the identity yourself, pass the address of the account making the registration. - If you want the API to register the identity on gnosis mainnet, pass the - address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc. For chiado pass the + - description: 'Ethereum address associated with the identity. Time‑based: use + the address that will register the identity (your account if self‑registering, + or the API signer address below if you are using the API register endpoint). + Event‑based (triggerDefinition provided): users cannot self‑register because + the registry is owner‑only, please use the API signer address below. Gnosis + Mainnet API address: 0x228DefCF37Da29475F0EE2B9E4dfAeDc3b0746bc Chiado API address: 0xb9C303443c9af84777e60D5C987AbF0c43844918' in: query name: address @@ -301,6 +304,7 @@ paths: registration. parameters: - description: Eon number associated with the event identity registration. + format: int64 in: query name: eon required: true From 33f4edca416ee8f4dd606a71671a5dca0902d0a5 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 12:50:05 +0100 Subject: [PATCH 03/10] Add event trigger to /decrypt_commitment --- internal/service/crypto.go | 4 ++-- internal/usecase/crypto.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index 488cd44..69db09c 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -208,10 +208,10 @@ func (svc *CryptoService) RegisterIdentity(ctx *gin.Context) { // DecryptCommitment godoc // // @Summary Allows clients to decrypt their encrypted message. -// @Description Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. Timestamp with which the identity was registered should have been passed for the message to be decrypted successfully. +// @Description Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. The trigger condition for the decryption key (timestamp or event) to be released must have been met for the message to be decrypted successfully. // @Tags Crypto // @Produce json -// @Param identity query string true "Identity used for registeration and encrypting the message." +// @Param identity query string true "Identity used for registration and encrypting the message." // @Param encryptedCommitment query string true "Encrypted commitment is the clients encrypted message." // @Success 200 {object} []byte "Success." // @Failure 400 {object} error.Http "Invalid Decrypt commitment request." diff --git a/internal/usecase/crypto.go b/internal/usecase/crypto.go index 31a7096..0075cdf 100644 --- a/internal/usecase/crypto.go +++ b/internal/usecase/crypto.go @@ -547,7 +547,10 @@ func (uc *CryptoUsecase) DecryptCommitment(ctx context.Context, encryptedCommitm return "", &err } - decKeyResponse, httpErr := uc.GetDecryptionKey(ctx, identity) + decKeyResponse, httpErr := uc.GetEventDecryptionKey(ctx, identity) + if httpErr.StatusCode == http.StatusNotFound { + decKeyResponse, httpErr = uc.GetDecryptionKey(ctx, identity) + } if httpErr != nil { return "", httpErr } From 5ca214af0bd8a1220feb749405e3591903f4c335 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 12:50:26 +0100 Subject: [PATCH 04/10] Update swagger docs --- docs/docs.go | 4 ++-- docs/swagger.json | 4 ++-- docs/swagger.yaml | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index ed14f21..98c8da5 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -76,7 +76,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. Timestamp with which the identity was registered should have been passed for the message to be decrypted successfully.", + "description": "Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. The trigger condition for the decryption key (timestamp or event) to be released must have been met for the message to be decrypted successfully.", "produces": [ "application/json" ], @@ -87,7 +87,7 @@ const docTemplate = `{ "parameters": [ { "type": "string", - "description": "Identity used for registeration and encrypting the message.", + "description": "Identity used for registration and encrypting the message.", "name": "identity", "in": "query", "required": true diff --git a/docs/swagger.json b/docs/swagger.json index 1483554..1f50cb5 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -67,7 +67,7 @@ "BearerAuth": [] } ], - "description": "Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. Timestamp with which the identity was registered should have been passed for the message to be decrypted successfully.", + "description": "Provides a way for clients to easily decrypt their encrypted message for which they have registered the identity for. The trigger condition for the decryption key (timestamp or event) to be released must have been met for the message to be decrypted successfully.", "produces": [ "application/json" ], @@ -78,7 +78,7 @@ "parameters": [ { "type": "string", - "description": "Identity used for registeration and encrypting the message.", + "description": "Identity used for registration and encrypting the message.", "name": "identity", "in": "query", "required": true diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d2fbdec..0b7dae5 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -164,11 +164,11 @@ paths: /decrypt_commitment: get: description: Provides a way for clients to easily decrypt their encrypted message - for which they have registered the identity for. Timestamp with which the - identity was registered should have been passed for the message to be decrypted - successfully. + for which they have registered the identity for. The trigger condition for + the decryption key (timestamp or event) to be released must have been met + for the message to be decrypted successfully. parameters: - - description: Identity used for registeration and encrypting the message. + - description: Identity used for registration and encrypting the message. in: query name: identity required: true From a0637c75083a8106c54a4c3d788908d29486a391 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 12:51:30 +0100 Subject: [PATCH 05/10] Fix name mismatch in docstring --- internal/service/crypto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index 69db09c..ef754d3 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -254,7 +254,7 @@ func (svc *CryptoService) DecryptCommitment(ctx *gin.Context) { // @BasePath /api // -// EventTriggerDefinition godoc +// CompileEventTriggerDefinition godoc // // @Summary Allows clients to compile an event trigger definition string. // @Description This endpoint takes an event signature snippet and some arguments to create an event trigger definition that will be understood by keypers From 636b0f50e22b39ed49567fb1cecbe97b2eba402b Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 13:01:42 +0100 Subject: [PATCH 06/10] Add new endpoint to rate limiter --- docker-compose.rate_limit.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docker-compose.rate_limit.yaml b/docker-compose.rate_limit.yaml index c7529aa..b5910fe 100644 --- a/docker-compose.rate_limit.yaml +++ b/docker-compose.rate_limit.yaml @@ -95,6 +95,13 @@ services: caddy.rate_limit_0.zone_6.match.path: "*/get_event_trigger_expiration_block*" caddy.rate_limit_0.zone_6.match.method: GET + caddy.rate_limit_0.zone_7: get_event_decryption_key__unauthorized + caddy.rate_limit_0.zone_7.key: "{remote_host}" + caddy.rate_limit_0.zone_7.events: 20 + caddy.rate_limit_0.zone_7.window: 1d + caddy.rate_limit_0.zone_7.match.path: "*/get_event_decryption_key*" + caddy.rate_limit_0.zone_7.match.method: GET + # Rate limits with api key caddy.rate_limit_1: "@withApiKey" caddy.rate_limit_1.log_key: " " @@ -148,6 +155,14 @@ services: caddy.rate_limit_1.zone_6.match.path: "*/get_event_trigger_expiration_block*" caddy.rate_limit_1.zone_6.match.method: GET + caddy.rate_limit_1.zone_7: get_event_decryption_key__authorized + caddy.rate_limit_1.zone_7.key: "{header.Authorization}" + caddy.rate_limit_1.zone_7.events: 2000 + caddy.rate_limit_1.zone_7.window: 1d + caddy.rate_limit_1.zone_7.match.path: "*/get_event_decryption_key*" + caddy.rate_limit_1.zone_7.match.method: GET + + caddy: build: context: . From 9d2da5cc8dc650e66e3e0ec7e6fe9a44d8eabb4b Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 13:06:06 +0100 Subject: [PATCH 07/10] Add 'get_event_decryption_key' to README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 256c920..903908f 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ For unauthorized access, the API on Gnosis Mainnet is rate limited with these li - `/get_event_trigger_expiration_block` 20 requests per 24 hours - `/get_data_for_encryption` 10 requests per 24 hours - `/get_decryption_key` 20 requests per 24 hours + - `/get_event_decryption_key` 20 requests per 24 hours - `/decrypt_commitment` 10 requests per 24 hours We recommend using Chiado for development, because there are no rate limits in place. @@ -92,6 +93,7 @@ Authorized requests have these limits: - `/get_event_trigger_expiration_block` 2000 requests per 24 hours - `/get_data_for_encryption` 1000 requests per 24 hours - `/get_decryption_key` 2000 requests per 24 hours + - `/get_event_decryption_key` 2000 requests per 24 hours - `/decrypt_commitment` 1000 requests per 24 hours Authorization is done by using an `Authorization: Bearer $API_KEY` header, when calling the API. @@ -367,7 +369,7 @@ console.log("Encrypted Commitment:", encryptedCommitment); #### 3.A Retrieve the Decryption Key -After the decryption trigger conditions are met (i.e., the specified timestamp has passed), retrieve the decryption key using the `/get_decryption_key` endpoint. +After the decryption trigger conditions are met (i.e., the specified timestamp has passed), retrieve the decryption key using the `/get_decryption_key` endpoint, or for event based decryption triggers the `get_event_decryption_key` endpoint. Refer to the Swagger documentation for detailed usage. From 7a2cc491deb22eea9c1602fcc644647d59a5d1df Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 17:29:35 +0100 Subject: [PATCH 08/10] Fix swagger docstring --- internal/service/crypto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index ef754d3..459cc90 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -54,7 +54,7 @@ func NewCryptoService( // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." // @Security BearerAuth -// @Router /get_decryption_key [get] +// @Router /get_event_decryption_key [get] func (svc *CryptoService) GetEventDecryptionKey(ctx *gin.Context) { identity, ok := ctx.GetQuery("identity") if !ok { From 914f2a77ba1f51207f1c899e898132a4c1e81d67 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 17:30:35 +0100 Subject: [PATCH 09/10] Introduce optional 'eon' argument for --- internal/service/crypto.go | 36 +++++++++++++++++++++-- internal/usecase/crypto.go | 6 ++-- internal/usecase/eventtrigger.go | 49 +++++++++++++++++--------------- tests/decrypt_commitment_test.go | 4 +-- 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index 459cc90..3a81240 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -48,6 +48,7 @@ func NewCryptoService( // @Tags Crypto // @Produce json // @Param identity query string true "Identity associated with the decryption key." +// @Param eon query int64 false "Optional eon parameter for the identity." // @Success 200 {object} usecase.GetDecryptionKeyResponse "Success." // @Failure 400 {object} error.Http "Invalid Get decryption key request." // @Failure 404 {object} error.Http "Decryption key not found for the associated identity." @@ -67,7 +68,22 @@ func (svc *CryptoService) GetEventDecryptionKey(ctx *gin.Context) { return } - data, err := svc.CryptoUsecase.GetEventDecryptionKey(ctx, identity) + eon := int64(-1) + eonArg, ok := ctx.GetQuery("eon") + var err error + if ok { + eon, err = strconv.ParseInt(eonArg, 10, 64) + if err != nil { + err := sherror.NewHttpError( + "query parameter invalid", + "eon query parameter could not be parsed", + http.StatusBadRequest, + ) + ctx.Error(err) + return + } + } + data, err := svc.CryptoUsecase.GetEventDecryptionKey(ctx, identity, eon) if err != nil { ctx.Error(err) return @@ -213,6 +229,7 @@ func (svc *CryptoService) RegisterIdentity(ctx *gin.Context) { // @Produce json // @Param identity query string true "Identity used for registration and encrypting the message." // @Param encryptedCommitment query string true "Encrypted commitment is the clients encrypted message." +// @Param eon query int64 false "Optional eon parameter for the identity." // @Success 200 {object} []byte "Success." // @Failure 400 {object} error.Http "Invalid Decrypt commitment request." // @Failure 429 {object} error.Http "Too many requests. Rate limited." @@ -241,8 +258,23 @@ func (svc *CryptoService) DecryptCommitment(ctx *gin.Context) { ctx.Error(err) return } + eon := int64(-1) + eonArg, ok := ctx.GetQuery("eon") + var err error + if ok { + eon, err = strconv.ParseInt(eonArg, 10, 64) + if err != nil { + err := sherror.NewHttpError( + "query parameter invalid", + "eon query parameter could not be parsed", + http.StatusBadRequest, + ) + ctx.Error(err) + return + } + } - data, err := svc.CryptoUsecase.DecryptCommitment(ctx, encryptedCommitment, identity) + data, err := svc.CryptoUsecase.DecryptCommitment(ctx, encryptedCommitment, identity, eon) if err != nil { ctx.Error(err) return diff --git a/internal/usecase/crypto.go b/internal/usecase/crypto.go index 0075cdf..d7bd155 100644 --- a/internal/usecase/crypto.go +++ b/internal/usecase/crypto.go @@ -526,7 +526,7 @@ func (uc *CryptoUsecase) RegisterIdentity(ctx context.Context, decryptionTimesta }, nil } -func (uc *CryptoUsecase) DecryptCommitment(ctx context.Context, encryptedCommitment string, identity string) (string, *httpError.Http) { +func (uc *CryptoUsecase) DecryptCommitment(ctx context.Context, encryptedCommitment string, identity string, eon int64) (string, *httpError.Http) { if len(encryptedCommitment) == 0 { log.Debug().Msg("empty encrypted commitment") err := httpError.NewHttpError( @@ -547,8 +547,8 @@ func (uc *CryptoUsecase) DecryptCommitment(ctx context.Context, encryptedCommitm return "", &err } - decKeyResponse, httpErr := uc.GetEventDecryptionKey(ctx, identity) - if httpErr.StatusCode == http.StatusNotFound { + decKeyResponse, httpErr := uc.GetEventDecryptionKey(ctx, identity, eon) + if httpErr != nil && httpErr.StatusCode == http.StatusNotFound { decKeyResponse, httpErr = uc.GetDecryptionKey(ctx, identity) } if httpErr != nil { diff --git a/internal/usecase/eventtrigger.go b/internal/usecase/eventtrigger.go index 046f762..15ed154 100644 --- a/internal/usecase/eventtrigger.go +++ b/internal/usecase/eventtrigger.go @@ -519,7 +519,7 @@ func (uc *CryptoUsecase) GetEventTriggerExpirationBlock(ctx context.Context, eon }, nil } -func (uc *CryptoUsecase) GetEventDecryptionKey(ctx context.Context, identity string) (*GetDecryptionKeyResponse, *httpError.Http) { +func (uc *CryptoUsecase) GetEventDecryptionKey(ctx context.Context, identity string, eon int64) (*GetDecryptionKeyResponse, *httpError.Http) { identityBytes, err := hex.DecodeString(strings.TrimPrefix(string(identity), "0x")) if err != nil { log.Err(err).Msg("err encountered while decoding identity") @@ -541,30 +541,33 @@ func (uc *CryptoUsecase) GetEventDecryptionKey(ctx context.Context, identity str return nil, &err } - blockNumber, err := uc.ethClient.BlockNumber(ctx) - if err != nil { - log.Err(err).Msg("err encountered while querying for recent block") - metrics.TotalFailedRPCCalls.Inc() - err := httpError.NewHttpError( - "error encountered while querying for recent block", - "", - http.StatusInternalServerError, - ) - return nil, &err - } + if eon < 0 { + blockNumber, err := uc.ethClient.BlockNumber(ctx) + if err != nil { + log.Err(err).Msg("err encountered while querying for recent block") + metrics.TotalFailedRPCCalls.Inc() + err := httpError.NewHttpError( + "error encountered while querying for recent block", + "", + http.StatusInternalServerError, + ) + return nil, &err + } - eon, err := uc.keyperSetManagerContract.GetKeyperSetIndexByBlock(nil, blockNumber) - if err != nil { - log.Err(err).Msg("err encountered while querying current eon") - metrics.TotalFailedRPCCalls.Inc() - err := httpError.NewHttpError( - "error encountered while querying current eon", - "", - http.StatusInternalServerError, - ) - return nil, &err - } + eonUint, err := uc.keyperSetManagerContract.GetKeyperSetIndexByBlock(nil, blockNumber) + if err != nil { + log.Err(err).Msg("err encountered while querying current eon") + metrics.TotalFailedRPCCalls.Inc() + err := httpError.NewHttpError( + "error encountered while querying current eon", + "", + http.StatusInternalServerError, + ) + return nil, &err + } + eon = int64(eonUint) + } arg := data.GetDecryptionKeyParams{ EpochID: []byte(identityBytes), Eon: int64(eon), diff --git a/tests/decrypt_commitment_test.go b/tests/decrypt_commitment_test.go index e251f35..0e8fbf0 100644 --- a/tests/decrypt_commitment_test.go +++ b/tests/decrypt_commitment_test.go @@ -42,7 +42,7 @@ func (s *TestShutterService) TestDecryptionCommitmentNotFound() { identityStringified := hex.EncodeToString(identity) encryptedCommitmentStringified := hex.EncodeToString(encrypedCommitmentBytes) - _, err = s.cryptoUsecase.DecryptCommitment(ctx, encryptedCommitmentStringified, identityStringified) + _, err = s.cryptoUsecase.DecryptCommitment(ctx, encryptedCommitmentStringified, identityStringified, -1) s.Require().Error(err) } @@ -80,7 +80,7 @@ func (s *TestShutterService) TestDecryptionCommitment() { identityStringified := hex.EncodeToString(identity) encryptedCommitmentStringified := "0x" + hex.EncodeToString(encrypedCommitmentBytes) - decryptedCommitment, err := s.cryptoUsecase.DecryptCommitment(ctx, encryptedCommitmentStringified, identityStringified) + decryptedCommitment, err := s.cryptoUsecase.DecryptCommitment(ctx, encryptedCommitmentStringified, identityStringified, -1) s.Require().Nil(err) dec, err := hex.DecodeString(strings.TrimPrefix(decryptedCommitment, "0x")) s.Require().Nil(err) From e1533d21bd4c2a8565cfbc5ea6b461ffca4098a6 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 20 Jan 2026 17:31:06 +0100 Subject: [PATCH 10/10] Compile swagger docs --- docs/docs.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++ docs/swagger.json | 72 +++++++++++++++++++++++++++++++++++++++++++++++ docs/swagger.yaml | 49 ++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) diff --git a/docs/docs.go b/docs/docs.go index 98c8da5..d07b27b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -98,6 +98,13 @@ const docTemplate = `{ "name": "encryptedCommitment", "in": "query", "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "Optional eon parameter for the identity.", + "name": "eon", + "in": "query" } ], "responses": { @@ -254,6 +261,71 @@ const docTemplate = `{ } } }, + "/get_event_decryption_key": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieves a decryption key for a given registered event based identity once it was triggered. Decryption key is 0x padded, clients need to remove the prefix when decrypting on their end.", + "produces": [ + "application/json" + ], + "tags": [ + "Crypto" + ], + "summary": "Get decryption key.", + "parameters": [ + { + "type": "string", + "description": "Identity associated with the decryption key.", + "name": "identity", + "in": "query", + "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "Optional eon parameter for the identity.", + "name": "eon", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "#/definitions/GetDecryptionKey" + } + }, + "400": { + "description": "Invalid Get decryption key request.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "404": { + "description": "Decryption key not found for the associated identity.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "429": { + "description": "Too many requests. Rate limited.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "500": { + "description": "Internal server error.", + "schema": { + "$ref": "#/definitions/error.Http" + } + } + } + } + }, "/get_event_trigger_expiration_block": { "get": { "security": [ diff --git a/docs/swagger.json b/docs/swagger.json index 1f50cb5..71cb7b3 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -89,6 +89,13 @@ "name": "encryptedCommitment", "in": "query", "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "Optional eon parameter for the identity.", + "name": "eon", + "in": "query" } ], "responses": { @@ -245,6 +252,71 @@ } } }, + "/get_event_decryption_key": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieves a decryption key for a given registered event based identity once it was triggered. Decryption key is 0x padded, clients need to remove the prefix when decrypting on their end.", + "produces": [ + "application/json" + ], + "tags": [ + "Crypto" + ], + "summary": "Get decryption key.", + "parameters": [ + { + "type": "string", + "description": "Identity associated with the decryption key.", + "name": "identity", + "in": "query", + "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "Optional eon parameter for the identity.", + "name": "eon", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "#/definitions/GetDecryptionKey" + } + }, + "400": { + "description": "Invalid Get decryption key request.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "404": { + "description": "Decryption key not found for the associated identity.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "429": { + "description": "Too many requests. Rate limited.", + "schema": { + "$ref": "#/definitions/error.Http" + } + }, + "500": { + "description": "Internal server error.", + "schema": { + "$ref": "#/definitions/error.Http" + } + } + } + } + }, "/get_event_trigger_expiration_block": { "get": { "security": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 0b7dae5..1664043 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -178,6 +178,11 @@ paths: name: encryptedCommitment required: true type: string + - description: Optional eon parameter for the identity. + format: int64 + in: query + name: eon + type: integer produces: - application/json responses: @@ -298,6 +303,50 @@ paths: summary: Get decryption key. tags: - Crypto + /get_event_decryption_key: + get: + description: Retrieves a decryption key for a given registered event based identity + once it was triggered. Decryption key is 0x padded, clients need to remove + the prefix when decrypting on their end. + parameters: + - description: Identity associated with the decryption key. + in: query + name: identity + required: true + type: string + - description: Optional eon parameter for the identity. + format: int64 + in: query + name: eon + type: integer + produces: + - application/json + responses: + "200": + description: Success. + schema: + $ref: '#/definitions/GetDecryptionKey' + "400": + description: Invalid Get decryption key request. + schema: + $ref: '#/definitions/error.Http' + "404": + description: Decryption key not found for the associated identity. + schema: + $ref: '#/definitions/error.Http' + "429": + description: Too many requests. Rate limited. + schema: + $ref: '#/definitions/error.Http' + "500": + description: Internal server error. + schema: + $ref: '#/definitions/error.Http' + security: + - BearerAuth: [] + summary: Get decryption key. + tags: + - Crypto /get_event_trigger_expiration_block: get: description: Retrieves the expiration block number for a given event identity