summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Winder <wwinder.unh@gmail.com>2022-01-06 10:09:30 -0500
committerGitHub <noreply@github.com>2022-01-06 10:09:30 -0500
commitcb1650ee321b8571d5b29dac1e35217ab7a11aa1 (patch)
treef077a9ec1f94cafd2c4c62c86c2d2660713a3e82
parent7248b958706a3aa401a7713827eb63324f236ddc (diff)
PKI State Proof Incremental Key Loading (#3281)
## Summary Followup to #3261 (contained in diff). Use the new key loading routine from the REST API. ## Test Plan New unit tests.
-rw-r--r--daemon/algod/api/algod.oas2.json100
-rw-r--r--daemon/algod/api/algod.oas3.yml137
-rw-r--r--daemon/algod/api/server/v2/generated/private/routes.go313
-rw-r--r--daemon/algod/api/server/v2/generated/private/types.go2
-rw-r--r--daemon/algod/api/server/v2/generated/routes.go346
-rw-r--r--daemon/algod/api/server/v2/generated/types.go2
-rw-r--r--daemon/algod/api/server/v2/handlers.go29
-rw-r--r--daemon/algod/api/server/v2/test/handlers_test.go111
-rw-r--r--daemon/algod/api/server/v2/test/helpers.go12
-rw-r--r--data/account/msgp_gen.go160
-rw-r--r--data/account/msgp_gen_test.go60
-rw-r--r--data/account/participationRegistry.go36
-rw-r--r--node/node.go25
13 files changed, 967 insertions, 366 deletions
diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json
index c6e2816d8..3e258d36f 100644
--- a/daemon/algod/api/algod.oas2.json
+++ b/daemon/algod/api/algod.oas2.json
@@ -494,7 +494,7 @@
}
},
"404": {
- "description": "Application Not Found",
+ "description": "Participation Key Not Found",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
@@ -553,6 +553,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
+ "404": {
+ "description": "Participation Key Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
"500": {
"description": "Internal Error",
"schema": {
@@ -576,14 +582,14 @@
"tags": [
"private"
],
- "description": "Delete a given participation key by id",
+ "description": "Delete a given participation key by ID",
"produces": [
"application/json"
],
"schemes": [
"http"
],
- "summary": "Delete a given participation key by id",
+ "summary": "Delete a given participation key by ID",
"operationId": "DeleteParticipationKeyByID",
"responses": {
"200": {
@@ -601,6 +607,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
+ "404": {
+ "description": "Participation Key Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
"500": {
"description": "Internal Error",
"schema": {
@@ -616,14 +628,14 @@
"tags": [
"private"
],
- "description": "Given a participation id, return information about that participation key",
+ "description": "Given a participation ID, return information about that participation key",
"produces": [
"application/json"
],
"schemes": [
"http"
],
- "summary": "Get participation key info by id",
+ "summary": "Get participation key info given a participation ID",
"operationId": "GetParticipationKeyByID",
"responses": {
"200": {
@@ -643,7 +655,69 @@
}
},
"404": {
- "description": "Application Not Found",
+ "description": "Participation Key Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Error",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "default": {
+ "description": "Unknown Error"
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "private"
+ ],
+ "description": "Given a participation ID, append state proof keys to a particular set of participation keys",
+ "consumes": [
+ "application/msgpack"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "description": "The state proof keys to add to an existing participation ID",
+ "name": "keymap",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ ],
+ "schemes": [
+ "http"
+ ],
+ "summary": "Append state proof keys to a participation key",
+ "operationId": "AppendKeys",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "$ref": "#/responses/ParticipationKeyResponse"
+ },
+ "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"
}
@@ -934,7 +1008,7 @@
},
"/v2/transactions/pending/{txid}": {
"get": {
- "description": "Given a transaction id of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round \u003e 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.\n",
+ "description": "Given a transaction ID of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round \u003e 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.\n",
"produces": [
"application/json",
"application/msgpack"
@@ -948,7 +1022,7 @@
{
"pattern": "[A-Z0-9]+",
"type": "string",
- "description": "A transaction id",
+ "description": "A transaction ID",
"name": "txid",
"in": "path",
"required": true
@@ -959,7 +1033,7 @@
],
"responses": {
"200": {
- "description": "Given a transaction id of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round \u003e 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\n\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.",
+ "description": "Given a transaction ID of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round \u003e 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\n\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.",
"schema": {
"$ref": "#/definitions/PendingTransactionResponse"
}
@@ -998,7 +1072,7 @@
},
"/v2/applications/{application-id}": {
"get": {
- "description": "Given a application id, it returns application information including creator, approval and clear programs, global and local schemas, and global state.",
+ "description": "Given a application ID, it returns application information including creator, approval and clear programs, global and local schemas, and global state.",
"produces": [
"application/json"
],
@@ -1061,7 +1135,7 @@
},
"/v2/assets/{asset-id}": {
"get": {
- "description": "Given a asset id, it returns asset information including creator, name, total supply and special addresses.",
+ "description": "Given a asset ID, it returns asset information including creator, name, total supply and special addresses.",
"produces": [
"application/json"
],
@@ -2576,7 +2650,7 @@
}
},
"ParticipationKeyResponse": {
- "description": "A detailed description of a participation id",
+ "description": "A detailed description of a participation ID",
"schema": {
"$ref": "#/definitions/ParticipationKey"
}
@@ -2593,7 +2667,7 @@
],
"properties": {
"partId": {
- "description": "encoding of the participation id.",
+ "description": "encoding of the participation ID.",
"type": "string"
}
}
diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml
index 86ac76a5d..538136733 100644
--- a/daemon/algod/api/algod.oas3.yml
+++ b/daemon/algod/api/algod.oas3.yml
@@ -465,7 +465,7 @@
}
}
},
- "description": "A detailed description of a participation id"
+ "description": "A detailed description of a participation ID"
},
"ParticipationKeysResponse": {
"content": {
@@ -516,7 +516,7 @@
"schema": {
"properties": {
"partId": {
- "description": "encoding of the participation id.",
+ "description": "encoding of the participation ID.",
"type": "string"
}
},
@@ -1898,7 +1898,7 @@
},
"/v2/applications/{application-id}": {
"get": {
- "description": "Given a application id, it returns application information including creator, approval and clear programs, global and local schemas, and global state.",
+ "description": "Given a application ID, it returns application information including creator, approval and clear programs, global and local schemas, and global state.",
"operationId": "GetApplicationByID",
"parameters": [
{
@@ -1972,7 +1972,7 @@
},
"/v2/assets/{asset-id}": {
"get": {
- "description": "Given a asset id, it returns asset information including creator, name, total supply and special addresses.",
+ "description": "Given a asset ID, it returns asset information including creator, name, total supply and special addresses.",
"operationId": "GetAssetByID",
"parameters": [
{
@@ -2582,7 +2582,7 @@
}
}
},
- "description": "Application Not Found"
+ "description": "Participation Key Not Found"
},
"500": {
"content": {
@@ -2625,7 +2625,7 @@
"schema": {
"properties": {
"partId": {
- "description": "encoding of the participation id.",
+ "description": "encoding of the participation ID.",
"type": "string"
}
},
@@ -2658,6 +2658,16 @@
},
"description": "Invalid API Token"
},
+ "404": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Participation Key Not Found"
+ },
"500": {
"content": {
"application/json": {
@@ -2692,7 +2702,7 @@
},
"/v2/participation/{participation-id}": {
"delete": {
- "description": "Delete a given participation key by id",
+ "description": "Delete a given participation key by ID",
"operationId": "DeleteParticipationKeyByID",
"parameters": [
{
@@ -2729,6 +2739,16 @@
},
"description": "Invalid API Token"
},
+ "404": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Participation Key Not Found"
+ },
"500": {
"content": {
"application/json": {
@@ -2744,13 +2764,13 @@
"description": "Unknown Error"
}
},
- "summary": "Delete a given participation key by id",
+ "summary": "Delete a given participation key by ID",
"tags": [
"private"
]
},
"get": {
- "description": "Given a participation id, return information about that participation key",
+ "description": "Given a participation ID, return information about that participation key",
"operationId": "GetParticipationKeyByID",
"parameters": [
{
@@ -2771,7 +2791,7 @@
}
}
},
- "description": "A detailed description of a participation id"
+ "description": "A detailed description of a participation ID"
},
"400": {
"content": {
@@ -2801,7 +2821,7 @@
}
}
},
- "description": "Application Not Found"
+ "description": "Participation Key Not Found"
},
"500": {
"content": {
@@ -2818,10 +2838,97 @@
"description": "Unknown Error"
}
},
- "summary": "Get participation key info by id",
+ "summary": "Get participation key info given a participation ID",
"tags": [
"private"
]
+ },
+ "post": {
+ "description": "Given a participation ID, append state proof keys to a particular set of participation keys",
+ "operationId": "AppendKeys",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "participation-id",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "format": "binary",
+ "type": "string"
+ }
+ }
+ },
+ "description": "The state proof keys to add to an existing participation ID",
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ParticipationKey"
+ }
+ }
+ },
+ "description": "A detailed description of a participation ID"
+ },
+ "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"
+ },
+ "default": {
+ "content": {},
+ "description": "Unknown Error"
+ }
+ },
+ "summary": "Append state proof keys to a participation key",
+ "tags": [
+ "private"
+ ],
+ "x-codegen-request-body-name": "keymap"
}
},
"/v2/shutdown": {
@@ -3604,11 +3711,11 @@
},
"/v2/transactions/pending/{txid}": {
"get": {
- "description": "Given a transaction id of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round > 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.\n",
+ "description": "Given a transaction ID of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round > 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.\n",
"operationId": "PendingTransactionInformation",
"parameters": [
{
- "description": "A transaction id",
+ "description": "A transaction ID",
"in": "path",
"name": "txid",
"required": true,
@@ -3644,7 +3751,7 @@
}
}
},
- "description": "Given a transaction id of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round > 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\n\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error."
+ "description": "Given a transaction ID of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round > 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\n\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error."
},
"400": {
"content": {
diff --git a/daemon/algod/api/server/v2/generated/private/routes.go b/daemon/algod/api/server/v2/generated/private/routes.go
index 56baf1f2b..abfd37f7d 100644
--- a/daemon/algod/api/server/v2/generated/private/routes.go
+++ b/daemon/algod/api/server/v2/generated/private/routes.go
@@ -29,12 +29,15 @@ type ServerInterface interface {
// Add a participation key to the node
// (POST /v2/participation)
AddParticipationKey(ctx echo.Context) error
- // Delete a given participation key by id
+ // Delete a given participation key by ID
// (DELETE /v2/participation/{participation-id})
DeleteParticipationKeyByID(ctx echo.Context, participationId string) error
- // Get participation key info by id
+ // Get participation key info given a participation ID
// (GET /v2/participation/{participation-id})
GetParticipationKeyByID(ctx echo.Context, participationId string) error
+ // Append state proof keys to a participation key
+ // (POST /v2/participation/{participation-id})
+ AppendKeys(ctx echo.Context, participationId string) error
// (POST /v2/shutdown)
ShutdownNode(ctx echo.Context, params ShutdownNodeParams) error
@@ -211,6 +214,36 @@ func (w *ServerInterfaceWrapper) GetParticipationKeyByID(ctx echo.Context) error
return err
}
+// AppendKeys converts echo context to params.
+func (w *ServerInterfaceWrapper) AppendKeys(ctx echo.Context) error {
+
+ validQueryParams := map[string]bool{
+ "pretty": true,
+ }
+
+ // Check for unknown query parameters.
+ for name, _ := range ctx.QueryParams() {
+ if _, ok := validQueryParams[name]; !ok {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unknown parameter detected: %s", name))
+ }
+ }
+
+ var err error
+ // ------------- Path parameter "participation-id" -------------
+ var participationId string
+
+ err = runtime.BindStyledParameter("simple", false, "participation-id", ctx.Param("participation-id"), &participationId)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter participation-id: %s", err))
+ }
+
+ ctx.Set("api_key.Scopes", []string{""})
+
+ // Invoke the callback with all the unmarshalled arguments
+ err = w.Handler.AppendKeys(ctx, participationId)
+ return err
+}
+
// ShutdownNode converts echo context to params.
func (w *ServerInterfaceWrapper) ShutdownNode(ctx echo.Context) error {
@@ -270,6 +303,7 @@ func RegisterHandlers(router interface {
router.POST("/v2/participation", wrapper.AddParticipationKey, m...)
router.DELETE("/v2/participation/:participation-id", wrapper.DeleteParticipationKeyByID, m...)
router.GET("/v2/participation/:participation-id", wrapper.GetParticipationKeyByID, m...)
+ router.POST("/v2/participation/:participation-id", wrapper.AppendKeys, m...)
router.POST("/v2/shutdown", wrapper.ShutdownNode, m...)
}
@@ -277,143 +311,144 @@ func RegisterHandlers(router interface {
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "GUfr6/fX/xsAAP//tpthUSegAAA=",
+ "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",
}
// GetSwagger returns the Swagger specification corresponding to the generated code
diff --git a/daemon/algod/api/server/v2/generated/private/types.go b/daemon/algod/api/server/v2/generated/private/types.go
index 9e6d5685b..301578ade 100644
--- a/daemon/algod/api/server/v2/generated/private/types.go
+++ b/daemon/algod/api/server/v2/generated/private/types.go
@@ -640,7 +640,7 @@ type PendingTransactionsResponse struct {
// PostParticipationResponse defines model for PostParticipationResponse.
type PostParticipationResponse struct {
- // encoding of the participation id.
+ // encoding of the participation ID.
PartId string `json:"partId"`
}
diff --git a/daemon/algod/api/server/v2/generated/routes.go b/daemon/algod/api/server/v2/generated/routes.go
index 4d3d7bd11..83946f45b 100644
--- a/daemon/algod/api/server/v2/generated/routes.go
+++ b/daemon/algod/api/server/v2/generated/routes.go
@@ -617,179 +617,179 @@ func RegisterHandlers(router interface {
var swaggerSpec = []string{
"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",
- "rr/7PSgAJEiCkvzIq9d/JRbxKBQKhUI9P45SUZSCA9dqtP9xVFJJC9Ag8S+apqLiOmGZ+SsDlUpWaib4",
- "aN9/I0pLxhej8YiZX0uql6PxiNMCmjam/3gk4V8Vk5CN9rWsYDxS6RIKagbW69K0rkdaJQuRuCEO7BBH",
- "h6PLDR9olklQqg/lLzxfE8bTvMqAaEm5oqn5pMgF00uil0wR15kwTgQHIuZEL1uNyZxBnqmJX+S/KpDr",
- "YJVu8uElXTYgJlLk0IfzhShmjIOHCmqg6g0hWpAM5thoSTUxMxhYfUMtiAIq0yWZC7kFVAtECC/wqhjt",
- "vx8p4BlI3K0U2Dn+dy4B/oBEU7kAPfowji1urkEmmhWRpR057EtQVa4Vwba4xgU7B05Mrwl5XSlNZkAo",
- "J+9+fEGePHny3CykoFpD5ohscFXN7OGabPfR/iijGvznPq3RfCEk5VlSt3/34wuc/9gtcNdWVCmIH5YD",
- "84UcHQ4twHeMkBDjGha4Dy3qNz0ih6L5eQZzIWHHPbGNb3VTwvm/6K6kVKfLUjCuI/tC8Cuxn6M8LOi+",
- "iYfVALTalwZT0gz6fi95/uHjo/Gjvct/e3+Q/Lf789mTyx2X/6IedwsGog3TSkrg6TpZSKB4WpaU9/Hx",
- "ztGDWooqz8iSnuPm0wJZvetLTF/LOs9pXhk6YakUB/lCKEIdGWUwp1WuiZ+YVDw3bMqM5qidMEVKKc5Z",
- "BtnYcN+LJUuXJKXKDoHtyAXLc0ODlYJsiNbiq9twmC5DlBi4roUPXNDXi4xmXVswASvkBkmaCwWJFluu",
- "J3/jUJ6R8EJp7ip1tcuKnCyB4OTmg71sEXfc0HSer4nGfc0IVYQSfzWNCZuTtajIBW5Ozs6wv1uNwVpB",
- "DNJwc1r3qDm8Q+jrISOCvJkQOVCOyPPnro8yPmeLSoIiF0vQS3fnSVCl4AqImP0TUm22/X8d//KGCEle",
- "g1J0AW9pekaApyIb3mM3aewG/6cSZsMLtShpeha/rnNWsAjIr+mKFVVBeFXMQJr98veDFkSCriQfAsiO",
- "uIXOCrrqT3oiK57i5jbTtgQ1Q0pMlTldT8jRnBR09f3e2IGjCM1zUgLPGF8QveKDQpqZezt4iRQVz3aQ",
- "YbTZsODWVCWkbM4gI/UoGyBx02yDh/GrwdNIVgE4fpBBcOpZtoDDYRWhGXN0zRdS0gUEJDMhvzrOhV+1",
- "OANeMzgyW+OnUsI5E5WqOw3AiFNvFq+50JCUEuYsQmPHDh2Ge9g2jr0WTsBJBdeUccgM50WghQbLiQZh",
- "Cibc/JjpX9EzquC7p0MXePN1x92fi+6ub9zxnXYbGyX2SEbuRfPVHdi42NTqv8PjL5xbsUVif+5tJFuc",
- "mKtkznK8Zv5p9s+joVLIBFqI8BePYgtOdSVh/5Q/NH+RhBxryjMqM/NLYX96XeWaHbOF+Sm3P70SC5Ye",
- "s8UAMmtYo68p7FbYf8x4cXasV9FHwyshzqoyXFDaepXO1uTocGiT7ZhXJcyD+ikbvipOVv6lcdUeelVv",
- "5ACQg7grqWl4BmsJBlqazvGf1Rzpic7lH+afssxjODUE7C5aVAo4ZcE795v5yRx5sG8CMwpLqUHqFK/P",
- "/Y8BQP8uYT7aH/3btNGUTO1XNXXjmhkvx6ODZpzbn6npadfXecg0nwnjdnew6di+CW8fHjNqFBIUVDsw",
- "/JCL9OxaMJRSlCA1s/s4M+P0TwoOT5ZAM5Ako5pOmkeVlbMG6B07/oz98JUEMnLF/YL/oTkxn80ppNqL",
- "b0Z0ZcoIcSJQNGVG4rP3iJ3JNEBJVJDCCnnECGdXgvJFM7ll0DVHfe/Q8qE7WmR3Xlq5kmAPvwiz9ObV",
- "eDAT8nr00iEETpq3MKFm1Fr6NStv7yw2rcrE4SciT9sGnYEa9WOfrYYY6g4fw1ULC8eafgIsKDPqbWCh",
- "PdBtY0EUJcvhFs7rkqplfxFGwHnymBz/fPDs0ePfHj/7ztzQpRQLSQsyW2tQ5L67V4jS6xwe9FeGDL7K",
- "dXz07576F1R73K0YQoDrsXc5USdgOIPFGLH6AgPdIeSg4S2VmqWsRGwdZSFG26O0GpIzWJOF0CTDQTJ7",
- "0+Ooci0rfgsbA1IKGZGkkSC1SEWenINUTESUIm9dC+JaGO5mpfnO7xZackEVMXPjI6/iGchJbD/N6w0F",
- "BQ2F2nb92KFPVrzBuBuQSknXvX21642szs27y063ke/fDIqUIBO94iSDWbUIbz4yl6IglGTYEdnsG5HB",
- "saa6UrfAW5rBGmDMRoQg0JmoNKGEi8ywCdM4znUGNKSomkGNkg4ZmV7aW20GRuZOabVYamKEVRHb2qZj",
- "QlO7KQneQGrgQVlrAmwrO53VvuUSaLYmMwBOxMy92tx7EhdJUdmjvR3H8bwGrPql0YKrlCIFpSBLnNFq",
- "K2i+nd1lvQFPCDgCXM9ClCBzKq8JrBaa5lsAxTYxcGshxT11+1DvNv2mDexOHm4jleblaqnASETmdBs2",
- "N4TCHXFyDhKffJ90//wk192+qhwwyLh7/YQV5vgSTrlQkAqeqehgOVU62XZsTaOW8GFWEJyU2EnFgQfU",
- "Dq+o0vbhz3iGgqhlNzgP9sEphgEevFHMyH/3l0l/7NTwSa4qVd8sqipLITVksTVwWG2Y6w2s6rnEPBi7",
- "vr60IJWCbSMPYSkY3yHLrsQiiGqneao1Y/3FoZLf3APrKCpbQDSI2ATIsW8VYDdUSg8AYl4tdU8kHKY6",
- "lFNrwscjpUVZmvOnk4rX/YbQdGxbH+hfm7Z94qK64euZADO79jA5yC8sZq05YkmNxIgjk4KembsJ5T+r",
- "oejDbA5johhPIdlE+eZYHptW4RHYckgHRG9n8Axm6xyODv1GiW6QCLbswtCCB94BLaH0b7C+dSVCd4Ko",
- "PoFkoCnLISPBB2TgyHsbqZllowjQ1xO0dhJC++D3pNDIcnKm8MIouyK/QvCtLeMksIDcgqQYGdWcbsoJ",
- "Auo1pOZCDpvAiqY6X5trTi9hTS5AAlHVrGBaW+NUW5DUokzCAaLP4Q0zOoWEtQP4HdhFQ3KMQwXL62/F",
- "eGTFls3wnXQElxY6nMBUCpFPtp/4HjKiEOzy8DggpTC7zpwt1BvMPCW1gHRCDGqjauZ5T7XQjCsg/1tU",
- "JKUcBbBKQ30jCIlsFq9fM4O5wOo5mZV0GgxBDgVYuRK/PHzYXfjDh27PmSJzuPAOBKZhFx0PH+Ir6a1Q",
- "unW4buHFa47bUYS3o57AXBROhuvylMlWnYEbeZedbD/zjw79pHimlHKEa5Z/YwbQOZmrXdYe0siSquX2",
- "teO4O6lJgqFj67b7LoWY38JqWbaKWc0yWMVW6ggX3yj3jEC/VqAnUdmrNABGDOcgz3JUgIh550CSAsxJ",
- "UUtWmiEbI99aQ8tB6P/c/8/99wfJf9Pkj73k+X9MP3x8evngYe/Hx5fff/9/2z89ufz+wX/+e0xeVZrN",
- "4iq4n6laGkgd41zxI26V6HMh7Stn7YQnMf/ccHdIzGymx3ywpJ2OW2xDGCfUbjbSnJGN8/Ut3LF2ICKh",
- "lKCQI4ZvSmW/innoH+QoT62VhqKvlrFdfxsQSt95ka5HpYLnjENSCA7rqEss4/AaP8Z6W6480Bnvx6G+",
- "XZG3BX8HrPY8u2zmTfGLux2wobe1t9ItbH533I5GLvSMQo0C5CWhJM0Z6hsEV1pWqT7lFF80AblGbAT+",
- "nTb8xn3hm8Qf1ZE3rxvqlFNlcFi/c6Ka2jlENBg/AvinrqoWC1C6I9vNAU65a8U4qTjTOFdh9iuxG1aC",
- "REX9xLYs6JrMaY5P8j9ACjKrdFvaQQcOpc2L2aoHzTREzE851SQHqjR5zfjJCofzfhKeZjjoCyHPaizE",
- "ef4COCimkjgj/cl+RX7qlr90vBW9ae1nz28+9wXgYY+5FzjIjw7dS+DoEMW9RjHYg/2zaYsKxpMokZ0s",
- "gRSMo5dah7bIfSO0egJ60KgY3a6fcr3ihpDOac4yqq9HDl0W1zuL9nR0qKa1EZ3Hv1/rh5gteCGSkqZn",
- "aAocLZheVrNJKoqpfwFNF6J+DU0zCoXg+C2b0pJNVQnp9PzRFnHsBvyKRNjV5XjkuI66dX2BGzi2oO6c",
- "tdrN/60FuffTyxMydTul7llfIzt04CQSebS6UJeWXcUs3vrKW2erU37KD2HOODPf9095RjWdzqhiqZpW",
- "CuQPNKc8hclCkH3ihjykmp7yHosfDGdBT2AHTVnNcpaSs/Aqbo6mdVHuj3B6+t4QyOnph56Svn9xuqmi",
- "Z9ROkFwwvRSVTpwPZiLhgsosArqqffBwZOtBvWnWMXFjW4p0Pp5u/DirpmWpklykNE+Uphriyy/L3Cw/",
- "IENFsBO6jhClhfRM0HBGCw3u7xvhzBSSXngH3kqBIr8XtHzPuP5AktNqb+8JkIOyfGXGPDZw/O54jaHJ",
- "dQkt9caOTj/NYDHVBi7cClSw0pImJV2Aii5fAy1x9/GiLlCRlucEu4U4qQ3nOFSzAI+P4Q2wcFzZrQkX",
- "d2x7+WCa+BLwE24htjHcqdFPX3e/zFA/i9wQ2bW3KxgjukuVXibmbEdXpQyJ+52pfewXhid7o4FiC24O",
- "gQtHmAFJl5CeQYae0VCUej1udfd2KXfDedbBlI0gsN5L6OaKmqAZkKrMqJMBKF93/Q0VaO2dLN/BGaxP",
- "ROMlexUHw8vxKLU+/YmhmaGDipQaXEaGWMNj68bobr6zcRpIaVmSRS5m7nTXZLFf04XvM3yQ7Q15C4c4",
- "RhQ1GjbQe0llBBGW+AdQcI2FmvFuRPqx5RnxZmZvvojexPN+4po0UpuzU4arOVnW3wvAcCRxociMKsiI",
- "cJE0Nigl4GKVogsYUOaEyrgdPT1bCjwcZNu9F73pxLx7ofXumyjItnFi1hylFDBfDKmY4921TvuZrL4X",
- "VzAhGCDrEDbLUUyqDeOW6VDZUoraiL8h0OIEDJI3AocHo42RULJZUuWDfDAWyp/lnWSAIRNebYI1BO5t",
- "sPgUbYQ6ZubN4ZwO4X/YM/0oMKwGAU+137nnud1zOq5jEGzssfdP907p3hN9NL6SV/l45Hx9YtshOApA",
- "GeSwsAu3jT2hONDuqWCDDBy/zOc540CSmI2WKiVSZqO0mmvGzQFGPn5IiNU9kZ1HiJFxADbaMXBg8kaE",
- "Z5MvrgIkB4aGD+rHRgtI8DdsV4Q3QeBO8t4qIbd5Y5+TNEdq3IRs2E3tq8vGoyiDGnrKtO0QtskMem+/",
- "GMEaRtVXIPXVVApyQLkhafHZ5CymVjTiDyBRHvtuwfuG3GdzI408CIxbEhZMaWge+Obseo3V51WynAsN",
- "yZxJpRPULUSXZxr9qFBq/dE0jTOjjvFJWWVFnBfhtGewTjKWV/HddvP+7dBM+6Z+6KlqdgZrvHKApksy",
- "wxjoqEl6w9TWa2Hjgl/ZBb+it7be3WjJNDUTSyF0Z45vhKo63GXTYYoQYIw4+rs2iNIN7AUfaYeQ65ir",
- "eyB24fPbsE8bizGo3ugdpsyPvUkYC6AY5sN2pOhaAol84yoYmgyNSMl0EELc96AdOAO0LFm26igb7KiD",
- "Iim90ovCPk0iNrNRPdgWDASKhZiTlgSvHLFbGtygNhich2ub7IQZI4uFCAkYQjgVUz6VSR9RhrQx3n4b",
- "rk6A5n+D9d9NW1zO6HI8upluIoZrN+IWXL+ttzeKZ1S627dqS9V4RZTTspTinOaJ0+AMkaYU5440sblX",
- "+HxmVhfXE5y8PHj11oFvHsk5UGl1ehtXhe3Kb2ZV5uku5MAB8akSjOzqH/lWEAs2v44/C7U+F0twYemB",
- "LGe4mCMue7wajV5wFJ0WaB63/W3V6Tjlo13iBiUklLUOsnkfWxVkW+1IzynL/cPUQztgp8PFNYrfK3OF",
- "cIAbqy8DLXRyq+ymd7rjp6Ohri08KZxrQ+B8YXNDKCJ41wHMiJD43kVSLejaUJDVoveZE6+KxBy/ROUs",
- "jSsx+EwZ4uBWOW0aE2w8IIyaESs2YOvgFQvGMs3UDma9DpDBHFFkou5rA+5mwiX1qjj7VwWEZcC1+STx",
- "VHYOqjmXPjFM/zo1skN/LjewVYE1w99ExjBDDUkXCMRmASNUhffAPawfnH6htQ7f/BDo/K5gUQtn7F2J",
- "G6xhjj4cNVu3hGVbpR3m4OrzP0MYNl/D9gRgXomxtIAOzBFN6DV4WxwM3xSm9xXuiOZKQHDDy2BsNau5",
- "EpFhKn5Buc3PY/pZHLreCqzOwPS6EBJDUhRE3QmYSuZS/AHxl+zcbFTER9WhEsVF7D2JuPp3mWito2ky",
- "r3n8hnAMkvaQJBd8JG2L58AJRyoPdPwYOe7VXZRbsra5hFp29vjhCH1jpnb85nA4mHv+RDm9mNFYWL0R",
- "qAxMB401qaWY04L4zn4XnA6xob3AMFW3ZTaOowTZOJL3YwavKRx9WySfQcoKmselpAyx345ay9iC2YRM",
- "lYIg448byGays1TksiZZe12DmqM52RsHOcXcbmTsnCk2ywFbPLItZlThrVUrX+suZnnA9VJh88c7NF9W",
- "PJOQ6aWyiFWC1AIsPuVqTfgM9AUAJ3vY7tFzch9tAIqdwwODRSeLjPYfPUclqv1jL3bZucxrm/hKhozl",
- "vxxjidMxGkHsGOaScqNOojFFNl3mMAvbcJps113OErZ0XG/7WSoopwuIm52LLTDZvribqDTs4IVnNteb",
- "0lKsCdPx+UFTw58GfOgM+7NgkFQUBdNo3tOCKFEYemrS+dhJ/XA2cZxLseHh8h/R4FLaZwN0H8yfV0Fs",
- "7/LYqtEs9oYW0EbrmFAbepezxhTqGOKEHPkAXsw5Uqcasbgxc5mlo0iHltE5KSXjGh9RlZ4nfyXpkkqa",
- "GvY3GQI3mX33NJJnpZ1agV8N8M+OdwkK5Hkc9XKA7L004fqS+1zwpDAcJXvQ+KwGpzKaykBomse9bzxH",
- "7zpfbR56VwHUjJIMklvVIjcacOobER7fMOANSbFez5Xo8cor++yUWck4edDK7NCv7145KaMQMpbOoTnu",
- "TuKQoCWDc3QEim+SGfOGeyHznXbhJtB/WStL8wKoxTJ/lmMPgR8qlmd/b3zwO6mqJOXpMmrjmJmOvzW5",
- "9eol23MczR6wpJxDHh3O3pm/+bs1cvv/U+w6T8H4jm27KajscjuLawBvg+mB8hMa9DKdmwlCrLadkmsv",
- "tnwhMoLzNKHqDZX1s2oFiXP+VYHSsTy/+ME6gKIuy7wLbN4WAjxDqXpCfrK5sZdAWpG0KM2yosptVCZk",
- "C5BOyVqVuaDZmJhxTl4evCJ2VtvH5jC1eWMWKMy1V9HRYQR5LXbzyfLJ6eL+oruPs9mBzaxaaQxsV5oW",
- "ZSwUwLQ48Q0w3iDU66KYF2JnQg6thK28/GYnMfQwZ7Iwkmk9muXxSBPmP1rTdImia4ubDJP87gmPPFWq",
- "IJ1onZmxTk2B587A7XIe2ZRHYyLM++KCKZsSGc6hHX1Qh+K4p5OPRmgvT1acW0qJ8uhNoWLXQbsHzhrv",
- "veo3ClkH8VcUXJSoZApXzf90jL2isd7dZFK9PKI27LHO4+dT3aeUC85SjLQOkjDXILv0yrvYRXYISu+q",
- "pfwRdyc0criiKaxq9yCHxcGkVp4ROsT1FbPBV7Opljrsnxrz+C6pJgvQynE2yMY++ZnTlzCuwKUawUzb",
- "AZ8UsmVrQg4ZNV8mtZr7imSEvsgDAvCP5tsb9zxCJ70zxlEQcmhz/oBWo4HZX7WRnpgmCwHKracdO6ze",
- "mz4TjJ/NYPVh4rPF4hjWVGOWbe2S/aEOvJXSWQVN2xemLUGzTPNzy+/ZTnpQlm7SaOhvvcOxRGuDCI5Y",
- "mxKv7g+QW48fjraB3Da6F+B9aggNztE4CSXewz3CqHPWdVJantO8shSFLYh164nGqzEeAeMV49DkMo5c",
- "EGn0SsCNwfM60E+lkmorAu7E006A5miRjDE0pZ2K9qZDdTYYUYJr9HMMb2OTbm+AcdQNGsGN8nWdQtlQ",
- "dyBMvMDc7Q6R/eR5KFU5ISpDN85OOr0Y4zCM26e3bF8A/WPQl4lsdy2pPTlXuYmGInNSEZM3X64grazB",
- "XdgcHrQsSYqhrsF9EdVoMmUeT8Usj/i+HdYfg8yX6HI7W+O/scwqwyhxFvEr+2R58zd2vLLA2h6pJ24a",
- "YkoUW1xzm5v+t7rPuVi0Afm8CoWNZzwkmdjpfmnY5nBu0gPPWOtYSnRDEj4tMj6a6iig9plERh59lDYZ",
- "bjc/yodz1Y6R9Q84I75r0gRQe7tYG8OQS2I66EFLtXOW15Q0Mfn9g2kTzMZGsP4MNrGtLRIT1a8M+TBY",
- "Fwbzudd7N7moJ2Xi2BsR6p1j+gD9zXvekZIyZ0BrTmwfs85Ht+81vYv3XrPB3UU4z1ccJLaSXm6uzRTS",
- "83wOfN9tCqXJ7lG6jUEebSaYAHcB3GXAbfs07uxZNZ9Dqtn5Fk/z/zISa+PFPPYyrU1GHjies9pTx9cS",
- "uqKo3QC0yRF8IzxBKoAbgzPkZ3oG63uKtPMwH0bPnyPU6wSBIQYwTUJiSESomPbfPsKdQpapmjIQC97a",
- "ZrtDk6FmMJlm7e4VS0i001yeJAl1clad7Wcof6eISfE7zWW67uB41Xhvo0vGkDN6P53d8O11iNkDVZ0I",
- "uS4WFDhTmMdaNyvUhQtCw7iAWu/kw9FA+d98CI2dxRahatJ9opbvgsrMt4iKrV4iTgbcu7oO09YvncWB",
- "ntczs8Y3ou8zHAneRl+YNBeK8UUy5DLVdkeodfn3lDW6oIIA8wQiXHOQLs2v9jW+Ei28L8UmODahwpWY",
- "uA4S1GBuLwvcYBjjuyZOEzPWUFvhzRmUwgUSCQU10MkgmnJ4zk3IfmG/eydZn7Gkkx8oMq6n12RrOKT3",
- "imGqh8SQ6ufE3ZbbnW+v815gnNss6ioWWskNKkNNUilFVqX2gg4PBvh31c6ByxtYSVTKT/ur7AlsOYbx",
- "vwpCGc5gPbVCU7qkvMmn0D7WNpm6XUMQeNfZ7Vt9SsUF1nxhF7C4FTi/5EtoPCqFyJMB1dFRP0K0ewbO",
- "WHoGGTF3h7cnDyTUJPdRY1HbBi6Wa58+vCyBQ/ZgQoh5SxWlXnszQTs3Umdyfk9vmn+Fs2aVDdp2j7TJ",
- "KY+7QtiaiTfkb36YzVzNFhG+4VR2kM0T6RUfYG30IpJedtd6OxHFfTflZ0NUFoqYlHLNWLmdznf/oRYh",
- "/TDKYcv756z1qrPZPzrKeiHhll93gZbyiq+7fvzGrsvDdSBXqxT017nzBrRwO4D7XRDfqCb6yB3WKOjZ",
- "LhqFeKYC0x1VGhYhmOaDIKjk90e/EwlzV8D14UOc4OHDsWv6++P2Z/P6evgwejI/mzKjVdbHzRujmL8P",
- "GXetAXPAj6CzHxXLs22E0fIKaVLwod/Db85/5oskAfzNPpH7R9XlQ7uKGrW7CYiYyFpbkwdTBf4eO7h6",
- "uG4Rxw68bNJKMr3GECb/omK/RUPDf6qVMK5WXO0I7vyQbZlS55bUqGyaypI/CVvtqTB3PSrWNebSfrmi",
- "RZmDOyjf35v9BZ789Wm29+TRX2Z/3Xu2l8LTZ8/39ujzp/TR8yeP4PFfnz3dg0fz757PHmePnz6ePX38",
- "9Ltnz9MnTx/Nnn73/C/3fFlHC2hTMvEfmCkzOXh7lJwYYBuc0JLVKfQNGfusezTFk2jeJPlo3//0//sT",
- "NklFEVSid7+OnI/aaKl1qfan04uLi0nYZbrAN1qiRZUup36efuryt0e1/4yNe8Adta4RhhRwUx0pHOC3",
- "dy+PT8jB26NJQzCj/dHeZG/yCJPblsBpyUb7oyf4E56eJe771BHbaP/j5Xg0XQLN9dL9UYCWLPWf1AVd",
- "LEBOXPpB89P546k3v08/uvfppRl1EQvusp5AgftHPyuf03WhUcdXKw6yqyiXdGVc5z5y4iPP0EHDPvkM",
- "a6uRdZQ1GTyOggKLLhLLhqbvv/+GKlHHygPE0htGCsw2qqLh2rJB+X1fcv/ZXy8jfoAfOvVCH+/tfYIa",
- "oePWKB4v1yw2+vQWQWwbgG4MaHe4Hld4TXNDN1DXjx/hgh59sws64qj/NmyLWLZ8OR49+4Z36Iibg0Nz",
- "gi2DSJo+K/yVn3FxwX1LcyVXRUHlGi/cIOlgKFpdDrLcdgyb09YO82EIClUEWdVa2qLZ2tPZmKi6mlEp",
- "mTCCw9i8AjJIJVC85oVEd72m5IXTDIAt3/T64B+oL3598A/yPRmqRB9Mb1/kbSb+E+hISZYf1k015Y0c",
- "/UuxyfFXW7z/27nzbnrV3BX2+WYL++zAtO92965s0zdbtunbFklXdfwxJVzwhGOWyXMggVrrTkb9qmXU",
- "Z3tPvtnVHIM8ZymQEyhKIalk+Zr8yuuAjZuJ4DXPqXgQQrOR//TMW40UHYjvQTLu6ceWJ0O2XXnScmnI",
- "xoTpRjJseTsEGXrrZMAuWG/cZPqiPLOO9t7zVY19xivU1ll7rN2PcS8f1iQmpAdmmh/WR4e7yOWtNQWJ",
- "eGKyeQtfG0X03qX1STUWYcBX5F6L782nvgF6cPxAM+Ij+j4xb96NmT7de/r5IAh34Y3Q5Ed09PjELP2T",
- "6gniZBUwG0x4P/3oc/bswGBcPqw2a3HeQxuZijmhYxek70qL1dZ9w08sI7Qpyfpcw8ywK7/op+yKcYom",
- "TdHXwiNswv8IXXbRe8cX7vjCjfhCl6AajoA+smr6ET3ZQnbQO5JY3PJPZCgJyhlIUfgMuoLMQadL6zvc",
- "tWVH2IqPGx3mKZuyK92Yv3Ss67hF/ewSuBZnr8WsPzt68WDHn6359HI8SkFGiO8XH8RiPrM5+mLVMcE+",
- "iRhm0mA+r0adUsMlHmKKGALVgrhQFWJ28UpQvmgm79vWES3X0ybdIfgmCO4xtZcuw4k9Xm4R37riI7gt",
- "SULeoDiEB9yHxP4Z1R6f8kb+1At6IzgQWDGFZU4sLd6ZG2txoS7lXbsuh1UaB0SHttHxo16x7HJax9YM",
- "CRVvXU3qjUJFc1OzJtN9W71CyxKoVNe+pLebw046Mx4dhpU4WqFAdRBQBBSDlytaEv9jFzPin9dad1eX",
- "/q4u/fXq0n/WJ3PjkGNZlbcTyQ7X+KLvaf1F3tNvBE/wtgWuveTXQsuXe1tjAEKrQJ7PIcWFrYgvJAoJ",
- "IR9Qk52uVxg0JbSYCrp0DpOxu2xTqtNlVU4/4n/QGfSycbu0CdOmVs226b49ti1u1YHCjklk45Mf+h87",
- "1V+0TLFaKw1FP5227frbplRcUR4usDZeUggec122lfNe48doKAwaZQc6o3l8qG83CWIL/g5Y7Xl2YXU3",
- "xe/k61Dh3Ugc7axWQlk7oaG1Hum/OS3duqSxn6cf22W7rDbctVTLSmfiIujbFIMcPFu2xa2erTciAztu",
- "27u/nxKUoruD84juH6maa8SjvTx+m3Y28I4pF6qY0mqx1DYddDTXfN0xoak9CjacX22Lf7atfJzfORCa",
- "S6DZmswAOBEzs+h2HoluOUvHG+NhvA1cpRQpKAVZEuaB3ARa7WeOGkK9AU8IOAJcz0KUIHMqrwmsZRKb",
- "Ae0mQK7BrfVAjg/0od5t+k0b2J083EYqgzLEWqCfTQ6ukHgEhTviBIVX9on3z09y3e2rSkw1GAlEt19P",
- "WIFBc5xyoSAVPFPD6SK2HVtMEBGsRYHNru9PSjSDmxl44Gp9RZV2mS5bUbVBmhEzxYb8FkMxYmbkv9cR",
- "Yr2xm3KodRJQK3tBFs2vDqsNc72BVT2XmEdKrbraD9tGHsJSMH6dFjRIWKEDHYUZLrK4C5bnaK2NSyIt",
- "IBpEbALk2LcKsBsqAgYAYapBdB2F3qacoC6D0qIszfnTScXrfkNoOratD/SvTds+cTnXcOTrmQAVCt4O",
- "8guLWZvxd0kVcXCQgp45mX3hPLT7MJvDmCjGU5dlZyibAyvg2LQKj8CWQ9oV+8Lj3zpnncPRod8o0Q0S",
- "wZZdGFpwTND8KsTCq777uhqFT6gIbQvagXjVCJr27+kFZTqZC+kyGGFNmYhNtZPYiTLtKhm5V7EWTpHp",
- "qtJYhuLGCfJdq9C91RUe98kXWBHxwzJT/SjkTibcRtuqBTELIxXXzAfgmfNWy5hfnz30Tnq+k57vpOc7",
- "6flOer6Tnu+k5zvp+VNLz1/GJ5MkiefTPuAmFm5DRt+khP8NRbR8zhCURuivRX58JBgR3Zzjjb4aGmg+",
- "dVUm0Kgezalunb7DihWpmY5xUuYUy1WutA89xkqVQc0qnyrdZlQyvMY0ePKYHP988OzR498eP/vOcJ+l",
- "LZsVtr3vk/0qvc7hgfNpq1OeeOc24BRzsqNvG/Wvn9T7PVhpfs5yIMog6yU2P4RzyI0ob62fxDxG+s+j",
- "E6D5C4ccy5VA6R9Etu4Qjln/FFHRJpnGhM44lZG6CX1C6SFZC6yd4gqB9F5Ql7fqRRH3HOhv2La9GigZ",
- "GCXvTfSy1VPAlbxyY+9iNTN76tFJXM2FL8qyCULkyKxhT1+Nb3035687ONjWSBXu/H2rfvAe8dGDh8d2",
- "7HOiEqxfbilulZhGC+CJYwvJTGRrX1vclXBpcVlbW2OYydrCFeAqA7ljcF89MGwWMbrSLVVPtLZZUAew",
- "Sdj6ZRinreqwkW9enzraRedu7EXZHa7PNQI3jPtCkoUUVfnAVrHma3wSFyXla68GM7IiVq3DDNbo+X27",
- "nLpOu9rjs7sXXQvfKxjG3/3dogWTtbqKa5ktuRbPidgtDLYd403Zm2158HxG0EiJroGCXP1N9LvsXB9r",
- "1V9p8yNHCuV0yuLchVv9j7gS3kpxzszDOcph+35ZDUOYbL0ZZMCy8GroJN/wd0Obn76jFyet4kW78dRV",
- "4gTPG0ulS0CBrJbSIplKzH0pBc1SqjCixNUy/MQSq14dRfQOCCZmnOr7/poLfLJVsMRxd5In277fbkJM",
- "CaNsas0vK102/qcHLoCnhY07VcCfRRXwgz98ilDM0t05nEF90R3YFL3QKx7lUlO0Eg57vAUH4q1teau2",
- "u97wbRNeY8J0JgjIS0JJmjM0UAiutKxSfcopqkA7Kcw75j2v2B0WpV74JnEtfERJ7oY65RRr0teK0ahI",
- "NYdYtU0AL7GparEApTuceA5wyl0rxpv695gRPrGeoOa6Nhx9YlsWdE3mWCNPkD9ACjIzr4gwiwkqFJVm",
- "ee7siWYaIuannGqSg2H6r5kR6MxwXudU28hdXVuPhYFKFzbHbBLXQvxkv2IYg1u+1xuhest+bor7fJFM",
- "0EmsWJKD/OjQZRg7OsSkMY0lsQf7ZzMvFYwnUSIzN76zyHdpi9w3Mp4noAeNTdLt+ik3wrQWBBk91dcj",
- "h64ZoHcW7enoUE1rIzrWAr/WD7Ho1oVIzJMR6+aNFkwvqxnmYvZRr9OFqCNgpxmFQnD8lk1pyaaqhHR6",
- "/miLfHADfkUi7Oru5v7zKPFDOjCnpd54LFHU3fuBe/kWErp+3Vlct7oo3eVMvcuZepdV8y5n6t3u3uVM",
- "vcsoepdR9H9qRtHJRgnRZeHYmuOvFXucoetnU7e1ZuBhs1Y2wL5ZkukJISdYFZOaOwDOQdKcpFRZwciV",
- "uS3YYqmJqtIUINs/5UkLklQUbuL7zX/tM/e02tt7AmTvQbeP1VsEnLffF0VV/GQrsn9PTkeno95IEgpx",
- "Di43WFgl0PbaOuz/V4/7S6/gKGphULni6xoSVc3nLGUW5bkwj4GF6Pj3cYFfQBrgbOoJwrRNw4r4RL9I",
- "553TLmbYFrr79/sVSuEcdMjlLs3Jp69/s6nC6k154MaxewzxjmV8DpbxxZnGnygj213yta9sQaEhtZVd",
- "9QaSVF1DLlaa3slITY3GsOYh3nB1tcP3HwwfVyDP/eXXlPDbn04x//lSKD0dmaupXd4v/GjuB7qwI7jL",
- "pZTsHHMnfrj8fwEAAP///pnN5DLyAAA=",
+ "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",
+ "XErJzjF34ofL/xcAAP//K/9iFjLyAAA=",
}
// GetSwagger returns the Swagger specification corresponding to the generated code
diff --git a/daemon/algod/api/server/v2/generated/types.go b/daemon/algod/api/server/v2/generated/types.go
index 41d33126d..6e1343a7c 100644
--- a/daemon/algod/api/server/v2/generated/types.go
+++ b/daemon/algod/api/server/v2/generated/types.go
@@ -640,7 +640,7 @@ type PendingTransactionsResponse struct {
// PostParticipationResponse defines model for PostParticipationResponse.
type PostParticipationResponse struct {
- // encoding of the participation id.
+ // encoding of the participation ID.
PartId string `json:"partId"`
}
diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go
index 278fe7dbf..da20f66fa 100644
--- a/daemon/algod/api/server/v2/handlers.go
+++ b/daemon/algod/api/server/v2/handlers.go
@@ -73,6 +73,7 @@ type NodeInterface interface {
ListParticipationKeys() ([]account.ParticipationRecord, error)
GetParticipationKey(account.ParticipationID) (account.ParticipationRecord, error)
RemoveParticipationKey(account.ParticipationID) error
+ AppendParticipationKeys(id account.ParticipationID, keys account.StateProofKeys) error
}
func roundToPtrOrNil(value basics.Round) *uint64 {
@@ -140,7 +141,6 @@ func (v2 *Handlers) GetParticipationKeys(ctx echo.Context) error {
// AddParticipationKey Add a participation key to the node
// (POST /v2/participation)
func (v2 *Handlers) AddParticipationKey(ctx echo.Context) error {
-
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(ctx.Request().Body)
if err != nil {
@@ -212,6 +212,33 @@ func (v2 *Handlers) GetParticipationKeyByID(ctx echo.Context, participationID st
return ctx.JSON(http.StatusOK, response)
}
+// AppendKeys Append state proof keys to a participation key
+// (POST /v2/participation/{participation-id})
+func (v2 *Handlers) AppendKeys(ctx echo.Context, participationID string) error {
+ decodedParticipationID, err := account.ParseParticipationID(participationID)
+ if err != nil {
+ return badRequest(ctx, err, err.Error(), v2.Log)
+ }
+
+ var keys account.StateProofKeys
+ dec := protocol.NewDecoder(ctx.Request().Body)
+ err = dec.Decode(&keys)
+ if err != nil {
+ err = fmt.Errorf("unable to parse keys from body: %w", err)
+ return badRequest(ctx, err, err.Error(), v2.Log)
+ }
+ if len(keys) == 0 {
+ err = errors.New("empty request, please attach keys to request body")
+ return badRequest(ctx, err, err.Error(), v2.Log)
+ }
+
+ err = v2.Node.AppendParticipationKeys(decodedParticipationID, keys)
+ if err != nil {
+ return internalError(ctx, err, err.Error(), v2.Log)
+ }
+ return nil
+}
+
// ShutdownNode shuts down the node.
// (POST /v2/shutdown)
func (v2 *Handlers) ShutdownNode(ctx echo.Context, params private.ShutdownNodeParams) error {
diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go
index d683d8616..0b416eaa5 100644
--- a/daemon/algod/api/server/v2/test/handlers_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_test.go
@@ -522,7 +522,7 @@ func tealCompileTest(t *testing.T, bytesToUse []byte, expectedCode int, enableDe
mockNode := makeMockNode(mockLedger, t.Name(), nil)
mockNode.config.EnableDeveloperAPI = enableDeveloperAPI
handler := v2.Handlers{
- Node: &mockNode,
+ Node: mockNode,
Log: logging.Base(),
Shutdown: dummyShutdownChan,
}
@@ -562,7 +562,7 @@ func tealDryrunTest(
mockNode := makeMockNode(mockLedger, t.Name(), nil)
mockNode.config.EnableDeveloperAPI = enableDeveloperAPI
handler := v2.Handlers{
- Node: &mockNode,
+ Node: mockNode,
Log: logging.Base(),
Shutdown: dummyShutdownChan,
}
@@ -672,3 +672,110 @@ func TestTealDryrun(t *testing.T) {
tealDryrunTest(t, &gdr, "msgp", 200, "REJECT", true)
tealDryrunTest(t, &gdr, "json", 404, "", false)
}
+
+func TestAppendParticipationKeys(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ mockLedger, _, _, _, releasefunc := testingenv(t, 1, 1, true)
+ defer releasefunc()
+ mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ handler := v2.Handlers{
+ Node: mockNode,
+ Log: logging.Base(),
+ Shutdown: make(chan struct{}),
+ }
+
+ id := account.ParticipationID{}
+ id[0] = 10
+
+ t.Run("Happy path", func(t *testing.T) {
+ // Create test object to append.
+ keys := make(account.StateProofKeys)
+ keys[100] = []byte{100}
+ keys[101] = []byte{101}
+ keyBytes := protocol.Encode(keys)
+
+ // Put keys in the body.
+ e := echo.New()
+ req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(keyBytes))
+ rec := httptest.NewRecorder()
+ c := e.NewContext(req, rec)
+
+ // Call handler with request.
+ err := handler.AppendKeys(c, id.String())
+
+ // Verify that request was properly received and deserialized.
+ require.NoError(t, err)
+ require.Equal(t, http.StatusOK, rec.Code)
+ require.Equal(t, id, mockNode.id)
+ require.Len(t, mockNode.keys, 2)
+ require.Equal(t, mockNode.keys[100], keys[100])
+ require.Equal(t, mockNode.keys[101], keys[101])
+ })
+
+ t.Run("Invalid body", func(t *testing.T) {
+ // Create request with bogus bytes in the body
+ e := echo.New()
+ req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{0x99, 0x88, 0x77}))
+ rec := httptest.NewRecorder()
+ c := e.NewContext(req, rec)
+
+ // Call handler with request.
+ err := handler.AppendKeys(c, id.String())
+
+ // Verify that request was properly received and deserialized.
+ require.NoError(t, err)
+ require.Equal(t, http.StatusBadRequest, rec.Code)
+ require.Contains(t, rec.Body.String(), "unable to parse keys from body: msgpack decode error")
+ })
+
+ t.Run("Empty body", func(t *testing.T) {
+ // Create test object with no keys to append.
+ keys := make(account.StateProofKeys)
+ keyBytes := protocol.Encode(keys)
+
+ // Put keys in the body.
+ e := echo.New()
+ req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(keyBytes))
+ rec := httptest.NewRecorder()
+ c := e.NewContext(req, rec)
+
+ // Call handler with request.
+ err := handler.AppendKeys(c, id.String())
+
+ // Verify that request was properly received and deserialized.
+ require.NoError(t, err)
+ require.Equal(t, http.StatusBadRequest, rec.Code)
+ require.Contains(t, rec.Body.String(), "empty request, please attach keys to request body")
+ })
+
+ t.Run("Internal error", func(t *testing.T) {
+ // Create mock node with an error.
+ expectedErr := errors.New("expected error")
+ mockNode := makeMockNode(mockLedger, t.Name(), expectedErr)
+ handler := v2.Handlers{
+ Node: mockNode,
+ Log: logging.Base(),
+ Shutdown: make(chan struct{}),
+ }
+
+ keys := make(account.StateProofKeys)
+ keys[100] = []byte{100}
+ keys[101] = []byte{101}
+ keyBytes := protocol.Encode(keys)
+
+ // Put keys in the body.
+ e := echo.New()
+ req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(keyBytes))
+ rec := httptest.NewRecorder()
+ c := e.NewContext(req, rec)
+
+ // Call handler with request.
+ err := handler.AppendKeys(c, id.String())
+
+ // Verify that request was properly received and deserialized.
+ require.NoError(t, err)
+ require.Equal(t, http.StatusInternalServerError, rec.Code)
+ require.Contains(t, rec.Body.String(), expectedErr.Error())
+ })
+}
diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go
index 60de7aaf7..bd5768fe1 100644
--- a/daemon/algod/api/server/v2/test/helpers.go
+++ b/daemon/algod/api/server/v2/test/helpers.go
@@ -85,6 +85,8 @@ type mockNode struct {
genesisID string
config config.Local
err error
+ id account.ParticipationID
+ keys account.StateProofKeys
}
func (m mockNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) {
@@ -103,8 +105,14 @@ func (m mockNode) RemoveParticipationKey(id account.ParticipationID) error {
panic("implement me")
}
-func makeMockNode(ledger *data.Ledger, genesisID string, nodeError error) mockNode {
- return mockNode{
+func (m *mockNode) AppendParticipationKeys(id account.ParticipationID, keys account.StateProofKeys) error {
+ m.id = id
+ m.keys = keys
+ return m.err
+}
+
+func makeMockNode(ledger *data.Ledger, genesisID string, nodeError error) *mockNode {
+ return &mockNode{
ledger: ledger,
genesisID: genesisID,
config: config.GetDefaultLocal(),
diff --git a/data/account/msgp_gen.go b/data/account/msgp_gen.go
index 8f6a96fd7..b253f2122 100644
--- a/data/account/msgp_gen.go
+++ b/data/account/msgp_gen.go
@@ -3,6 +3,8 @@ package account
// Code generated by github.com/algorand/msgp DO NOT EDIT.
import (
+ "sort"
+
"github.com/algorand/msgp/msgp"
)
@@ -15,6 +17,22 @@ import (
// |-----> (*) Msgsize
// |-----> (*) MsgIsZero
//
+// StateProofKey
+// |-----> MarshalMsg
+// |-----> CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> Msgsize
+// |-----> MsgIsZero
+//
+// StateProofKeys
+// |-----> MarshalMsg
+// |-----> CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> Msgsize
+// |-----> MsgIsZero
+//
// MarshalMsg implements msgp.Marshaler
func (z *ParticipationKeyIdentity) MarshalMsg(b []byte) (o []byte) {
@@ -236,3 +254,145 @@ func (z *ParticipationKeyIdentity) Msgsize() (s int) {
func (z *ParticipationKeyIdentity) MsgIsZero() bool {
return ((*z).Parent.MsgIsZero()) && ((*z).VRFSK.MsgIsZero()) && ((*z).VoteID.MsgIsZero()) && ((*z).FirstValid.MsgIsZero()) && ((*z).LastValid.MsgIsZero()) && ((*z).KeyDilution == 0)
}
+
+// MarshalMsg implements msgp.Marshaler
+func (z StateProofKey) MarshalMsg(b []byte) (o []byte) {
+ o = msgp.Require(b, z.Msgsize())
+ o = msgp.AppendBytes(o, []byte(z))
+ return
+}
+
+func (_ StateProofKey) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(StateProofKey)
+ if !ok {
+ _, ok = (z).(*StateProofKey)
+ }
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *StateProofKey) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ {
+ var zb0001 []byte
+ zb0001, bts, err = msgp.ReadBytesBytes(bts, []byte((*z)))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ (*z) = StateProofKey(zb0001)
+ }
+ o = bts
+ return
+}
+
+func (_ *StateProofKey) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*StateProofKey)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z StateProofKey) Msgsize() (s int) {
+ s = msgp.BytesPrefixSize + len([]byte(z))
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z StateProofKey) MsgIsZero() bool {
+ return len(z) == 0
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z StateProofKeys) MarshalMsg(b []byte) (o []byte) {
+ o = msgp.Require(b, z.Msgsize())
+ if z == nil {
+ o = msgp.AppendNil(o)
+ } else {
+ o = msgp.AppendMapHeader(o, uint32(len(z)))
+ }
+ za0001_keys := make([]uint64, 0, len(z))
+ for za0001 := range z {
+ za0001_keys = append(za0001_keys, za0001)
+ }
+ sort.Sort(SortUint64(za0001_keys))
+ for _, za0001 := range za0001_keys {
+ za0002 := z[za0001]
+ _ = za0002
+ o = msgp.AppendUint64(o, za0001)
+ o = msgp.AppendBytes(o, []byte(za0002))
+ }
+ return
+}
+
+func (_ StateProofKeys) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(StateProofKeys)
+ if !ok {
+ _, ok = (z).(*StateProofKeys)
+ }
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *StateProofKeys) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var zb0003 int
+ var zb0004 bool
+ zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0003 > 1000 {
+ err = msgp.ErrOverflow(uint64(zb0003), uint64(1000))
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0004 {
+ (*z) = nil
+ } else if (*z) == nil {
+ (*z) = make(StateProofKeys, zb0003)
+ }
+ for zb0003 > 0 {
+ var zb0001 uint64
+ var zb0002 StateProofKey
+ zb0003--
+ zb0001, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ {
+ var zb0005 []byte
+ zb0005, bts, err = msgp.ReadBytesBytes(bts, []byte(zb0002))
+ if err != nil {
+ err = msgp.WrapError(err, zb0001)
+ return
+ }
+ zb0002 = StateProofKey(zb0005)
+ }
+ (*z)[zb0001] = zb0002
+ }
+ o = bts
+ return
+}
+
+func (_ *StateProofKeys) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*StateProofKeys)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z StateProofKeys) Msgsize() (s int) {
+ s = msgp.MapHeaderSize
+ if z != nil {
+ for za0001, za0002 := range z {
+ _ = za0001
+ _ = za0002
+ s += 0 + msgp.Uint64Size + msgp.BytesPrefixSize + len([]byte(za0002))
+ }
+ }
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z StateProofKeys) MsgIsZero() bool {
+ return len(z) == 0
+}
diff --git a/data/account/msgp_gen_test.go b/data/account/msgp_gen_test.go
index a8927e790..9bde0e5d8 100644
--- a/data/account/msgp_gen_test.go
+++ b/data/account/msgp_gen_test.go
@@ -71,3 +71,63 @@ func BenchmarkUnmarshalParticipationKeyIdentity(b *testing.B) {
}
}
}
+
+func TestMarshalUnmarshalStateProofKeys(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ v := StateProofKeys{}
+ bts := v.MarshalMsg(nil)
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingStateProofKeys(t *testing.T) {
+ protocol.RunEncodingTest(t, &StateProofKeys{})
+}
+
+func BenchmarkMarshalMsgStateProofKeys(b *testing.B) {
+ v := StateProofKeys{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgStateProofKeys(b *testing.B) {
+ v := StateProofKeys{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalStateProofKeys(b *testing.B) {
+ v := StateProofKeys{}
+ bts := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go
index dba96d965..192abcbdb 100644
--- a/data/account/participationRegistry.go
+++ b/data/account/participationRegistry.go
@@ -82,16 +82,26 @@ type ParticipationRecord struct {
Voting *crypto.OneTimeSignatureSecrets
}
-// StateProofKey is a placeholder for the real state proof key type.
-// PKI TODO: Replace this with a real object.
-type StateProofKey []byte
+type (
+ // StateProofKey is a placeholder for the real state proof key type.
+ // PKI TODO: Replace this with a real object.
+ StateProofKey []byte
-// ParticipationRecordForRound adds in the per-round state proof key.
-type ParticipationRecordForRound struct {
- ParticipationRecord
+ // StateProofKeys are a map of StateProofKeys.
+ //msgp:allocbound StateProofKeys 1000
+ StateProofKeys map[uint64]StateProofKey
- StateProof StateProofKey
-}
+ // SortUint64 implements sorting by uint64 keys for
+ // canonical encoding of maps in msgpack format.
+ SortUint64 = basics.SortUint64
+
+ // ParticipationRecordForRound adds in the per-round state proof key.
+ ParticipationRecordForRound struct {
+ ParticipationRecord
+
+ StateProof StateProofKey
+ }
+)
// IsZero returns true if the object contains zero values.
func (r ParticipationRecordForRound) IsZero() bool {
@@ -178,7 +188,7 @@ type ParticipationRegistry interface {
// AppendKeys appends state proof keys to an existing Participation record. Keys can only be appended
// once, an error will occur when the data is flushed when inserting a duplicate key.
- AppendKeys(id ParticipationID, keys map[uint64]StateProofKey) error
+ AppendKeys(id ParticipationID, keys StateProofKeys) error
// Delete removes a record from storage.
Delete(id ParticipationID) error
@@ -363,7 +373,7 @@ type updatingParticipationRecord struct {
type partDBWriteRecord struct {
insertID ParticipationID
insert Participation
- keys map[uint64]StateProofKey
+ keys StateProofKeys
registerUpdated map[ParticipationID]updatingParticipationRecord
@@ -491,7 +501,7 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID)
return err
}
-func (db *participationDB) appendKeysInner(id ParticipationID, keys map[uint64]StateProofKey) error {
+func (db *participationDB) appendKeysInner(id ParticipationID, keys StateProofKeys) error {
err := db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error {
// Fetch primary key
var pk int
@@ -690,7 +700,7 @@ func (db *participationDB) Insert(record Participation) (id ParticipationID, err
return
}
-func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64]StateProofKey) error {
+func (db *participationDB) AppendKeys(id ParticipationID, keys StateProofKeys) error {
db.mutex.Lock()
defer db.mutex.Unlock()
@@ -698,7 +708,7 @@ func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64]StateP
return ErrParticipationIDNotFound
}
- keyCopy := make(map[uint64]StateProofKey, len(keys))
+ keyCopy := make(StateProofKeys, len(keys))
for k, v := range keys {
keyCopy[k] = v // PKI TODO: Deep copy?
}
diff --git a/node/node.go b/node/node.go
index 8d497089c..7132a2bc3 100644
--- a/node/node.go
+++ b/node/node.go
@@ -786,23 +786,23 @@ func (node *AlgorandFullNode) ListParticipationKeys() (partKeys []account.Partic
}
// GetParticipationKey retries the information of a participation id from the node
-func (node *AlgorandFullNode) GetParticipationKey(partKey account.ParticipationID) (account.ParticipationRecord, error) {
- rval := node.accountManager.Registry().Get(partKey)
+func (node *AlgorandFullNode) GetParticipationKey(partKeyID account.ParticipationID) (account.ParticipationRecord, error) {
+ rval := node.accountManager.Registry().Get(partKeyID)
if rval.IsZero() {
return account.ParticipationRecord{}, account.ErrParticipationIDNotFound
}
- return node.accountManager.Registry().Get(partKey), nil
+ return rval, nil
}
// RemoveParticipationKey given a participation id, remove the records from the node
-func (node *AlgorandFullNode) RemoveParticipationKey(partKey account.ParticipationID) error {
+func (node *AlgorandFullNode) RemoveParticipationKey(partKeyID account.ParticipationID) error {
// Need to remove the file and then remove the entry in the registry
// Let's first get the recorded information from the registry so we can lookup the file
- partRecord := node.accountManager.Registry().Get(partKey)
+ partRecord := node.accountManager.Registry().Get(partKeyID)
if partRecord.IsZero() {
return account.ErrParticipationIDNotFound
@@ -815,7 +815,7 @@ func (node *AlgorandFullNode) RemoveParticipationKey(partKey account.Participati
filename := config.PartKeyFilename(partRecord.ParticipationID.String(), uint64(partRecord.FirstValid), uint64(partRecord.LastValid))
fullyQualifiedFilename := filepath.Join(outDir, filepath.Base(filename))
- err := node.accountManager.Registry().Delete(partKey)
+ err := node.accountManager.Registry().Delete(partKeyID)
if err != nil {
return err
}
@@ -834,6 +834,19 @@ func (node *AlgorandFullNode) RemoveParticipationKey(partKey account.Participati
return nil
}
+// AppendParticipationKeys given a participation id, remove the records from the node
+func (node *AlgorandFullNode) AppendParticipationKeys(partKeyID account.ParticipationID, keys account.StateProofKeys) error {
+ err := node.accountManager.Registry().AppendKeys(partKeyID, keys)
+ if err != nil {
+ return err
+ }
+
+ // PKI TODO: pick a better timeout, this is just something short. This could also be removed if we change
+ // POST /v2/participation and DELETE /v2/participation to return "202 OK Accepted" instead of waiting and getting
+ // the error message.
+ return node.accountManager.Registry().Flush(500 * time.Millisecond)
+}
+
func createTemporaryParticipationKey(outDir string, partKeyBinary []byte) (string, error) {
var sb strings.Builder