From a0ae6e5505992723e4e4d550ccd4afe783c18b45 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 10 Oct 2023 11:46:30 -0400 Subject: [PATCH 01/19] New endpoint definition. --- daemon/algod/api/algod.oas2.json | 86 ++++ daemon/algod/api/algod.oas3.yml | 113 +++++ .../api/server/v2/generated/data/routes.go | 7 +- .../v2/generated/experimental/routes.go | 18 +- .../api/server/v2/generated/model/types.go | 12 + .../nonparticipating/private/routes.go | 11 +- .../nonparticipating/public/routes.go | 80 +-- .../generated/participating/private/routes.go | 461 ++++++++++-------- .../generated/participating/public/routes.go | 24 +- libgoal/participation_test.go | 42 ++ 10 files changed, 578 insertions(+), 276 deletions(-) create mode 100644 libgoal/participation_test.go diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index b28e35abaa..1133444da3 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -934,6 +934,92 @@ } } }, + "/v2/participation/generate/{address}": { + "get": { + "tags": [ + "private", + "participating" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "summary": "Generate and install participation keys to the node.", + "operationId": "GenerateParticipationKeys", + "parameters": [ + { + "type": "string", + "description": "An account public key", + "name": "address", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Key dilution for two-level participation keys (defaults to sqrt of validity window).", + "name": "dilution", + "in": "query" + }, + { + "type": "integer", + "description": "First round for participation key.", + "name": "first", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Last round for participation key.", + "name": "last", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "The current swagger spec", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "401": { + "description": "Invalid API Token", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "404": { + "description": "Participation Key Not Found", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "503": { + "description": "Service Temporarily Unavailable", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "default": { + "description": "Unknown Error" + } + } + } + }, "/v2/participation/{participation-id}": { "delete": { "tags": [ diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index ecb205869d..c3e97334b3 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -5379,6 +5379,119 @@ "x-codegen-request-body-name": "participationkey" } }, + "/v2/participation/generate/{address}": { + "get": { + "operationId": "GenerateParticipationKeys", + "parameters": [ + { + "description": "An account public key", + "in": "path", + "name": "address", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Key dilution for two-level participation keys (defaults to sqrt of validity window).", + "in": "query", + "name": "dilution", + "schema": { + "type": "integer" + } + }, + { + "description": "First round for participation key.", + "in": "query", + "name": "first", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "description": "Last round for participation key.", + "in": "query", + "name": "last", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "The current swagger spec" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Bad Request" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Invalid API Token" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Participation Key Not Found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Service Temporarily Unavailable" + }, + "default": { + "content": {}, + "description": "Unknown Error" + } + }, + "summary": "Generate and install participation keys to the node.", + "tags": [ + "private", + "participating" + ] + } + }, "/v2/participation/{participation-id}": { "delete": { "description": "Delete a given participation key by ID", diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go index a17b5f1ca8..9fd389f273 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -314,9 +314,10 @@ var swaggerSpec = []string{ "4ru9Z2L8LnQF0x21KUbBuSdr2Q4/lKqH++v3vu9Rs1Pdi23Q5F+M4F+M4IiMQNeSJ49ocH9hgSWoXG5d", "TvMl7OIHw9syuOAnlYhlkF/sYBaujUGKV1x0eUUbqTV5/m5csybnfrCW5QIUc63yUaswInMr9MuGI/kz", "j9FPwV7v6kP78f0f4n5/Qbk/z50dtzU+qCwZyIYKKB92lvgXF/hvwwVsixxq93VKNJSlCs++Fnj2rSvG", - "1c3j1kU2kg90yhy2wnTn59MPnT+7CpFa1roQN8G3aFC33qCh7mAe1qr/9+kNZTqbC+lq5mHH7OHHGmh5", - "6hpk9H5ta1IPnmCh7eDHMDst+uspdUpE7Fnle8pHH/YV2dhTp8glXvKhof5xa9QKjUTIPRvz0Lv3hndh", - "K1zHWFubx/PTU8wVWAqlTycfpx969pDw4fuGXHwHt0kl2RpLlL//+P8DAAD//wKdD2Ya9wAA", + "1c3j1kU2kg90yhy2wnTn51NvQIjpkN03P3T+7KpOalnrQtwEs6Dp3fqNhlqGeVir/t+nN5TpbC6kq66H", + "vbWHH2ug5alrpdH7ta1ePXiCJbmDH8M8tuivp9SpG7Fnle8+H33YV3ljT53Kl3jJB5H6x635KzQnIZ9t", + "DEnv3hsuh01zHQturSPPT08xq2AplD6dfJx+6FlOwofvG8Lyvd4mlWRrLGb+/uP/DwAA//+EcjtHRPcA", + "AA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go index 77f2fccbea..1888e71ef1 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -285,15 +285,15 @@ var swaggerSpec = []string{ "KLbY83Ds0zXEW3aw9ElvDKx7emxYEawkpzpf1tXxJ/wPUm8AtE3ld6w3/Bjtb8efOmt1nwdr7f7edg9b", "rFeiAA+cmM9tfchdn48/2X+DiWBTgWSGLbTpM5ytsTl0Z8XkZPJ90OjFEvKrCdaUQs8vPE1PHj2K5DkN", "ehF7uOmshMKczGePno3owIUOO7mwnmHHn/kVF9ecYFY8e9PXqxWVW+SgdC25Im9fETYn0J+CKT8D3i50", - "odDCUM9Klk+mkw56Pn52SLNZoI6xitK2xaX/ecvz6I/Dbe5kwEn8fPyp82f3rKhlrQtxHfRFWcsqCobz", - "mY+16v99fE2ZNtyTS6eCxRSHnTXQ8tjlTu792qYrHHzBHIzBj6HjcvTXY+oQOKmEihDje3odKEhPsbFl", - "MUDp7wTe1RNXbqWX6uN4k80YR7r4NGnryLcslv04lNEGb5WRONEi7bVUw1BojMeUgha5kf218GnIJyE/", - "pGUNn6OHCQ/Jox1rcW/QZFw9/G7CyMiKvqMF8WGsGXlDS4MVKMipe8g7S7NH+PGXg+6MW6dKc2QtL/N5", - "OvnmS+LnjBu2m5b+kjHTP/1y05+DXLMcyAWsKiGpZOWW/Mwbv9AbX48/IHFKml8hy9UQrHVikPS662oq", - "42GC3Sz7PmoUiN6QJeVF6QKrRI0FOg1loVZZBNYx86z4KhOVkAiATd8Dhc27oI7I+dKrmjC21Do1Y7Gc", - "NZSiQrUPJqWzk1COaeBxNeH13r3VjQxpDvECeOaukWwmiq2veS3ptd7YGKnBXdUUL49+7PNcsa+O50g0", - "8l5M/nMrf4XyzOTkQyDJfPj4+aP5JtfobvHhU8Cenxwfo1vrUih9PPk8/dRj3cOPHxuE+WJDk0qyNWbT", - "/fj5/wYAAP//423gG8XxAAA=", + "odDCUM9Klk+mkw56Pn52SLNZoI6xitK2xaX/ecvz6I/Dbe5kwEn8fOzfltj10m35qfNn91SpZa0LcR3M", + "glKZVSkMITMfa9X/+/iaMm34LJd4BcsuDjtroOWxy7Lc+7VNbDj4gtkagx9DF+for8fUoXpSCRUh2/f0", + "OlClnmJjy4yA0t8JvNUnrjBLLynI8SabMY4U9GnSVpxvmTH7cSjNDV41I5ui7drrs4ZB0xi5KQUtcqqw", + "3J9LWD4JOScta/gcPXZ4nB7tWIt7rSbjKud3U0tGVvQdLYgPeM3IG1oarEBBTt2T31maPeyPvxx0Z9y6", + "X5rDbbmez9PJN18SP2fcMOi09NeRmf7pl5v+HOSa5UAuYFUJSSUrt+Rn3niQ3vgi/QGJU9L8CpmzhmCt", + "u4Ok112nVBkPKOzm4/fxpUD0hiwpL0oXgiVqLOVpKAv1zyKwo5kHyNejqIREAGyiHyhshgZ1RM6XXimF", + "UajW/RnL6qyhFBUqiDB9nZ2EckwYj6sJH4Lu/W+kTXOIF8Azd41kM1FsfXVsSa/1xkZTDe6qpsx59GOf", + "O4t9ddxJopH3d/KfW0ktlHwmJx8CmefDx88fzTe5RseMD58CRv7k+BgdYJdC6ePJ5+mnHpMffvzYIMyX", + "JZpUkq0x7+7Hz/83AAD//8gL9x7v8QAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go index 4be4676f9f..c30caa6c94 100644 --- a/daemon/algod/api/server/v2/generated/model/types.go +++ b/daemon/algod/api/server/v2/generated/model/types.go @@ -1485,6 +1485,18 @@ type GetTransactionGroupLedgerStateDeltasForRoundParams struct { // GetTransactionGroupLedgerStateDeltasForRoundParamsFormat defines parameters for GetTransactionGroupLedgerStateDeltasForRound. type GetTransactionGroupLedgerStateDeltasForRoundParamsFormat string +// GenerateParticipationKeysParams defines parameters for GenerateParticipationKeys. +type GenerateParticipationKeysParams struct { + // Dilution Key dilution for two-level participation keys (defaults to sqrt of validity window). + Dilution *uint64 `form:"dilution,omitempty" json:"dilution,omitempty"` + + // First First round for participation key. + First uint64 `form:"first" json:"first"` + + // Last Last round for participation key. + Last uint64 `form:"last" json:"last"` +} + // ShutdownNodeParams defines parameters for ShutdownNode. type ShutdownNodeParams struct { Timeout *uint64 `form:"timeout,omitempty" json:"timeout,omitempty"` diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go index 04a1236519..3d3c20de36 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go @@ -340,11 +340,12 @@ var swaggerSpec = []string{ "wOsIo4ljqhXM69K/O65KNre1ToUo/USqrirDceZUNZTlIluNBGufcTZDk5rngluPOAYR+1yK+BwTH26q", "K1Z1urC5oSpXhZMDuJd6uOm/1iA37a6vmBFF2+0dxGz8nizc4vEALLw70IFZ+JM92eiff8X/tS+tZ4/+", "9ukg8NkKLtgKRK3/rJfmub3B7nRpOhneJtw+1mt+jFFyxx86Gon7PNBIur+33cMW1ytRgFchxHxuq7hv", - "+3z8wf4bTATrCiRbAbflVN2v9uY4xmKem+HPG55Hfxyuo5OIMfHz8YfOn12VTS1rXYgbW1UqKoXgpUhL", - "V0UZ7fONrm9uNzdAm/mR/FA1149LZ0AoVtERtW6NMTbO170TbNxleE+ppfNLLBjHCdDvgbPYcuE0uJYV", - "mBsPTQw9icdB9r0oYCjxxK43B2PnimsIPFKc+87X3ZCdftyP/NE/Y52LQ+IwH2vV//v4hjJt5CKXghEx", - "OuysgZbHrt5K79c2xfngC+ZtD34MHztGfz2mXWrvmj5suf/Ex75dJPbV2QUSjXyksf/c2khDmyOSS2Nt", - "fPfe7DpWVnaU1JrQTo6P8enJUih9jPJl17wWfnzfbLQvCNhs+Mf3H/9/AAAA//9K9iBFafkAAA==", + "+3z8wf4bTATrCiRbAbflVN2v9uY4xmKem+HPG55Hfxyuo5OIMfHzsTdxxLTcbssPnT+7yp1a1roQN7b+", + "VFReweuTlq7eMlryG6uAuQfdAG2OSPJD1VxULvEBoVhvR9S6NdvYiGD3orBxrOGNppbOg7FgHCdADwnO", + "YguL0+ACV2DuRjRG9GQjB9n3ooChbBS7CB2MncuwOQqRMt53vhiHjPfjfgcFPTnWDTkkI/OxVv2/j28o", + "00aCcskaEaPDzhpoeewqs/R+bZOhD75ghvfgx/BZZPTXY9o9F10jidmyVMeBBSX21VkQEo18TLL/3FpT", + "Q+skkktjl3z33uw61mB2lNQa206Oj/GRylIofYySaNcQF35832y0Lx3YbPjH9x//fwAAAP//APnWz5P5", + "AAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go index 223956bf33..89202c3fb1 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -964,46 +964,46 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index 49c6517c19..3fa3bd6a48 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -27,6 +27,9 @@ type ServerInterface interface { // Add a participation key to the node // (POST /v2/participation) AddParticipationKey(ctx echo.Context) error + // Generate and install participation keys to the node. + // (GET /v2/participation/generate/{address}) + GenerateParticipationKeys(ctx echo.Context, address string, params GenerateParticipationKeysParams) error // Delete a given participation key by ID // (DELETE /v2/participation/{participation-id}) DeleteParticipationKeyByID(ctx echo.Context, participationId string) error @@ -65,6 +68,47 @@ func (w *ServerInterfaceWrapper) AddParticipationKey(ctx echo.Context) error { return err } +// GenerateParticipationKeys converts echo context to params. +func (w *ServerInterfaceWrapper) GenerateParticipationKeys(ctx echo.Context) error { + var err error + // ------------- Path parameter "address" ------------- + var address string + + err = runtime.BindStyledParameterWithLocation("simple", false, "address", runtime.ParamLocationPath, ctx.Param("address"), &address) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) + } + + ctx.Set(Api_keyScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params GenerateParticipationKeysParams + // ------------- Optional query parameter "dilution" ------------- + + err = runtime.BindQueryParameter("form", true, false, "dilution", ctx.QueryParams(), ¶ms.Dilution) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter dilution: %s", err)) + } + + // ------------- Required query parameter "first" ------------- + + err = runtime.BindQueryParameter("form", true, true, "first", ctx.QueryParams(), ¶ms.First) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter first: %s", err)) + } + + // ------------- Required query parameter "last" ------------- + + err = runtime.BindQueryParameter("form", true, true, "last", ctx.QueryParams(), ¶ms.Last) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter last: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GenerateParticipationKeys(ctx, address, params) + return err +} + // DeleteParticipationKeyByID converts echo context to params. func (w *ServerInterfaceWrapper) DeleteParticipationKeyByID(ctx echo.Context) error { var err error @@ -149,6 +193,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/v2/participation", wrapper.GetParticipationKeys, m...) router.POST(baseURL+"/v2/participation", wrapper.AddParticipationKey, m...) + router.GET(baseURL+"/v2/participation/generate/:address", wrapper.GenerateParticipationKeys, m...) router.DELETE(baseURL+"/v2/participation/:participation-id", wrapper.DeleteParticipationKeyByID, m...) router.GET(baseURL+"/v2/participation/:participation-id", wrapper.GetParticipationKeyByID, m...) router.POST(baseURL+"/v2/participation/:participation-id", wrapper.AppendKeys, m...) @@ -158,213 +203,215 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "16auAQEA", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go index 3003f8b27d..67e59f47b8 100644 --- a/daemon/algod/api/server/v2/generated/participating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go @@ -380,18 +380,18 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/libgoal/participation_test.go b/libgoal/participation_test.go new file mode 100644 index 0000000000..3e46682045 --- /dev/null +++ b/libgoal/participation_test.go @@ -0,0 +1,42 @@ +package libgoal + +import ( + "github.com/algorand/go-algorand/data/account" + "reflect" + "testing" +) + +func TestGenParticipationKeysTo(t *testing.T) { + type args struct { + address string + firstValid uint64 + lastValid uint64 + keyDilution uint64 + outDir string + installFunc func(keyPath string) error + } + tests := []struct { + name string + args args + wantPart account.Participation + wantFilePath string + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotPart, gotFilePath, err := GenParticipationKeysTo(tt.args.address, tt.args.firstValid, tt.args.lastValid, tt.args.keyDilution, tt.args.outDir, tt.args.installFunc) + if (err != nil) != tt.wantErr { + t.Errorf("GenParticipationKeysTo() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotPart, tt.wantPart) { + t.Errorf("GenParticipationKeysTo() gotPart = %v, want %v", gotPart, tt.wantPart) + } + if gotFilePath != tt.wantFilePath { + t.Errorf("GenParticipationKeysTo() gotFilePath = %v, want %v", gotFilePath, tt.wantFilePath) + } + }) + } +} \ No newline at end of file From 2b48d547ca48d4be9b4ba6a8894eb7c045d7cdb4 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 10 Oct 2023 11:49:48 -0400 Subject: [PATCH 02/19] WIP --- cmd/goal/account.go | 6 ++- daemon/algod/api/server/v2/handlers.go | 27 ++++++++++ libgoal/participation.go | 12 +++-- libgoal/participation_test.go | 49 ++++++------------- .../accountParticipationTransitions_test.go | 6 ++- .../features/stateproofs/stateproofs_test.go | 6 ++- .../transactions/onlineStatusChange_test.go | 6 ++- 7 files changed, 70 insertions(+), 42 deletions(-) diff --git a/cmd/goal/account.go b/cmd/goal/account.go index fcf4fbe09f..a833f06e3d 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -912,7 +912,11 @@ var addParticipationKeyCmd = &cobra.Command{ var err error var part algodAcct.Participation participationGen := func() { - part, _, err = client.GenParticipationKeysTo(accountAddress, roundFirstValid, roundLastValid, keyDilution, partKeyOutDir) + installFunc := func(keyPath string) error { + _, err := c.AddParticipationKey(keyPath) + return err + } + part, _, err = client.GenParticipationKeysTo(accountAddress, roundFirstValid, roundLastValid, keyDilution, partKeyOutDir, installFunc) } util.RunFuncWithSpinningCursor(participationGen) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 7fc2718ab7..b4965db1a4 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -25,6 +25,7 @@ import ( "io" "math" "net/http" + "os" "strings" "time" @@ -48,6 +49,7 @@ import ( "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/simulation" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/node" "github.com/algorand/go-algorand/protocol" @@ -240,6 +242,31 @@ func (v2 *Handlers) GetParticipationKeys(ctx echo.Context) error { return ctx.JSON(http.StatusOK, response) } +func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { + + installFunc := func(path string) error { + bytes, err := os.ReadFile(path) + if err != nil { + return err + } + partKeyBinary := bytes + + if len(partKeyBinary) == 0 { + lenErr := fmt.Errorf(errRESTPayloadZeroLength) + return badRequest(ctx, lenErr, lenErr.Error(), v2.Log) + } + + partID, err := v2.Node.InstallParticipationKey(partKeyBinary) + v2.Log.Infof("Installed participation key %s", partID) + return err + } + _, _, err := libgoal.GenParticipationKeysTo(address, params.First, params.Last, nilToZero(params.Dilution), "output-directory", installFunc) + if err != nil { + v2.Log.Warnf("Error generating participation keys: %v", err) + } + return nil +} + // AddParticipationKey Add a participation key to the node // (POST /v2/participation) func (v2 *Handlers) AddParticipationKey(ctx echo.Context) error { diff --git a/libgoal/participation.go b/libgoal/participation.go index f57629a362..f16c863a20 100644 --- a/libgoal/participation.go +++ b/libgoal/participation.go @@ -70,15 +70,19 @@ func participationKeysPath(dataDir string, address basics.Address, firstValid, l // GenParticipationKeys creates a .partkey database for a given address, fills // it with keys, and installs it in the right place func (c *Client) GenParticipationKeys(address string, firstValid, lastValid, keyDilution uint64) (part account.Participation, filePath string, err error) { - return c.GenParticipationKeysTo(address, firstValid, lastValid, keyDilution, "") + installFunc := func(keyPath string) error { + _, err := c.AddParticipationKey(keyPath) + return err + } + return GenParticipationKeysTo(address, firstValid, lastValid, keyDilution, "", installFunc) } // GenParticipationKeysTo creates a .partkey database for a given address, fills // it with keys, and saves it in the specified output directory. If the output // directory is empty, the key will be installed. -func (c *Client) GenParticipationKeysTo(address string, firstValid, lastValid, keyDilution uint64, outDir string) (part account.Participation, filePath string, err error) { +func GenParticipationKeysTo(address string, firstValid, lastValid, keyDilution uint64, outDir string, installFunc func(keyPath string) error) (part account.Participation, filePath string, err error) { - install := outDir == "" + install := outDir == "" && installFunc != nil // Parse the address parsedAddr, err := basics.UnmarshalChecksumAddress(address) @@ -133,7 +137,7 @@ func (c *Client) GenParticipationKeysTo(address string, firstValid, lastValid, k } if install { - _, err = c.AddParticipationKey(partKeyPath) + err = installFunc(partKeyPath) } return part, partKeyPath, err } diff --git a/libgoal/participation_test.go b/libgoal/participation_test.go index 3e46682045..f2579af638 100644 --- a/libgoal/participation_test.go +++ b/libgoal/participation_test.go @@ -1,42 +1,23 @@ package libgoal import ( - "github.com/algorand/go-algorand/data/account" - "reflect" + "github.com/algorand/go-algorand/data/basics" + + "github.com/stretchr/testify/require" + "testing" ) func TestGenParticipationKeysTo(t *testing.T) { - type args struct { - address string - firstValid uint64 - lastValid uint64 - keyDilution uint64 - outDir string - installFunc func(keyPath string) error - } - tests := []struct { - name string - args args - wantPart account.Participation - wantFilePath string - wantErr bool - }{ - // TODO: Add test cases. + var called bool + installFunc := func(keyPath string) error { + called = true + return nil } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotPart, gotFilePath, err := GenParticipationKeysTo(tt.args.address, tt.args.firstValid, tt.args.lastValid, tt.args.keyDilution, tt.args.outDir, tt.args.installFunc) - if (err != nil) != tt.wantErr { - t.Errorf("GenParticipationKeysTo() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(gotPart, tt.wantPart) { - t.Errorf("GenParticipationKeysTo() gotPart = %v, want %v", gotPart, tt.wantPart) - } - if gotFilePath != tt.wantFilePath { - t.Errorf("GenParticipationKeysTo() gotFilePath = %v, want %v", gotFilePath, tt.wantFilePath) - } - }) - } -} \ No newline at end of file + var addr basics.Address + addr[1] = 1 + + _, _, err := GenParticipationKeysTo(addr.String(), 1000, 2000, 0, "", installFunc) + require.NoError(t, err) + require.True(t, called, "the install function should have been called") +} diff --git a/test/e2e-go/features/participation/accountParticipationTransitions_test.go b/test/e2e-go/features/participation/accountParticipationTransitions_test.go index e9a4e5735f..85a7676b57 100644 --- a/test/e2e-go/features/participation/accountParticipationTransitions_test.go +++ b/test/e2e-go/features/participation/accountParticipationTransitions_test.go @@ -39,7 +39,11 @@ import ( // installParticipationKey generates a new key for a given account and installs it with the client. func installParticipationKey(t *testing.T, client libgoal.Client, addr string, firstValid, lastValid uint64) (resp model.PostParticipationResponse, part account.Participation, err error) { // Install overlapping participation keys... - part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, t.TempDir()) + installFunc := func(keyPath string) error { + _, err := c.AddParticipationKey(keyPath) + return err + } + part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, t.TempDir(), installFunc) require.NoError(t, err) require.NotNil(t, filePath) require.Equal(t, addr, part.Parent.String()) diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go index 4d4bc9f51b..4d133f73c3 100644 --- a/test/e2e-go/features/stateproofs/stateproofs_test.go +++ b/test/e2e-go/features/stateproofs/stateproofs_test.go @@ -677,7 +677,11 @@ func installParticipationKey(t *testing.T, client libgoal.Client, addr string, f defer os.RemoveAll(dir) // Install overlapping participation keys... - part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, dir) + installFunc := func(keyPath string) error { + _, err := c.AddParticipationKey(keyPath) + return err + } + part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, dir, installFunc) require.NoError(t, err) require.NotNil(t, filePath) require.Equal(t, addr, part.Parent.String()) diff --git a/test/e2e-go/features/transactions/onlineStatusChange_test.go b/test/e2e-go/features/transactions/onlineStatusChange_test.go index a8a9ceb013..d47294cd1f 100644 --- a/test/e2e-go/features/transactions/onlineStatusChange_test.go +++ b/test/e2e-go/features/transactions/onlineStatusChange_test.go @@ -171,7 +171,11 @@ func TestCloseOnError(t *testing.T) { _, curRound := fixture.GetBalanceAndRound(initiallyOnline) var partkeyFile string - _, partkeyFile, err = client.GenParticipationKeysTo(initiallyOffline, 0, curRound+1000, 0, t.TempDir()) + installFunc := func(keyPath string) error { + _, err := c.AddParticipationKey(keyPath) + return err + } + _, partkeyFile, err = client.GenParticipationKeysTo(initiallyOffline, 0, curRound+1000, 0, t.TempDir(), installFunc) a.NoError(err) // make a participation key for initiallyOffline From 8a82257e67bdc3d45c64f9952975ab9e88b11b88 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 10 Oct 2023 12:35:39 -0400 Subject: [PATCH 03/19] Move GenPartitipationTo to a new package. --- data/account/participation.go | 5 ++ libgoal/participation.go | 82 +------------------- libgoal/participation/participation.go | 85 +++++++++++++++++++++ libgoal/participation/participation_test.go | 85 +++++++++++++++++++++ libgoal/participation_test.go | 23 ------ 5 files changed, 177 insertions(+), 103 deletions(-) create mode 100644 libgoal/participation/participation.go create mode 100644 libgoal/participation/participation_test.go delete mode 100644 libgoal/participation_test.go diff --git a/data/account/participation.go b/data/account/participation.go index 376e9090fc..c67191725e 100644 --- a/data/account/participation.go +++ b/data/account/participation.go @@ -20,6 +20,7 @@ import ( "context" "database/sql" "fmt" + "math" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -215,6 +216,10 @@ func (part PersistedParticipation) PersistNewParent() error { }) } +func DefaultKeyDilution(first, last basics.Round) uint64 { + return 1 + uint64(math.Sqrt(float64(last-first))) +} + // FillDBWithParticipationKeys initializes the passed database with participation keys func FillDBWithParticipationKeys(store db.Accessor, address basics.Address, firstValid, lastValid basics.Round, keyDilution uint64) (part PersistedParticipation, err error) { if lastValid < firstValid { diff --git a/libgoal/participation.go b/libgoal/participation.go index f16c863a20..302af5df94 100644 --- a/libgoal/participation.go +++ b/libgoal/participation.go @@ -18,15 +18,10 @@ package libgoal import ( "fmt" - "math" - "os" - "path/filepath" - - "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/account" "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/util/db" + "github.com/algorand/go-algorand/libgoal/participation" ) // chooseParticipation chooses which participation keys to use for going online @@ -59,14 +54,6 @@ func (c *Client) chooseParticipation(address basics.Address, round basics.Round) return } -func participationKeysPath(dataDir string, address basics.Address, firstValid, lastValid basics.Round) (string, error) { - // Build ///
...partkey - first := uint64(firstValid) - last := uint64(lastValid) - fileName := config.PartKeyFilename(address.String(), first, last) - return filepath.Join(dataDir, fileName), nil -} - // GenParticipationKeys creates a .partkey database for a given address, fills // it with keys, and installs it in the right place func (c *Client) GenParticipationKeys(address string, firstValid, lastValid, keyDilution uint64) (part account.Participation, filePath string, err error) { @@ -74,72 +61,7 @@ func (c *Client) GenParticipationKeys(address string, firstValid, lastValid, key _, err := c.AddParticipationKey(keyPath) return err } - return GenParticipationKeysTo(address, firstValid, lastValid, keyDilution, "", installFunc) -} - -// GenParticipationKeysTo creates a .partkey database for a given address, fills -// it with keys, and saves it in the specified output directory. If the output -// directory is empty, the key will be installed. -func GenParticipationKeysTo(address string, firstValid, lastValid, keyDilution uint64, outDir string, installFunc func(keyPath string) error) (part account.Participation, filePath string, err error) { - - install := outDir == "" && installFunc != nil - - // Parse the address - parsedAddr, err := basics.UnmarshalChecksumAddress(address) - if err != nil { - return - } - - firstRound, lastRound := basics.Round(firstValid), basics.Round(lastValid) - - // If we are installing, generate in the temp dir - if install { - outDir = os.TempDir() - } - // Connect to the database - partKeyPath, err := participationKeysPath(outDir, parsedAddr, firstRound, lastRound) - if err != nil { - return - } - _, err = os.Stat(partKeyPath) - if err == nil { - err = fmt.Errorf("ParticipationKeys exist for the range %d to %d", firstRound, lastRound) - return - } else if !os.IsNotExist(err) { - err = fmt.Errorf("participation key file '%s' cannot be accessed : %w", partKeyPath, err) - return - } - - // If the key is being installed, remove it afterwards. - if install { - // Explicitly ignore any errors - defer func(name string) { - _ = os.Remove(name) - }(partKeyPath) - } - - partdb, err := db.MakeErasableAccessor(partKeyPath) - if err != nil { - return - } - - if keyDilution == 0 { - keyDilution = 1 + uint64(math.Sqrt(float64(lastRound-firstRound))) - } - - // Fill the database with new participation keys - newPart, err := account.FillDBWithParticipationKeys(partdb, parsedAddr, firstRound, lastRound, keyDilution) - part = newPart.Participation - partdb.Close() - - if err != nil { - return - } - - if install { - err = installFunc(partKeyPath) - } - return part, partKeyPath, err + return participation.GenParticipationKeysTo(address, firstValid, lastValid, keyDilution, "", installFunc) } // ListParticipationKeys returns the available participation keys, diff --git a/libgoal/participation/participation.go b/libgoal/participation/participation.go new file mode 100644 index 0000000000..9a46948e05 --- /dev/null +++ b/libgoal/participation/participation.go @@ -0,0 +1,85 @@ +package participation + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/data/account" + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/util/db" +) + +func participationKeysPath(dataDir string, address basics.Address, firstValid, lastValid basics.Round) (string, error) { + // Build ///
...partkey + first := uint64(firstValid) + last := uint64(lastValid) + fileName := config.PartKeyFilename(address.String(), first, last) + return filepath.Join(dataDir, fileName), nil +} + +// GenParticipationKeysTo creates a .partkey database for a given address, fills +// it with keys, and saves it in the specified output directory. If the output +// directory is empty, the key will be installed. +func GenParticipationKeysTo(address string, firstValid, lastValid, keyDilution uint64, outDir string, installFunc func(keyPath string) error) (part account.Participation, filePath string, err error) { + + install := outDir == "" && installFunc != nil + + // Parse the address + parsedAddr, err := basics.UnmarshalChecksumAddress(address) + if err != nil { + return + } + + firstRound, lastRound := basics.Round(firstValid), basics.Round(lastValid) + + // If we are installing, generate in the temp dir + if install { + outDir = os.TempDir() + } + // Connect to the database + partKeyPath, err := participationKeysPath(outDir, parsedAddr, firstRound, lastRound) + if err != nil { + return + } + _, err = os.Stat(partKeyPath) + if err == nil { + err = fmt.Errorf("ParticipationKeys exist for the range %d to %d", firstRound, lastRound) + return + } else if !os.IsNotExist(err) { + err = fmt.Errorf("participation key file '%s' cannot be accessed : %w", partKeyPath, err) + return + } + + // If the key is being installed, remove it afterwards. + if install { + // Explicitly ignore any errors + defer func(name string) { + _ = os.Remove(name) + }(partKeyPath) + } + + partdb, err := db.MakeErasableAccessor(partKeyPath) + if err != nil { + return + } + + if keyDilution == 0 { + keyDilution = account.DefaultKeyDilution(firstRound, lastRound) + } + + // Fill the database with new participation keys + newPart, err := account.FillDBWithParticipationKeys(partdb, parsedAddr, firstRound, lastRound, keyDilution) + part = newPart.Participation + partdb.Close() + + if err != nil { + return + } + + if install { + err = installFunc(partKeyPath) + } + return part, partKeyPath, err +} diff --git a/libgoal/participation/participation_test.go b/libgoal/participation/participation_test.go new file mode 100644 index 0000000000..438e298169 --- /dev/null +++ b/libgoal/participation/participation_test.go @@ -0,0 +1,85 @@ +package participation + +import ( + "github.com/algorand/go-algorand/data/account" + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/test/partitiontest" + + "github.com/stretchr/testify/require" + + "testing" +) + +func TestGenParticipationKeysTo_Install(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testcases := []struct { + name string + outDir string + installed bool + }{ + { + name: "install", + installed: true, + }, + { + name: "do not install", + outDir: t.TempDir(), + installed: false, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + var err error + var called bool + installFunc := func(keyPath string) error { + called = true + return nil + } + var addr basics.Address + addr[1] = 1 + + _, _, err = GenParticipationKeysTo(addr.String(), 1000, 2000, 0, tc.outDir, installFunc) + require.NoError(t, err) + require.Equal(t, tc.installed, called, "The install function should only be called when outDir is not set.") + }) + } +} + +func TestGenParticipationKeysTo_DefaultKeyDilution(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + var addr basics.Address + addr[1] = 1 + first := uint64(1000) + last := uint64(2000) + + testcases := []struct { + name string + dilution uint64 + expected uint64 + }{ + { + name: "default", + dilution: 0, + expected: account.DefaultKeyDilution(basics.Round(first), basics.Round(last)), + }, { + name: "override", + dilution: 5, + expected: 5, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + part, _, err := GenParticipationKeysTo(addr.String(), first, last, tc.dilution, t.TempDir(), nil) + require.NoError(t, err) + require.Equal(t, tc.expected, part.KeyDilution) + }) + } +} diff --git a/libgoal/participation_test.go b/libgoal/participation_test.go deleted file mode 100644 index f2579af638..0000000000 --- a/libgoal/participation_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package libgoal - -import ( - "github.com/algorand/go-algorand/data/basics" - - "github.com/stretchr/testify/require" - - "testing" -) - -func TestGenParticipationKeysTo(t *testing.T) { - var called bool - installFunc := func(keyPath string) error { - called = true - return nil - } - var addr basics.Address - addr[1] = 1 - - _, _, err := GenParticipationKeysTo(addr.String(), 1000, 2000, 0, "", installFunc) - require.NoError(t, err) - require.True(t, called, "the install function should have been called") -} From 543a485630bca7b887bcfc5e278610c52d371dc6 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 11 Oct 2023 13:17:54 -0400 Subject: [PATCH 04/19] Implemented. --- cmd/goal/account.go | 5 ++-- daemon/algod/api/server/v2/handlers.go | 37 +++++++++++++++++++------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/cmd/goal/account.go b/cmd/goal/account.go index a833f06e3d..c4cbba63dd 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -41,6 +41,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/util" "github.com/algorand/go-algorand/util/db" @@ -913,10 +914,10 @@ var addParticipationKeyCmd = &cobra.Command{ var part algodAcct.Participation participationGen := func() { installFunc := func(keyPath string) error { - _, err := c.AddParticipationKey(keyPath) + _, err := client.AddParticipationKey(keyPath) return err } - part, _, err = client.GenParticipationKeysTo(accountAddress, roundFirstValid, roundLastValid, keyDilution, partKeyOutDir, installFunc) + part, _, err = participation.GenParticipationKeysTo(accountAddress, roundFirstValid, roundLastValid, keyDilution, partKeyOutDir, installFunc) } util.RunFuncWithSpinningCursor(participationGen) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index b4965db1a4..199ce216aa 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -49,7 +49,7 @@ import ( "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/simulation" - "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/node" "github.com/algorand/go-algorand/protocol" @@ -73,6 +73,7 @@ var WaitForBlockTimeout = 1 * time.Minute // Handlers is an implementation to the V2 route handler interface defined by the generated code. type Handlers struct { + keygen chan struct{} Node NodeInterface Log logging.Logger Shutdown <-chan struct{} @@ -242,8 +243,7 @@ func (v2 *Handlers) GetParticipationKeys(ctx echo.Context) error { return ctx.JSON(http.StatusOK, response) } -func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { - +func (v2 *Handlers) generateKeyHandler(address string, params model.GenerateParticipationKeysParams) error { installFunc := func(path string) error { bytes, err := os.ReadFile(path) if err != nil { @@ -252,19 +252,38 @@ func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, partKeyBinary := bytes if len(partKeyBinary) == 0 { - lenErr := fmt.Errorf(errRESTPayloadZeroLength) - return badRequest(ctx, lenErr, lenErr.Error(), v2.Log) + return fmt.Errorf("cannot install partkey '%s' is empty", partKeyBinary) } partID, err := v2.Node.InstallParticipationKey(partKeyBinary) v2.Log.Infof("Installed participation key %s", partID) return err } - _, _, err := libgoal.GenParticipationKeysTo(address, params.First, params.Last, nilToZero(params.Dilution), "output-directory", installFunc) - if err != nil { - v2.Log.Warnf("Error generating participation keys: %v", err) + _, _, err := participation.GenParticipationKeysTo(address, params.First, params.Last, nilToZero(params.Dilution), "output-directory", installFunc) + return err +} + +func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { + if v2.keygen == nil { + v2.keygen = make(chan struct{}, 1) } - return nil + + select { + case v2.keygen <- struct{}{}: + go func() { + defer func() { + <-v2.keygen + }() + err := v2.generateKeyHandler(address, params) + if err != nil { + v2.Log.Warnf("Error generating participation keys: %v", err) + } + }() + default: + err := fmt.Errorf("participation key generation already in progress") + return badRequest(ctx, err, err.Error(), v2.Log) + } + return ctx.String(http.StatusOK, "{}") } // AddParticipationKey Add a participation key to the node From f904dd59e6a03c21af17d6a60d506df1fb730274 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 11:41:24 -0400 Subject: [PATCH 05/19] Add test and limit concurrent key generations. --- daemon/algod/api/server/v2/handlers.go | 13 ++--- .../algod/api/server/v2/test/handlers_test.go | 51 +++++++++++++++++++ daemon/algod/api/server/v2/test/helpers.go | 2 + 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 199ce216aa..afbceede12 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -73,10 +73,11 @@ var WaitForBlockTimeout = 1 * time.Minute // Handlers is an implementation to the V2 route handler interface defined by the generated code. type Handlers struct { - keygen chan struct{} Node NodeInterface Log logging.Logger Shutdown <-chan struct{} + // Keygen is used to limit the number of concurrent key generation requests. + Keygen chan struct{} } // LedgerForAPI describes the Ledger methods used by the v2 API. @@ -259,20 +260,20 @@ func (v2 *Handlers) generateKeyHandler(address string, params model.GeneratePart v2.Log.Infof("Installed participation key %s", partID) return err } - _, _, err := participation.GenParticipationKeysTo(address, params.First, params.Last, nilToZero(params.Dilution), "output-directory", installFunc) + _, _, err := participation.GenParticipationKeysTo(address, params.First, params.Last, nilToZero(params.Dilution), "", installFunc) return err } func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { - if v2.keygen == nil { - v2.keygen = make(chan struct{}, 1) + if v2.Keygen == nil { + v2.Keygen = make(chan struct{}, 1) } select { - case v2.keygen <- struct{}{}: + case v2.Keygen <- struct{}{}: go func() { defer func() { - <-v2.keygen + <-v2.Keygen }() err := v2.generateKeyHandler(address, params) if err != nil { diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 549db8c5e0..7a9a5a3d19 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -2378,3 +2378,54 @@ func TestRouterRequestBody(t *testing.T) { e.ServeHTTP(rec, req) assert.Equal(t, http.StatusRequestEntityTooLarge, rec.Code) } + +func TestGeneratePartkeys(t *testing.T) { + numAccounts := 1 + numTransactions := 1 + offlineAccounts := true + mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts) + defer releasefunc() + dummyShutdownChan := make(chan struct{}) + mockNode := makeMockNode(mockLedger, t.Name(), nil, cannedStatusReportGolden, false) + handler := v2.Handlers{Node: mockNode, Log: logging.Base(), Shutdown: dummyShutdownChan} + e := echo.New() + + var addr basics.Address + addr[0] = 1 + + { + require.Len(t, mockNode.PartKeyBinary, 0) + req := httptest.NewRequest(http.MethodPost, "/", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + err := handler.GenerateParticipationKeys(c, addr.String(), model.GenerateParticipationKeysParams{ + First: 1000, + Last: 2000, + }) + + require.NoError(t, err) + assert.Equal(t, http.StatusOK, rec.Code) + require.Len(t, mockNode.PartKeyBinary, 0) + + // Wait for keygen to complete + handler.Keygen <- struct{}{} + require.Greater(t, len(mockNode.PartKeyBinary), 0) + <-handler.Keygen + } + + { + req := httptest.NewRequest(http.MethodPost, "/", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + // Simulate a blocked keygen process (and block until the previous keygen is complete) + handler.Keygen <- struct{}{} + err := handler.GenerateParticipationKeys(c, addr.String(), model.GenerateParticipationKeysParams{ + First: 1000, + Last: 2000, + }) + require.NoError(t, err) + assert.Equal(t, http.StatusBadRequest, rec.Code) + } + +} diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go index aedcede912..2f887d25a1 100644 --- a/daemon/algod/api/server/v2/test/helpers.go +++ b/daemon/algod/api/server/v2/test/helpers.go @@ -99,9 +99,11 @@ type mockNode struct { status node.StatusReport devmode bool timestampOffset *int64 + PartKeyBinary []byte } func (m *mockNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) { + m.PartKeyBinary = partKeyBinary return account.ParticipationID{}, nil } From ea8fb2eb54858c2a079edb9f7797bbe9647a43f7 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 11:43:48 -0400 Subject: [PATCH 06/19] Change get to post. --- daemon/algod/api/algod.oas2.json | 2 +- daemon/algod/api/algod.oas3.yml | 2 +- .../generated/participating/private/routes.go | 420 +++++++++--------- 3 files changed, 212 insertions(+), 212 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 1133444da3..cc71bbd7e8 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -935,7 +935,7 @@ } }, "/v2/participation/generate/{address}": { - "get": { + "post": { "tags": [ "private", "participating" diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index c3e97334b3..053eaddef5 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -5380,7 +5380,7 @@ } }, "/v2/participation/generate/{address}": { - "get": { + "post": { "operationId": "GenerateParticipationKeys", "parameters": [ { diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index 3fa3bd6a48..22cbc29bfb 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -28,7 +28,7 @@ type ServerInterface interface { // (POST /v2/participation) AddParticipationKey(ctx echo.Context) error // Generate and install participation keys to the node. - // (GET /v2/participation/generate/{address}) + // (POST /v2/participation/generate/{address}) GenerateParticipationKeys(ctx echo.Context, address string, params GenerateParticipationKeysParams) error // Delete a given participation key by ID // (DELETE /v2/participation/{participation-id}) @@ -193,7 +193,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/v2/participation", wrapper.GetParticipationKeys, m...) router.POST(baseURL+"/v2/participation", wrapper.AddParticipationKey, m...) - router.GET(baseURL+"/v2/participation/generate/:address", wrapper.GenerateParticipationKeys, m...) + router.POST(baseURL+"/v2/participation/generate/:address", wrapper.GenerateParticipationKeys, m...) router.DELETE(baseURL+"/v2/participation/:participation-id", wrapper.DeleteParticipationKeyByID, m...) router.GET(baseURL+"/v2/participation/:participation-id", wrapper.GetParticipationKeyByID, m...) router.POST(baseURL+"/v2/participation/:participation-id", wrapper.AppendKeys, m...) @@ -204,214 +204,214 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file From 38ced2aeddf1a06b369cccbd88ebc7c5084483c1 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 12:43:58 -0400 Subject: [PATCH 07/19] reviewdog warnings. --- cmd/goal/account.go | 4 ++-- daemon/algod/api/server/v2/handlers.go | 2 ++ daemon/algod/api/server/v2/test/handlers_test.go | 3 +++ data/account/participation.go | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/goal/account.go b/cmd/goal/account.go index c4cbba63dd..60b8ce72d2 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -914,8 +914,8 @@ var addParticipationKeyCmd = &cobra.Command{ var part algodAcct.Participation participationGen := func() { installFunc := func(keyPath string) error { - _, err := client.AddParticipationKey(keyPath) - return err + _, installErr := client.AddParticipationKey(keyPath) + return installErr } part, _, err = participation.GenParticipationKeysTo(accountAddress, roundFirstValid, roundLastValid, keyDilution, partKeyOutDir, installFunc) } diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index afbceede12..1709290717 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -264,6 +264,8 @@ func (v2 *Handlers) generateKeyHandler(address string, params model.GeneratePart return err } +// GenerateParticipationKeys generates and installs participation keys to the node. +// (POST /v2/participation/generate/{address}) func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { if v2.Keygen == nil { v2.Keygen = make(chan struct{}, 1) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 7a9a5a3d19..174b7b1c9b 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -2380,6 +2380,9 @@ func TestRouterRequestBody(t *testing.T) { } func TestGeneratePartkeys(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + numAccounts := 1 numTransactions := 1 offlineAccounts := true diff --git a/data/account/participation.go b/data/account/participation.go index c67191725e..99c0fadfa8 100644 --- a/data/account/participation.go +++ b/data/account/participation.go @@ -216,6 +216,7 @@ func (part PersistedParticipation) PersistNewParent() error { }) } +// DefaultKeyDilutiton computes the default diluation based on first and last rounds. func DefaultKeyDilution(first, last basics.Round) uint64 { return 1 + uint64(math.Sqrt(float64(last-first))) } From 57265e6627f680136692760f1eca2be0d15f44a6 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 12:47:14 -0400 Subject: [PATCH 08/19] Fix warnings. --- libgoal/participation/participation_test.go | 4 ++++ .../participation/accountParticipationTransitions_test.go | 5 +++-- test/e2e-go/features/stateproofs/stateproofs_test.go | 5 +++-- test/e2e-go/features/transactions/onlineStatusChange_test.go | 5 +++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/libgoal/participation/participation_test.go b/libgoal/participation/participation_test.go index 438e298169..0968cb3966 100644 --- a/libgoal/participation/participation_test.go +++ b/libgoal/participation/participation_test.go @@ -33,6 +33,8 @@ func TestGenParticipationKeysTo_Install(t *testing.T) { for _, tc := range testcases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + var err error var called bool installFunc := func(keyPath string) error { @@ -77,6 +79,8 @@ func TestGenParticipationKeysTo_DefaultKeyDilution(t *testing.T) { for _, tc := range testcases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + part, _, err := GenParticipationKeysTo(addr.String(), first, last, tc.dilution, t.TempDir(), nil) require.NoError(t, err) require.Equal(t, tc.expected, part.KeyDilution) diff --git a/test/e2e-go/features/participation/accountParticipationTransitions_test.go b/test/e2e-go/features/participation/accountParticipationTransitions_test.go index 85a7676b57..987d9de809 100644 --- a/test/e2e-go/features/participation/accountParticipationTransitions_test.go +++ b/test/e2e-go/features/participation/accountParticipationTransitions_test.go @@ -32,6 +32,7 @@ import ( "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/account" "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -40,10 +41,10 @@ import ( func installParticipationKey(t *testing.T, client libgoal.Client, addr string, firstValid, lastValid uint64) (resp model.PostParticipationResponse, part account.Participation, err error) { // Install overlapping participation keys... installFunc := func(keyPath string) error { - _, err := c.AddParticipationKey(keyPath) + _, err := client.AddParticipationKey(keyPath) return err } - part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, t.TempDir(), installFunc) + part, filePath, err := participation.GenParticipationKeysTo(addr, firstValid, lastValid, 100, t.TempDir(), installFunc) require.NoError(t, err) require.NotNil(t, filePath) require.Equal(t, addr, part.Parent.String()) diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go index 4d133f73c3..dd90d1f708 100644 --- a/test/e2e-go/features/stateproofs/stateproofs_test.go +++ b/test/e2e-go/features/stateproofs/stateproofs_test.go @@ -41,6 +41,7 @@ import ( "github.com/algorand/go-algorand/data/stateproofmsg" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/nodecontrol" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" @@ -678,10 +679,10 @@ func installParticipationKey(t *testing.T, client libgoal.Client, addr string, f // Install overlapping participation keys... installFunc := func(keyPath string) error { - _, err := c.AddParticipationKey(keyPath) + _, err := client.AddParticipationKey(keyPath) return err } - part, filePath, err := client.GenParticipationKeysTo(addr, firstValid, lastValid, 100, dir, installFunc) + part, filePath, err := participation.GenParticipationKeysTo(addr, firstValid, lastValid, 100, dir, installFunc) require.NoError(t, err) require.NotNil(t, filePath) require.Equal(t, addr, part.Parent.String()) diff --git a/test/e2e-go/features/transactions/onlineStatusChange_test.go b/test/e2e-go/features/transactions/onlineStatusChange_test.go index d47294cd1f..9c21ccc305 100644 --- a/test/e2e-go/features/transactions/onlineStatusChange_test.go +++ b/test/e2e-go/features/transactions/onlineStatusChange_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/libgoal/participation" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -172,10 +173,10 @@ func TestCloseOnError(t *testing.T) { var partkeyFile string installFunc := func(keyPath string) error { - _, err := c.AddParticipationKey(keyPath) + _, err := client.AddParticipationKey(keyPath) return err } - _, partkeyFile, err = client.GenParticipationKeysTo(initiallyOffline, 0, curRound+1000, 0, t.TempDir(), installFunc) + _, partkeyFile, err = participation.GenParticipationKeysTo(initiallyOffline, 0, curRound+1000, 0, t.TempDir(), installFunc) a.NoError(err) // make a participation key for initiallyOffline From f4e521228bc905002002e4c6f0461f92d1bc7d1a Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 13:34:16 -0400 Subject: [PATCH 09/19] Avoid data race. --- daemon/algod/api/server/v2/test/handlers_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 174b7b1c9b..1763a8de2b 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -2409,7 +2409,6 @@ func TestGeneratePartkeys(t *testing.T) { require.NoError(t, err) assert.Equal(t, http.StatusOK, rec.Code) - require.Len(t, mockNode.PartKeyBinary, 0) // Wait for keygen to complete handler.Keygen <- struct{}{} From 3db34fa8ca5efd6eebc5e2fad516499ab6cdd2f0 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 15:43:26 -0400 Subject: [PATCH 10/19] typos --- data/account/participation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/account/participation.go b/data/account/participation.go index 99c0fadfa8..6f76413252 100644 --- a/data/account/participation.go +++ b/data/account/participation.go @@ -216,7 +216,7 @@ func (part PersistedParticipation) PersistNewParent() error { }) } -// DefaultKeyDilutiton computes the default diluation based on first and last rounds. +// DefaultKeyDilution computes the default dilution based on first and last rounds. func DefaultKeyDilution(first, last basics.Round) uint64 { return 1 + uint64(math.Sqrt(float64(last-first))) } From c7a0ab93d871e7834fd18ad8cd7e885ce48735d1 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 12 Oct 2023 16:01:56 -0400 Subject: [PATCH 11/19] Add license. --- libgoal/participation/participation.go | 16 ++++++++++++++++ libgoal/participation/participation_test.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/libgoal/participation/participation.go b/libgoal/participation/participation.go index 9a46948e05..0b0b1c6e9a 100644 --- a/libgoal/participation/participation.go +++ b/libgoal/participation/participation.go @@ -1,3 +1,19 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + package participation import ( diff --git a/libgoal/participation/participation_test.go b/libgoal/participation/participation_test.go index 0968cb3966..44f8c5a675 100644 --- a/libgoal/participation/participation_test.go +++ b/libgoal/participation/participation_test.go @@ -1,3 +1,19 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + package participation import ( From 01bca53be499dd03a1bd3650bc486c1f20529f7e Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 13 Oct 2023 11:24:08 -0400 Subject: [PATCH 12/19] Reuse DefaultKeyDilution function --- cmd/algokey/part.go | 3 +-- data/account/participationRegistry_test.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/algokey/part.go b/cmd/algokey/part.go index 034b9821a5..76148ae1f6 100644 --- a/cmd/algokey/part.go +++ b/cmd/algokey/part.go @@ -19,7 +19,6 @@ package main import ( "encoding/base64" "fmt" - "math" "os" "github.com/spf13/cobra" @@ -58,7 +57,7 @@ var partGenerateCmd = &cobra.Command{ } if partKeyDilution == 0 { - partKeyDilution = 1 + uint64(math.Sqrt(float64(partLastRound-partFirstRound))) + partKeyDilution = account.DefaultKeyDilution(basics.Round(partFirstRound), basics.Round(partLastRound)) } var err error diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go index ef77283ce7..e0328bb7f8 100644 --- a/data/account/participationRegistry_test.go +++ b/data/account/participationRegistry_test.go @@ -23,7 +23,6 @@ import ( "encoding/binary" "errors" "fmt" - "math" "os" "path/filepath" "strconv" @@ -99,7 +98,7 @@ func makeTestParticipationWithLifetime(a *require.Assertions, addrID int, first, // Generate part keys like in partGenerateCmd and FillDBWithParticipationKeys if dilution == 0 { - dilution = 1 + uint64(math.Sqrt(float64(last-first))) + dilution = DefaultKeyDilution(first, last) } // Compute how many distinct participation keys we should generate From b946e6b2aee1ab366895ef448097b84d5692e06a Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 13 Oct 2023 11:29:51 -0400 Subject: [PATCH 13/19] Update DefaultKeyDilution with comment from algokey parameter. --- data/account/participation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/account/participation.go b/data/account/participation.go index 6f76413252..22130df6c1 100644 --- a/data/account/participation.go +++ b/data/account/participation.go @@ -216,7 +216,7 @@ func (part PersistedParticipation) PersistNewParent() error { }) } -// DefaultKeyDilution computes the default dilution based on first and last rounds. +// DefaultKeyDilution computes the default dilution based on first and last rounds as the sqrt of validity window. func DefaultKeyDilution(first, last basics.Round) uint64 { return 1 + uint64(math.Sqrt(float64(last-first))) } From bf3da2ec3c12e2c5f6d35ded44ce488e4eac148b Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 08:53:51 -0400 Subject: [PATCH 14/19] Fix concurrency issue with a semaphore and proper initialization. --- daemon/algod/api/server/router.go | 2 ++ daemon/algod/api/server/v2/handlers.go | 27 +++++++------------ .../algod/api/server/v2/test/handlers_test.go | 18 +++++++++---- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index b599194ab3..11d308abfb 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -19,6 +19,7 @@ package server import ( "fmt" + "golang.org/x/sync/semaphore" "net" "net/http" @@ -122,6 +123,7 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str Node: node, Log: logger, Shutdown: shutdown, + Limiter: semaphore.NewWeighted(1), } nppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...) npprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 1709290717..1115dc6334 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -22,6 +22,7 @@ import ( "encoding/base64" "errors" "fmt" + "golang.org/x/sync/semaphore" "io" "math" "net/http" @@ -76,8 +77,7 @@ type Handlers struct { Node NodeInterface Log logging.Logger Shutdown <-chan struct{} - // Keygen is used to limit the number of concurrent key generation requests. - Keygen chan struct{} + Limiter *semaphore.Weighted } // LedgerForAPI describes the Ledger methods used by the v2 API. @@ -267,22 +267,13 @@ func (v2 *Handlers) generateKeyHandler(address string, params model.GeneratePart // GenerateParticipationKeys generates and installs participation keys to the node. // (POST /v2/participation/generate/{address}) func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { - if v2.Keygen == nil { - v2.Keygen = make(chan struct{}, 1) - } - - select { - case v2.Keygen <- struct{}{}: - go func() { - defer func() { - <-v2.Keygen - }() - err := v2.generateKeyHandler(address, params) - if err != nil { - v2.Log.Warnf("Error generating participation keys: %v", err) - } - }() - default: + if v2.Limiter == nil || v2.Limiter.TryAcquire(1) { + defer v2.Limiter.Release(1) + err := v2.generateKeyHandler(address, params) + if err != nil { + v2.Log.Warnf("Error generating participation keys: %v", err) + } + } else { err := fmt.Errorf("participation key generation already in progress") return badRequest(ctx, err, err.Error(), v2.Log) } diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 1763a8de2b..abfe432d62 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "golang.org/x/sync/semaphore" "io" "math" "net" @@ -2390,7 +2391,12 @@ func TestGeneratePartkeys(t *testing.T) { defer releasefunc() dummyShutdownChan := make(chan struct{}) mockNode := makeMockNode(mockLedger, t.Name(), nil, cannedStatusReportGolden, false) - handler := v2.Handlers{Node: mockNode, Log: logging.Base(), Shutdown: dummyShutdownChan} + handler := v2.Handlers{ + Node: mockNode, + Log: logging.Base(), + Shutdown: dummyShutdownChan, + Limiter: semaphore.NewWeighted(1), + } e := echo.New() var addr basics.Address @@ -2411,9 +2417,10 @@ func TestGeneratePartkeys(t *testing.T) { assert.Equal(t, http.StatusOK, rec.Code) // Wait for keygen to complete - handler.Keygen <- struct{}{} + err = handler.Limiter.Acquire(context.Background(), 1) + require.NoError(t, err) require.Greater(t, len(mockNode.PartKeyBinary), 0) - <-handler.Keygen + handler.Limiter.Release(1) } { @@ -2421,8 +2428,9 @@ func TestGeneratePartkeys(t *testing.T) { rec := httptest.NewRecorder() c := e.NewContext(req, rec) // Simulate a blocked keygen process (and block until the previous keygen is complete) - handler.Keygen <- struct{}{} - err := handler.GenerateParticipationKeys(c, addr.String(), model.GenerateParticipationKeysParams{ + err := handler.Limiter.Acquire(context.Background(), 1) + require.NoError(t, err) + err = handler.GenerateParticipationKeys(c, addr.String(), model.GenerateParticipationKeysParams{ First: 1000, Last: 2000, }) From 81947ab7ac85736c386daaadba56f08fd7be027e Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 08:59:52 -0400 Subject: [PATCH 15/19] Update OpenAPI definition. --- daemon/algod/api/algod.oas2.json | 8 +- daemon/algod/api/algod.oas3.yml | 12 +- .../generated/participating/private/routes.go | 419 +++++++++--------- 3 files changed, 212 insertions(+), 227 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index cc71bbd7e8..b37f2001c4 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -979,7 +979,7 @@ ], "responses": { "200": { - "description": "The current swagger spec", + "description": "An empty JSON object is returned if the generation process was started. Currently no status is available.", "schema": { "type": "string" } @@ -996,12 +996,6 @@ "$ref": "#/definitions/ErrorResponse" } }, - "404": { - "description": "Participation Key Not Found", - "schema": { - "$ref": "#/definitions/ErrorResponse" - } - }, "500": { "description": "Internal Error", "schema": { diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index 053eaddef5..c8c82e0d98 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -5428,7 +5428,7 @@ } } }, - "description": "The current swagger spec" + "description": "An empty JSON object is returned if the generation process was started. Currently no status is available." }, "400": { "content": { @@ -5450,16 +5450,6 @@ }, "description": "Invalid API Token" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Participation Key Not Found" - }, "500": { "content": { "application/json": { diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index 22cbc29bfb..79a65f5721 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -203,215 +203,216 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "CB++b8jjQ3OPODL5+P7j/wsAAP//bjFnIYoHAQA=", } // GetSwagger returns the content of the embedded swagger specification file From aa13fde385ee604e47913dd580ae89cff13c6aed Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 10:50:32 -0400 Subject: [PATCH 16/19] More error handling and test cases. --- libgoal/participation/participation.go | 5 ++++- libgoal/participation/participation_test.go | 5 +++++ .../participation/accountParticipationTransitions_test.go | 4 ++-- test/e2e-go/features/stateproofs/stateproofs_test.go | 4 ++-- test/e2e-go/features/transactions/onlineStatusChange_test.go | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libgoal/participation/participation.go b/libgoal/participation/participation.go index 0b0b1c6e9a..2d1a30e8c0 100644 --- a/libgoal/participation/participation.go +++ b/libgoal/participation/participation.go @@ -40,7 +40,10 @@ func participationKeysPath(dataDir string, address basics.Address, firstValid, l // directory is empty, the key will be installed. func GenParticipationKeysTo(address string, firstValid, lastValid, keyDilution uint64, outDir string, installFunc func(keyPath string) error) (part account.Participation, filePath string, err error) { - install := outDir == "" && installFunc != nil + install := outDir == "" + if install && installFunc == nil { + return account.Participation{}, "", fmt.Errorf("must provide an install function when installing keys") + } // Parse the address parsedAddr, err := basics.UnmarshalChecksumAddress(address) diff --git a/libgoal/participation/participation_test.go b/libgoal/participation/participation_test.go index 44f8c5a675..0b0e6748da 100644 --- a/libgoal/participation/participation_test.go +++ b/libgoal/participation/participation_test.go @@ -103,3 +103,8 @@ func TestGenParticipationKeysTo_DefaultKeyDilution(t *testing.T) { }) } } + +func TestBadInput(t *testing.T) { + _, _, err := GenParticipationKeysTo("", 0, 0, 0, "", nil) + require.ErrorContains(t, err, "must provide an install function when installing keys") +} diff --git a/test/e2e-go/features/participation/accountParticipationTransitions_test.go b/test/e2e-go/features/participation/accountParticipationTransitions_test.go index 987d9de809..6752af841b 100644 --- a/test/e2e-go/features/participation/accountParticipationTransitions_test.go +++ b/test/e2e-go/features/participation/accountParticipationTransitions_test.go @@ -21,6 +21,7 @@ package participation // deterministic. import ( + "errors" "fmt" "path/filepath" "testing" @@ -41,8 +42,7 @@ import ( func installParticipationKey(t *testing.T, client libgoal.Client, addr string, firstValid, lastValid uint64) (resp model.PostParticipationResponse, part account.Participation, err error) { // Install overlapping participation keys... installFunc := func(keyPath string) error { - _, err := client.AddParticipationKey(keyPath) - return err + return errors.New("the install directory is provided, so keys should not be installed") } part, filePath, err := participation.GenParticipationKeysTo(addr, firstValid, lastValid, 100, t.TempDir(), installFunc) require.NoError(t, err) diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go index dd90d1f708..b58669a6a6 100644 --- a/test/e2e-go/features/stateproofs/stateproofs_test.go +++ b/test/e2e-go/features/stateproofs/stateproofs_test.go @@ -18,6 +18,7 @@ package stateproofs import ( "bytes" + "errors" "fmt" "os" "path/filepath" @@ -679,8 +680,7 @@ func installParticipationKey(t *testing.T, client libgoal.Client, addr string, f // Install overlapping participation keys... installFunc := func(keyPath string) error { - _, err := client.AddParticipationKey(keyPath) - return err + return errors.New("the install directory is provided, so keys should not be installed") } part, filePath, err := participation.GenParticipationKeysTo(addr, firstValid, lastValid, 100, dir, installFunc) require.NoError(t, err) diff --git a/test/e2e-go/features/transactions/onlineStatusChange_test.go b/test/e2e-go/features/transactions/onlineStatusChange_test.go index 9c21ccc305..7a9e6b3467 100644 --- a/test/e2e-go/features/transactions/onlineStatusChange_test.go +++ b/test/e2e-go/features/transactions/onlineStatusChange_test.go @@ -17,6 +17,7 @@ package transactions import ( + "errors" "fmt" "path/filepath" "testing" @@ -173,8 +174,7 @@ func TestCloseOnError(t *testing.T) { var partkeyFile string installFunc := func(keyPath string) error { - _, err := client.AddParticipationKey(keyPath) - return err + return errors.New("the install directory is provided, so keys should not be installed") } _, partkeyFile, err = participation.GenParticipationKeysTo(initiallyOffline, 0, curRound+1000, 0, t.TempDir(), installFunc) a.NoError(err) From 184a31fb365eb97afc5b87e80e3bf9424df2d786 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 15:55:53 -0400 Subject: [PATCH 17/19] More fixes. --- daemon/algod/api/server/router.go | 8 +++---- daemon/algod/api/server/v2/handlers.go | 22 ++++++++++--------- .../algod/api/server/v2/test/handlers_test.go | 14 ++++++------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index 11d308abfb..e380cb026d 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -120,10 +120,10 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str // Registering v2 routes v2Handler := v2.Handlers{ - Node: node, - Log: logger, - Shutdown: shutdown, - Limiter: semaphore.NewWeighted(1), + Node: node, + Log: logger, + Shutdown: shutdown, + KeygenLimiter: semaphore.NewWeighted(1), } nppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...) npprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 1115dc6334..d7883785ee 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -74,10 +74,10 @@ var WaitForBlockTimeout = 1 * time.Minute // Handlers is an implementation to the V2 route handler interface defined by the generated code. type Handlers struct { - Node NodeInterface - Log logging.Logger - Shutdown <-chan struct{} - Limiter *semaphore.Weighted + Node NodeInterface + Log logging.Logger + Shutdown <-chan struct{} + KeygenLimiter *semaphore.Weighted } // LedgerForAPI describes the Ledger methods used by the v2 API. @@ -267,12 +267,14 @@ func (v2 *Handlers) generateKeyHandler(address string, params model.GeneratePart // GenerateParticipationKeys generates and installs participation keys to the node. // (POST /v2/participation/generate/{address}) func (v2 *Handlers) GenerateParticipationKeys(ctx echo.Context, address string, params model.GenerateParticipationKeysParams) error { - if v2.Limiter == nil || v2.Limiter.TryAcquire(1) { - defer v2.Limiter.Release(1) - err := v2.generateKeyHandler(address, params) - if err != nil { - v2.Log.Warnf("Error generating participation keys: %v", err) - } + if v2.KeygenLimiter != nil && v2.KeygenLimiter.TryAcquire(1) { + go func() { + defer v2.KeygenLimiter.Release(1) + err := v2.generateKeyHandler(address, params) + if err != nil { + v2.Log.Warnf("Error generating participation keys: %v", err) + } + }() } else { err := fmt.Errorf("participation key generation already in progress") return badRequest(ctx, err, err.Error(), v2.Log) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index abfe432d62..1d5c425cd4 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -2392,10 +2392,10 @@ func TestGeneratePartkeys(t *testing.T) { dummyShutdownChan := make(chan struct{}) mockNode := makeMockNode(mockLedger, t.Name(), nil, cannedStatusReportGolden, false) handler := v2.Handlers{ - Node: mockNode, - Log: logging.Base(), - Shutdown: dummyShutdownChan, - Limiter: semaphore.NewWeighted(1), + Node: mockNode, + Log: logging.Base(), + Shutdown: dummyShutdownChan, + KeygenLimiter: semaphore.NewWeighted(1), } e := echo.New() @@ -2417,10 +2417,10 @@ func TestGeneratePartkeys(t *testing.T) { assert.Equal(t, http.StatusOK, rec.Code) // Wait for keygen to complete - err = handler.Limiter.Acquire(context.Background(), 1) + err = handler.KeygenLimiter.Acquire(context.Background(), 1) require.NoError(t, err) require.Greater(t, len(mockNode.PartKeyBinary), 0) - handler.Limiter.Release(1) + handler.KeygenLimiter.Release(1) } { @@ -2428,7 +2428,7 @@ func TestGeneratePartkeys(t *testing.T) { rec := httptest.NewRecorder() c := e.NewContext(req, rec) // Simulate a blocked keygen process (and block until the previous keygen is complete) - err := handler.Limiter.Acquire(context.Background(), 1) + err := handler.KeygenLimiter.Acquire(context.Background(), 1) require.NoError(t, err) err = handler.GenerateParticipationKeys(c, addr.String(), model.GenerateParticipationKeysParams{ First: 1000, From 5411434ded7c23ed55b991f672de2fc884d09f7d Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 15:57:55 -0400 Subject: [PATCH 18/19] Missing parallel test. --- libgoal/participation/participation_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libgoal/participation/participation_test.go b/libgoal/participation/participation_test.go index 0b0e6748da..69565a7800 100644 --- a/libgoal/participation/participation_test.go +++ b/libgoal/participation/participation_test.go @@ -105,6 +105,9 @@ func TestGenParticipationKeysTo_DefaultKeyDilution(t *testing.T) { } func TestBadInput(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + _, _, err := GenParticipationKeysTo("", 0, 0, 0, "", nil) require.ErrorContains(t, err, "must provide an install function when installing keys") } From 733885d6a51c156d7e303e6d9d011d6ccd8e1aad Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 16 Oct 2023 15:58:38 -0400 Subject: [PATCH 19/19] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 50d63ab397..86eaa895a2 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.11.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 + golang.org/x/sync v0.3.0 golang.org/x/sys v0.10.0 golang.org/x/text v0.11.0 gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 @@ -155,7 +156,6 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.12.0 // indirect - golang.org/x/sync v0.3.0 // indirect golang.org/x/term v0.10.0 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect golang.org/x/tools v0.11.0 // indirect