From 6b348cbc1bbe1e1d8a282f40d02f1d06fa9dbb21 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 23 Nov 2021 09:46:06 -0500 Subject: [PATCH 01/19] WIP: append keys. --- data/account/participationRegistry.go | 46 ++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go index 213e2be505..5645c794b2 100644 --- a/data/account/participationRegistry.go +++ b/data/account/participationRegistry.go @@ -157,6 +157,11 @@ type ParticipationRegistry interface { // Insert adds a record to storage and computes the ParticipationID Insert(record Participation) (ParticipationID, error) + // PKI TODO: use a real type instead of []byte + // AppendKeys appends state proof keys to an existing Participation record. Keys can only be appended + // once, an error + AppendKeys(id ParticipationID, keys map[uint64][]byte) error + // Delete removes a record from storage. Delete(id ParticipationID) error @@ -256,8 +261,9 @@ const ( key BLOB NOT NULL, --* msgpack encoding of ParticipationAccount.BlockProof.SignatureAlgorithm PRIMARY KEY (pk, round) )` - insertKeysetQuery = `INSERT INTO Keysets (participationID, account, firstValidRound, lastValidRound, keyDilution, vrf) VALUES (?, ?, ?, ?, ?, ?)` - insertRollingQuery = `INSERT INTO Rolling (pk, voting) VALUES (?, ?)` + insertKeysetQuery = `INSERT INTO Keysets (participationID, account, firstValidRound, lastValidRound, keyDilution, vrf, stateProof) VALUES (?, ?, ?, ?, ?, ?, ?)` + insertRollingQuery = `INSERT INTO Rolling (pk, voting) VALUES (?, ?)` + insertStateProofKeysQuery = `INSERT INTO StateProofKeys (pk, round, key) VALUES (?, ?, ?)` // SELECT pk FROM Keysets WHERE participationID = ? selectPK = `SELECT pk FROM Keysets WHERE participationID = ? LIMIT 1` @@ -332,6 +338,7 @@ type updatingParticipationRecord struct { type partDBWriteRecord struct { insertID ParticipationID insert Participation + keys map[uint64][]byte registerUpdated map[ParticipationID]updatingParticipationRecord @@ -380,7 +387,12 @@ func (db *participationDB) writeThread() { if len(wr.registerUpdated) != 0 { err = db.registerInner(wr.registerUpdated) } else if !wr.insertID.IsZero() { - err = db.insertInner(wr.insert, wr.insertID) + if wr.insert != (Participation{}) { + err = db.insertInner(wr.insert, wr.insertID) + } + if len(wr.keys) != 0 { + err = db.inertKeysInner(wr.insertID, wr.keys) + } } else if !wr.delete.IsZero() { err = db.deleteInner(wr.delete) } else if wr.flushResultChannel != nil { @@ -413,9 +425,9 @@ func verifyExecWithOneRowEffected(err error, result sql.Result, operationName st } func (db *participationDB) insertInner(record Participation, id ParticipationID) (err error) { - var rawVRF []byte var rawVoting []byte + var rawStateProof []byte if record.VRF != nil { rawVRF = protocol.Encode(record.VRF) @@ -424,6 +436,7 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) voting := record.Voting.Snapshot() rawVoting = protocol.Encode(&voting) } + // PKI TODO: Extract state proof from record. err = db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { result, err := tx.Exec( @@ -433,7 +446,8 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) record.FirstValid, record.LastValid, record.KeyDilution, - rawVRF) + rawVRF, + rawStateProof) if err := verifyExecWithOneRowEffected(err, result, "insert keyset"); err != nil { return err } @@ -453,6 +467,13 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) return err } +func (db *participationDB) inertKeysInner(id ParticipationID, keys map[uint64][]byte) (err error) { + // PKI TODO: Insert the keys + err = db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + + }) +} + func (db *participationDB) registerInner(updated map[ParticipationID]updatingParticipationRecord) error { var cacheDeletes []ParticipationID err := db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { @@ -619,6 +640,21 @@ func (db *participationDB) Insert(record Participation) (id ParticipationID, err return } +func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64][]byte) error { + db.mutex.Lock() + defer db.mutex.Unlock() + + if _, ok := db.cache[id]; ok { + return ErrParticipationIDNotFound + } + + db.writeQueue <- partDBWriteRecord{ + + } + + return nil +} + func (db *participationDB) Delete(id ParticipationID) error { db.mutex.Lock() defer db.mutex.Unlock() From 02fbb3dcab890c8fb4927f113b9125b53ede87f2 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 30 Nov 2021 16:52:45 -0500 Subject: [PATCH 02/19] Implement functions. --- data/account/participationRegistry.go | 119 +++++++++++++++--- .../participationRegistryBench_test.go | 64 ++++++++++ data/account/participationRegistry_test.go | 102 ++++++++------- go.mod | 2 +- go.sum | 2 + 5 files changed, 226 insertions(+), 63 deletions(-) create mode 100644 data/account/participationRegistryBench_test.go diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go index 5645c794b2..0ecb93457c 100644 --- a/data/account/participationRegistry.go +++ b/data/account/participationRegistry.go @@ -82,6 +82,22 @@ 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 + +// ParticipationRecordForRound adds in the per-round state proof key. +type ParticipationRecordForRound struct { + ParticipationRecord + + StateProof StateProofKey +} + +// IsZero returns true if the object contains zero values. +func (r ParticipationRecordForRound) IsZero() bool { + return r.StateProof == nil && r.ParticipationRecord.IsZero() +} + var zeroParticipationRecord = ParticipationRecord{} // IsZero returns true if the object contains zero values. @@ -152,15 +168,17 @@ var ErrMultipleKeysForID = errors.New("multiple valid keys found for the same pa // ErrNoKeyForID there may be cases where a key is deleted and used at the same time, so this error should be handled. var ErrNoKeyForID = errors.New("no valid key found for the participationID") +// ErrSecretNotFound is used when attempting to lookup secrets for a particular round. +var ErrSecretNotFound = errors.New("the participation ID did not have secrets for the requested round") + // ParticipationRegistry contain all functions for interacting with the Participation Registry. type ParticipationRegistry interface { // Insert adds a record to storage and computes the ParticipationID Insert(record Participation) (ParticipationID, error) - // PKI TODO: use a real type instead of []byte // AppendKeys appends state proof keys to an existing Participation record. Keys can only be appended // once, an error - AppendKeys(id ParticipationID, keys map[uint64][]byte) error + AppendKeys(id ParticipationID, keys map[uint64]StateProofKey) error // Delete removes a record from storage. Delete(id ParticipationID) error @@ -174,6 +192,9 @@ type ParticipationRegistry interface { // GetAll of the participation records. GetAll() []ParticipationRecord + // GetWithSecrets fetches a record with all secrets for a particular round. + GetWithSecrets(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) + // Register updates the EffectiveFirst and EffectiveLast fields. If there are multiple records for the account // then it is possible for multiple records to be updated. Register(id ParticipationID, on basics.Round) error @@ -263,7 +284,7 @@ const ( )` insertKeysetQuery = `INSERT INTO Keysets (participationID, account, firstValidRound, lastValidRound, keyDilution, vrf, stateProof) VALUES (?, ?, ?, ?, ?, ?, ?)` insertRollingQuery = `INSERT INTO Rolling (pk, voting) VALUES (?, ?)` - insertStateProofKeysQuery = `INSERT INTO StateProofKeys (pk, round, key) VALUES (?, ?, ?)` + appendStateProofKeysQuery = `INSERT INTO StateProofKeys (pk, round, key) VALUES(?, ?, ?)` // SELECT pk FROM Keysets WHERE participationID = ? selectPK = `SELECT pk FROM Keysets WHERE participationID = ? LIMIT 1` @@ -276,6 +297,10 @@ const ( FROM Keysets k INNER JOIN Rolling r ON k.pk = r.pk` + selectStateProofKeys = `SELECT s.key + FROM StateProofKeys s + WHERE round=? + AND pk IN (SELECT pk FROM Keysets WHERE participationID=?)` deleteKeysets = `DELETE FROM Keysets WHERE pk=?` deleteRolling = `DELETE FROM Rolling WHERE pk=?` updateRollingFieldsSQL = `UPDATE Rolling @@ -338,7 +363,7 @@ type updatingParticipationRecord struct { type partDBWriteRecord struct { insertID ParticipationID insert Participation - keys map[uint64][]byte + keys map[uint64]StateProofKey registerUpdated map[ParticipationID]updatingParticipationRecord @@ -391,7 +416,7 @@ func (db *participationDB) writeThread() { err = db.insertInner(wr.insert, wr.insertID) } if len(wr.keys) != 0 { - err = db.inertKeysInner(wr.insertID, wr.keys) + err = db.appendKeysInner(wr.insertID, wr.keys) } } else if !wr.delete.IsZero() { err = db.deleteInner(wr.delete) @@ -448,7 +473,7 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) record.KeyDilution, rawVRF, rawStateProof) - if err := verifyExecWithOneRowEffected(err, result, "insert keyset"); err != nil { + if err = verifyExecWithOneRowEffected(err, result, "insert keyset"); err != nil { return err } pk, err := result.LastInsertId() @@ -458,7 +483,7 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) // Create Rolling entry result, err = tx.Exec(insertRollingQuery, pk, rawVoting) - if err := verifyExecWithOneRowEffected(err, result, "insert rolling"); err != nil { + if err = verifyExecWithOneRowEffected(err, result, "insert rolling"); err != nil { return err } @@ -467,11 +492,35 @@ func (db *participationDB) insertInner(record Participation, id ParticipationID) return err } -func (db *participationDB) inertKeysInner(id ParticipationID, keys map[uint64][]byte) (err error) { - // PKI TODO: Insert the keys - err = db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { +func (db *participationDB) appendKeysInner(id ParticipationID, keys map[uint64]StateProofKey) error { + err := db.store.Wdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + // Fetch primary key + var pk int + row := tx.QueryRow(selectPK, id[:]) + err := row.Scan(&pk) + if err == sql.ErrNoRows { + // nothing to do. + return nil + } + if err != nil { + return fmt.Errorf("unable to scan pk: %w", err) + } + stmt, err := tx.Prepare(appendStateProofKeysQuery) + if err != nil { + return fmt.Errorf("unable to prepare state proof insert: %w", err) + } + + for k, v := range keys { + result, err := stmt.Exec(pk, k, v) + if err = verifyExecWithOneRowEffected(err, result, "append keys"); err != nil { + return err + } + } + + return nil }) + return err } func (db *participationDB) registerInner(updated map[ParticipationID]updatingParticipationRecord) error { @@ -523,12 +572,12 @@ func (db *participationDB) deleteInner(id ParticipationID) error { // Delete rows result, err := tx.Exec(deleteKeysets, pk) - if err := verifyExecWithOneRowEffected(err, result, "delete keyset"); err != nil { + if err = verifyExecWithOneRowEffected(err, result, "delete keyset"); err != nil { return err } result, err = tx.Exec(deleteRolling, pk) - if err := verifyExecWithOneRowEffected(err, result, "delete rolling"); err != nil { + if err = verifyExecWithOneRowEffected(err, result, "delete rolling"); err != nil { return err } @@ -599,6 +648,8 @@ func (db *participationDB) Insert(record Participation) (id ParticipationID, err id = record.ID() if _, ok := db.cache[id]; ok { + // PKI TODO: Add a special case to set the StateProof public key if it is in the input + // but not in the cache. return id, ErrAlreadyInserted } @@ -640,18 +691,27 @@ func (db *participationDB) Insert(record Participation) (id ParticipationID, err return } -func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64][]byte) error { +func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64]StateProofKey) error { db.mutex.Lock() defer db.mutex.Unlock() - if _, ok := db.cache[id]; ok { + if _, ok := db.cache[id]; !ok { return ErrParticipationIDNotFound } - db.writeQueue <- partDBWriteRecord{ + keyCopy := make(map[uint64]StateProofKey) + for k, v := range keys { + keyCopy[k] = v // PKI TODO: Deep copy? + } + // Write to the DB asynchronously. + db.writeQueue <- partDBWriteRecord{ + insertID: id, + keys: keyCopy, } + // Keys not stored in cache, no more work to do. + return nil } @@ -665,6 +725,7 @@ func (db *participationDB) Delete(id ParticipationID) error { } delete(db.dirty, id) delete(db.cache, id) + // do the db part async db.writeQueue <- partDBWriteRecord{ delete: id, @@ -806,6 +867,34 @@ func (db *participationDB) GetAll() []ParticipationRecord { return results } +// GetWithSecrets fetches a record with all secrets for a particular round. +func (db *participationDB) GetWithSecrets(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) { + var result ParticipationRecordForRound + result.ParticipationRecord = db.Get(id) + if result.ParticipationRecord.IsZero() { + return ParticipationRecordForRound{}, ErrParticipationIDNotFound + } + + err := db.store.Rdb.Atomic(func(ctx context.Context, tx *sql.Tx) error { + row := tx.QueryRow(selectStateProofKeys, round, id[:]) + err := row.Scan(&result.StateProof) + if err == sql.ErrNoRows { + return ErrSecretNotFound + } + if err != nil { + return fmt.Errorf("error while querying secrets: %w", err) + } + + return nil + }) + + if err != nil { + return ParticipationRecordForRound{}, fmt.Errorf("unable to lookup secrets: %w", err) + } + + return result, nil +} + // updateRollingFields sets all of the rolling fields according to the record object. func updateRollingFields(ctx context.Context, tx *sql.Tx, record ParticipationRecord) error { result, err := tx.ExecContext(ctx, updateRollingFieldsSQL, diff --git a/data/account/participationRegistryBench_test.go b/data/account/participationRegistryBench_test.go new file mode 100644 index 0000000000..4490baf7eb --- /dev/null +++ b/data/account/participationRegistryBench_test.go @@ -0,0 +1,64 @@ +package account + +import ( + "fmt" + "testing" + + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/util/db" +) + +func benchmarkKeyRegistration(numKeys int, b *testing.B) { + // setup + rootDB, err := db.OpenPair(b.Name(), true) + if err != nil { + b.Fail() + } + registry, err := makeParticipationRegistry(rootDB, logging.TestingLog(b)) + if err != nil { + b.Fail() + } + + // Insert records so that we can t + b.Run(fmt.Sprintf("KeyInsert_%d", numKeys), func(b *testing.B) { + for n := 0; n < b.N; n++ { + for key := 0; key < numKeys; key++ { + p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) + registry.Insert(p) + } + } + }) + + // The first call to Register updates the DB. + b.Run(fmt.Sprintf("KeyRegistered_%d", numKeys), func(b *testing.B) { + for n := 0; n < b.N; n++ { + for key := 0; key < numKeys; key++ { + p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) + + // Unfortunately we need to repeatedly clear out the registration fields to ensure the + // db update runs each time this is called. + record := registry.cache[p.ID()] + record.EffectiveFirst = 0 + record.EffectiveLast = 0 + registry.cache[p.ID()] = record + registry.Register(p.ID(), 50) + } + } + }) + + // The keys should now be updated, so Register is a no-op. + b.Run(fmt.Sprintf("NoOp_%d", numKeys), func(b *testing.B) { + for n := 0; n < b.N; n++ { + for key := 0; key < numKeys; key++ { + p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) + registry.Register(p.ID(), 50) + } + } + }) +} + +func BenchmarkKeyRegistration1(b *testing.B) { benchmarkKeyRegistration(1, b) } +func BenchmarkKeyRegistration5(b *testing.B) { benchmarkKeyRegistration(5, b) } +func BenchmarkKeyRegistration10(b *testing.B) { benchmarkKeyRegistration(10, b) } +func BenchmarkKeyRegistration50(b *testing.B) { benchmarkKeyRegistration(50, b) } diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go index d000f16cbb..4a49104865 100644 --- a/data/account/participationRegistry_test.go +++ b/data/account/participationRegistry_test.go @@ -433,6 +433,7 @@ func TestParticipation_RecordMultipleUpdates_DB(t *testing.T) { record.FirstValid, record.LastValid, record.KeyDilution, + nil, nil) if err != nil { return fmt.Errorf("unable to insert keyset: %w", err) @@ -714,56 +715,63 @@ func TestFlushDeadlock(t *testing.T) { wg.Wait() } -func benchmarkKeyRegistration(numKeys int, b *testing.B) { - // setup - rootDB, err := db.OpenPair(b.Name(), true) - if err != nil { - b.Fail() - } - registry, err := makeParticipationRegistry(rootDB, logging.TestingLog(b)) - if err != nil { - b.Fail() +func TestAddStateProofKeys(t *testing.T) { + partitiontest.PartitionTest(t) + a := assert.New(t) + registry := getRegistry(t) + defer registryCloseTest(t, registry) + + // Install a key to add StateProof keys. + max := uint64(1000) + p := makeTestParticipation(1, 0, basics.Round(max), 3) + id, err := registry.Insert(p) + a.NoError(err) + a.Equal(p.ID(), id) + + // Wait for async DB operations to finish. + err = registry.Flush(10 * time.Second) + a.NoError(err) + + // Initialize keys array. + keys := make(map[uint64]StateProofKey) + for i := uint64(0); i <= max; i++ { + bs := make([]byte, 8) + binary.LittleEndian.PutUint64(bs, i) + keys[i] = bs } - // Insert records so that we can t - b.Run(fmt.Sprintf("KeyInsert_%d", numKeys), func(b *testing.B) { - for n := 0; n < b.N; n++ { - for key := 0; key < numKeys; key++ { - p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) - registry.Insert(p) - } - } - }) + err = registry.AppendKeys(id, keys) + a.NoError(err) - // The first call to Register updates the DB. - b.Run(fmt.Sprintf("KeyRegistered_%d", numKeys), func(b *testing.B) { - for n := 0; n < b.N; n++ { - for key := 0; key < numKeys; key++ { - p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) - - // Unfortunately we need to repeatedly clear out the registration fields to ensure the - // db update runs each time this is called. - record := registry.cache[p.ID()] - record.EffectiveFirst = 0 - record.EffectiveLast = 0 - registry.cache[p.ID()] = record - registry.Register(p.ID(), 50) - } - } - }) + // Wait for async DB operations to finish. + err = registry.Flush(10 * time.Second) + a.NoError(err) - // The keys should now be updated, so Register is a no-op. - b.Run(fmt.Sprintf("NoOp_%d", numKeys), func(b *testing.B) { - for n := 0; n < b.N; n++ { - for key := 0; key < numKeys; key++ { - p := makeTestParticipation(key, basics.Round(0), basics.Round(1000000), 3) - registry.Register(p.ID(), 50) - } - } - }) + // Make sure we're able to fetch the same data that was put in. + for i := uint64(0); i <= max; i++ { + r, err := registry.GetWithSecrets(id, basics.Round(i)) + a.NoError(err) + a.Equal(keys[i], r.StateProof) + number := binary.LittleEndian.Uint64(r.StateProof) + a.Equal(i, number) + } } -func BenchmarkKeyRegistration1(b *testing.B) { benchmarkKeyRegistration(1, b) } -func BenchmarkKeyRegistration5(b *testing.B) { benchmarkKeyRegistration(5, b) } -func BenchmarkKeyRegistration10(b *testing.B) { benchmarkKeyRegistration(10, b) } -func BenchmarkKeyRegistration50(b *testing.B) { benchmarkKeyRegistration(50, b) } +func TestSecretNotFound(t *testing.T) { + partitiontest.PartitionTest(t) + a := assert.New(t) + registry := getRegistry(t) + defer registryCloseTest(t, registry) + + // Install a key for testing + p := makeTestParticipation(1, 0, 2, 3) + id, err := registry.Insert(p) + a.NoError(err) + a.Equal(p.ID(), id) + + r, err := registry.GetWithSecrets(id, basics.Round(100)) + + a.True(r.IsZero()) + a.Error(err) + a.ErrorIs(err, ErrSecretNotFound) +} \ No newline at end of file diff --git a/go.mod b/go.mod index 00a9719d63..8c97054261 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/net v0.0.0-20200904194848-62affa334b73 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect diff --git a/go.sum b/go.sum index 0ac28a9455..c037979898 100644 --- a/go.sum +++ b/go.sum @@ -142,6 +142,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= From 129e3a0d9b22bf8a20947c53a1ae2e900c3b8880 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 30 Nov 2021 16:54:55 -0500 Subject: [PATCH 03/19] Add missing license header. --- data/account/participationRegistryBench_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/data/account/participationRegistryBench_test.go b/data/account/participationRegistryBench_test.go index 4490baf7eb..d1a97d400b 100644 --- a/data/account/participationRegistryBench_test.go +++ b/data/account/participationRegistryBench_test.go @@ -1,3 +1,19 @@ +// Copyright (C) 2019-2021 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 account import ( From 26d2afa7cc952a4de3f93d8ac816e01d9dd56974 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 30 Nov 2021 21:50:39 -0500 Subject: [PATCH 04/19] PR Feedback + another test. --- data/account/participationRegistry.go | 7 ++--- data/account/participationRegistry_test.go | 32 +++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go index 0ecb93457c..8703cea21c 100644 --- a/data/account/participationRegistry.go +++ b/data/account/participationRegistry.go @@ -177,7 +177,7 @@ type ParticipationRegistry interface { Insert(record Participation) (ParticipationID, error) // AppendKeys appends state proof keys to an existing Participation record. Keys can only be appended - // once, an error + // once, an error will occur when the data is flushed when inserting a duplicate key. AppendKeys(id ParticipationID, keys map[uint64]StateProofKey) error // Delete removes a record from storage. @@ -414,8 +414,7 @@ func (db *participationDB) writeThread() { } else if !wr.insertID.IsZero() { if wr.insert != (Participation{}) { err = db.insertInner(wr.insert, wr.insertID) - } - if len(wr.keys) != 0 { + } else if len(wr.keys) != 0 { err = db.appendKeysInner(wr.insertID, wr.keys) } } else if !wr.delete.IsZero() { @@ -699,7 +698,7 @@ func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64]StateP return ErrParticipationIDNotFound } - keyCopy := make(map[uint64]StateProofKey) + keyCopy := make(map[uint64]StateProofKey, len(keys)) for k, v := range keys { keyCopy[k] = v // PKI TODO: Deep copy? } diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go index 4a49104865..77b76f2eb7 100644 --- a/data/account/participationRegistry_test.go +++ b/data/account/participationRegistry_test.go @@ -774,4 +774,34 @@ func TestSecretNotFound(t *testing.T) { a.True(r.IsZero()) a.Error(err) a.ErrorIs(err, ErrSecretNotFound) -} \ No newline at end of file +} + +func TestAddingSecretTwice(t *testing.T) { + partitiontest.PartitionTest(t) + a := assert.New(t) + registry := getRegistry(t) + defer registryCloseTest(t, registry) + + // Install a key for testing + p := makeTestParticipation(1, 0, 2, 3) + id, err := registry.Insert(p) + a.NoError(err) + a.Equal(p.ID(), id) + + // Append key + keys := make(map[uint64]StateProofKey) + bs := make([]byte, 8) + binary.LittleEndian.PutUint64(bs, 10) + keys[0] = bs + + err = registry.AppendKeys(id, keys) + a.NoError(err) + + // The error doesn't happen until the data persists. + err = registry.AppendKeys(id, keys) + a.NoError(err) + + err = registry.Flush(10 * time.Second) + a.Error(err) + a.EqualError(err, "unable to execute append keys: UNIQUE constraint failed: StateProofKeys.pk, StateProofKeys.round") +} From 3632133663e4347c3939f68ae26ea32f4d3c6888 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 1 Dec 2021 09:53:06 -0500 Subject: [PATCH 05/19] Minor cleanup. --- data/account/participationRegistry.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go index 8703cea21c..d63e3ea497 100644 --- a/data/account/participationRegistry.go +++ b/data/account/participationRegistry.go @@ -297,7 +297,7 @@ const ( FROM Keysets k INNER JOIN Rolling r ON k.pk = r.pk` - selectStateProofKeys = `SELECT s.key + selectStateProofKeys = `SELECT s.key FROM StateProofKeys s WHERE round=? AND pk IN (SELECT pk FROM Keysets WHERE participationID=?)` @@ -703,14 +703,11 @@ func (db *participationDB) AppendKeys(id ParticipationID, keys map[uint64]StateP keyCopy[k] = v // PKI TODO: Deep copy? } - // Write to the DB asynchronously. + // Update the DB asynchronously. db.writeQueue <- partDBWriteRecord{ insertID: id, keys: keyCopy, } - - // Keys not stored in cache, no more work to do. - return nil } From 8ad0dd54342baa8928b45dfd6eade45cb3c593db Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 3 Dec 2021 15:46:49 -0500 Subject: [PATCH 06/19] Rename GetWithRound to GetForRound. --- data/account/participationRegistry.go | 8 ++++---- data/account/participationRegistry_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go index d63e3ea497..a4aa0629af 100644 --- a/data/account/participationRegistry.go +++ b/data/account/participationRegistry.go @@ -192,8 +192,8 @@ type ParticipationRegistry interface { // GetAll of the participation records. GetAll() []ParticipationRecord - // GetWithSecrets fetches a record with all secrets for a particular round. - GetWithSecrets(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) + // GetForRound fetches a record with all secrets for a particular round. + GetForRound(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) // Register updates the EffectiveFirst and EffectiveLast fields. If there are multiple records for the account // then it is possible for multiple records to be updated. @@ -863,8 +863,8 @@ func (db *participationDB) GetAll() []ParticipationRecord { return results } -// GetWithSecrets fetches a record with all secrets for a particular round. -func (db *participationDB) GetWithSecrets(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) { +// GetForRound fetches a record with all secrets for a particular round. +func (db *participationDB) GetForRound(id ParticipationID, round basics.Round) (ParticipationRecordForRound, error) { var result ParticipationRecordForRound result.ParticipationRecord = db.Get(id) if result.ParticipationRecord.IsZero() { diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go index 77b76f2eb7..e960317e73 100644 --- a/data/account/participationRegistry_test.go +++ b/data/account/participationRegistry_test.go @@ -749,7 +749,7 @@ func TestAddStateProofKeys(t *testing.T) { // Make sure we're able to fetch the same data that was put in. for i := uint64(0); i <= max; i++ { - r, err := registry.GetWithSecrets(id, basics.Round(i)) + r, err := registry.GetForRound(id, basics.Round(i)) a.NoError(err) a.Equal(keys[i], r.StateProof) number := binary.LittleEndian.Uint64(r.StateProof) @@ -769,7 +769,7 @@ func TestSecretNotFound(t *testing.T) { a.NoError(err) a.Equal(p.ID(), id) - r, err := registry.GetWithSecrets(id, basics.Round(100)) + r, err := registry.GetForRound(id, basics.Round(100)) a.True(r.IsZero()) a.Error(err) From 7ae1cfc5177ec9930babc0aed18d098b35d670c3 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 1 Dec 2021 13:54:47 -0500 Subject: [PATCH 07/19] AppendKeys endpoint placeholder. --- daemon/algod/api/algod.oas2.json | 63 +++- daemon/algod/api/algod.oas3.yml | 98 +++++- .../api/server/v2/generated/private/routes.go | 297 ++++++++++-------- daemon/algod/api/server/v2/handlers.go | 6 + 4 files changed, 329 insertions(+), 135 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 0eb127d8cb..1af06cb61a 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": { @@ -601,6 +607,12 @@ "$ref": "#/definitions/ErrorResponse" } }, + "404": { + "description": "Participation Key Not Found", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, "500": { "description": "Internal Error", "schema": { @@ -643,7 +655,54 @@ } }, "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.", + "produces": [ + "application/json" + ], + "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" } diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index b302823104..0bc7b8c57b 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -2577,7 +2577,7 @@ } } }, - "description": "Application Not Found" + "description": "Participation Key Not Found" }, "500": { "content": { @@ -2653,6 +2653,16 @@ }, "description": "Invalid API Token" }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Participation Key Not Found" + }, "500": { "content": { "application/json": { @@ -2724,6 +2734,16 @@ }, "description": "Invalid API Token" }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Participation Key Not Found" + }, "500": { "content": { "application/json": { @@ -2796,7 +2816,7 @@ } } }, - "description": "Application Not Found" + "description": "Participation Key Not Found" }, "500": { "content": { @@ -2817,6 +2837,80 @@ "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" + } + } + ], + "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" + ] } }, "/v2/shutdown": { diff --git a/daemon/algod/api/server/v2/generated/private/routes.go b/daemon/algod/api/server/v2/generated/private/routes.go index e294ea1e75..9eca7b5299 100644 --- a/daemon/algod/api/server/v2/generated/private/routes.go +++ b/daemon/algod/api/server/v2/generated/private/routes.go @@ -35,6 +35,9 @@ type ServerInterface interface { // Get participation key info by 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,142 +311,143 @@ func RegisterHandlers(router interface { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "MeHH9zWNP9R7j6P19fvr/x8AAP//Xlr8usSkAAA=", } // GetSwagger returns the Swagger specification corresponding to the generated code diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index da47ce3508..32b1363cdf 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -212,6 +212,12 @@ 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 { + return nil +} + // ShutdownNode shuts down the node. // (POST /v2/shutdown) func (v2 *Handlers) ShutdownNode(ctx echo.Context, params private.ShutdownNodeParams) error { From f1a15d5db143e343b81e0dce9b86ec0eb6fdd7ae Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 2 Dec 2021 13:40:46 -0500 Subject: [PATCH 08/19] Update spec with consumes and correct body description. --- daemon/algod/api/algod.oas2.json | 15 + daemon/algod/api/algod.oas3.yml | 15 +- .../api/server/v2/generated/private/routes.go | 274 +++++++++--------- 3 files changed, 166 insertions(+), 138 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 1af06cb61a..cf81d16b43 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -676,9 +676,24 @@ "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" ], diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index 0bc7b8c57b..e69358f607 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -2851,6 +2851,18 @@ } } ], + "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": { @@ -2910,7 +2922,8 @@ "summary": "Append state proof keys to a participation key", "tags": [ "private" - ] + ], + "x-codegen-request-body-name": "keymap" } }, "/v2/shutdown": { diff --git a/daemon/algod/api/server/v2/generated/private/routes.go b/daemon/algod/api/server/v2/generated/private/routes.go index 9eca7b5299..45c852d5f1 100644 --- a/daemon/algod/api/server/v2/generated/private/routes.go +++ b/daemon/algod/api/server/v2/generated/private/routes.go @@ -311,143 +311,143 @@ func RegisterHandlers(router interface { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "MeHH9zWNP9R7j6P19fvr/x8AAP//Xlr8usSkAAA=", + "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", } // GetSwagger returns the Swagger specification corresponding to the generated code From ded5e57ea6303c0d0a694df7d91d86529ebeb846 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 3 Dec 2021 14:04:04 -0500 Subject: [PATCH 09/19] WIP: writing some code and working on serialization. --- daemon/algod/api/server/v2/handlers.go | 29 ++++- data/account/msgp_gen.go | 160 +++++++++++++++++++++++++ data/account/msgp_gen_test.go | 60 ++++++++++ data/account/participationRegistry.go | 36 ++++-- node/node.go | 25 +++- 5 files changed, 289 insertions(+), 21 deletions(-) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 32b1363cdf..6f3724b48a 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 { @@ -214,7 +214,32 @@ func (v2 *Handlers) GetParticipationKeyByID(ctx echo.Context, participationID st // AppendKeys Append state proof keys to a participation key // (POST /v2/participation/{participation-id}) -func (v2 *Handlers) AppendKeys(ctx echo.Context, participationId string) error { +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) + // What is this error? Hit EOF before an object could finish decoding? + if err == io.EOF { + return badRequest(ctx, err, err.Error(), v2.Log) + } + if err != nil { + return badRequest(ctx, err, err.Error(), v2.Log) + } + + if len(keys) == 0 { + err := errors.New("empty request, please include keys") + 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 } diff --git a/data/account/msgp_gen.go b/data/account/msgp_gen.go index 8f6a96fd70..b253f21220 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 a8927e7902..9bde0e5d83 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 a4aa0629af..a5e3dc6451 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 e5a2f7e6ac..88c825cfcd 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 From e000ac8c74fcb48b22c048a4206d2391a1586ba1 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 3 Dec 2021 14:26:45 -0500 Subject: [PATCH 10/19] Add a unit test. --- .../algod/api/server/v2/test/handlers_test.go | 44 ++++++++++++++++++- daemon/algod/api/server/v2/test/helpers.go | 12 ++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index e80ca258d2..c083cdd1d8 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,43 @@ func TestTealDryrun(t *testing.T) { tealDryrunTest(t, &gdr, "msgp", 200, "REJECT", true) tealDryrunTest(t, &gdr, "json", 404, "", false) } + +func TestMockNode_AppendParticipationKeys(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) + handler := v2.Handlers{ + Node: mockNode, + Log: logging.Base(), + Shutdown: dummyShutdownChan, + } + + // Create test object to append. + id := account.ParticipationID{} + id[0] = 10 + 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, 200, 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]) +} \ No newline at end of file diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go index 31e6053a60..2b0be26fdc 100644 --- a/daemon/algod/api/server/v2/test/helpers.go +++ b/daemon/algod/api/server/v2/test/helpers.go @@ -84,6 +84,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) { @@ -102,8 +104,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 nil +} + +func makeMockNode(ledger *data.Ledger, genesisID string, nodeError error) *mockNode { + return &mockNode{ ledger: ledger, genesisID: genesisID, config: config.GetDefaultLocal(), From aa81a0052d5d5a3037f24a3206e40e5e42c38a71 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 10:37:55 -0500 Subject: [PATCH 11/19] make fmt --- daemon/algod/api/server/v2/test/handlers_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index c083cdd1d8..2b8c77459d 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -691,8 +691,8 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { id := account.ParticipationID{} id[0] = 10 keys := make(account.StateProofKeys) - keys[100] = []byte{ 100 } - keys[101] = []byte{ 101 } + keys[100] = []byte{100} + keys[101] = []byte{101} keyBytes := protocol.Encode(keys) // Put keys in the body. @@ -711,4 +711,4 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { require.Len(t, mockNode.keys, 2) require.Equal(t, mockNode.keys[100], keys[100]) require.Equal(t, mockNode.keys[101], keys[101]) -} \ No newline at end of file +} From fb917bc77665a08da1865cd2ced07f3fc92e019f Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 10:55:35 -0500 Subject: [PATCH 12/19] Add negative tests. --- daemon/algod/api/server/v2/handlers.go | 8 +- .../algod/api/server/v2/test/handlers_test.go | 106 ++++++++++++++---- daemon/algod/api/server/v2/test/helpers.go | 2 +- 3 files changed, 90 insertions(+), 26 deletions(-) diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 6f3724b48a..c9018e08d4 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -223,16 +223,12 @@ func (v2 *Handlers) AppendKeys(ctx echo.Context, participationID string) error { var keys account.StateProofKeys dec := protocol.NewDecoder(ctx.Request().Body) err = dec.Decode(&keys) - // What is this error? Hit EOF before an object could finish decoding? - if err == io.EOF { - return badRequest(ctx, err, err.Error(), v2.Log) - } 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 include keys") + err = errors.New("empty request, please attach keys to request body") 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 2b8c77459d..a134e4b48e 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -687,28 +687,96 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { Shutdown: dummyShutdownChan, } - // Create test object to append. id := account.ParticipationID{} id[0] = 10 - 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) + t.Run("HappyPath", 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) - // Call handler with request. - err := handler.AppendKeys(c, id.String()) + // Put keys in the body. + e := echo.New() + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(keyBytes)) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) - // Verify that request was properly received and deserialized. - require.NoError(t, err) - require.Equal(t, 200, 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]) + // 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]) + }) + + // Invalid body + t.Run("Invalid body", func(t *testing.T) { + // Put keys in the body. + e := echo.New() + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{ 0x00, 0x01, 0x02 })) + 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: EOF") + }) + + t.Run("Empty body", func(t *testing.T) { + 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) { + expectedErr := errors.New("expected error") + mockNode := makeMockNode(mockLedger, t.Name(), expectedErr) + handler := v2.Handlers{ + Node: mockNode, + Log: logging.Base(), + Shutdown: dummyShutdownChan, + } + + 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 2b0be26fdc..07bfddd76a 100644 --- a/daemon/algod/api/server/v2/test/helpers.go +++ b/daemon/algod/api/server/v2/test/helpers.go @@ -107,7 +107,7 @@ func (m mockNode) RemoveParticipationKey(id account.ParticipationID) error { func (m *mockNode) AppendParticipationKeys(id account.ParticipationID, keys account.StateProofKeys) error { m.id = id m.keys = keys - return nil + return m.err } func makeMockNode(ledger *data.Ledger, genesisID string, nodeError error) *mockNode { From e41656a8f36d1769f43a642b56fd57465f0e9f4c Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 10:58:10 -0500 Subject: [PATCH 13/19] Better invalid msgpack bytes. --- daemon/algod/api/server/v2/test/handlers_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index a134e4b48e..2689f2af29 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -690,7 +690,7 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { id := account.ParticipationID{} id[0] = 10 - t.Run("HappyPath", func(t *testing.T) { + t.Run("Happy path", func(t *testing.T) { // Create test object to append. keys := make(account.StateProofKeys) keys[100] = []byte{100} @@ -719,7 +719,7 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { t.Run("Invalid body", func(t *testing.T) { // Put keys in the body. e := echo.New() - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{ 0x00, 0x01, 0x02 })) + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{ 0x99, 0x88, 0x77 })) rec := httptest.NewRecorder() c := e.NewContext(req, rec) @@ -729,7 +729,7 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { // 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: EOF") + require.Contains(t, rec.Body.String(), "unable to parse keys from body: msgpack decode error") }) t.Run("Empty body", func(t *testing.T) { @@ -751,7 +751,7 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { require.Contains(t, rec.Body.String(), "empty request, please attach keys to request body") }) - t.Run("Internal Error", func(t *testing.T) { + t.Run("Internal error", func(t *testing.T) { expectedErr := errors.New("expected error") mockNode := makeMockNode(mockLedger, t.Name(), expectedErr) handler := v2.Handlers{ From f49aee3696d7ea6d101975b54d132621ae9fb50e Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 11:23:55 -0500 Subject: [PATCH 14/19] fmt again --- daemon/algod/api/server/v2/test/handlers_test.go | 2 +- 1 file changed, 1 insertion(+), 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 2689f2af29..bd633020b9 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -719,7 +719,7 @@ func TestMockNode_AppendParticipationKeys(t *testing.T) { t.Run("Invalid body", func(t *testing.T) { // Put keys in the body. e := echo.New() - req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{ 0x99, 0x88, 0x77 })) + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader([]byte{0x99, 0x88, 0x77})) rec := httptest.NewRecorder() c := e.NewContext(req, rec) From 135f3e9933254ebbfacd95ee1aff1fbabfa2e49c Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 13:26:36 -0500 Subject: [PATCH 15/19] See if this fixes weird reviewdog warning. --- daemon/algod/api/server/v2/test/handlers_test.go | 2 +- 1 file changed, 1 insertion(+), 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 bd633020b9..7e33d6b797 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -673,7 +673,7 @@ func TestTealDryrun(t *testing.T) { tealDryrunTest(t, &gdr, "json", 404, "", false) } -func TestMockNode_AppendParticipationKeys(t *testing.T) { +func TestAppendParticipationKeys(t *testing.T) { numAccounts := 1 numTransactions := 1 offlineAccounts := true From 36976d0b80dee2ceaba1cb99615f49a568ddc1c8 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 8 Dec 2021 14:43:53 -0500 Subject: [PATCH 16/19] Cleanup test a little bit more... --- daemon/algod/api/server/v2/test/handlers_test.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 7e33d6b797..ec380e0642 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -674,17 +674,13 @@ func TestTealDryrun(t *testing.T) { } func TestAppendParticipationKeys(t *testing.T) { - numAccounts := 1 - numTransactions := 1 - offlineAccounts := true - mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts) + mockLedger, _, _, _, releasefunc := testingenv(t, 1, 1, true) defer releasefunc() - dummyShutdownChan := make(chan struct{}) mockNode := makeMockNode(mockLedger, t.Name(), nil) handler := v2.Handlers{ Node: mockNode, Log: logging.Base(), - Shutdown: dummyShutdownChan, + Shutdown: make(chan struct{}), } id := account.ParticipationID{} @@ -715,9 +711,8 @@ func TestAppendParticipationKeys(t *testing.T) { require.Equal(t, mockNode.keys[101], keys[101]) }) - // Invalid body t.Run("Invalid body", func(t *testing.T) { - // Put keys in the body. + // 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() @@ -733,6 +728,7 @@ func TestAppendParticipationKeys(t *testing.T) { }) t.Run("Empty body", func(t *testing.T) { + // Create test object with no keys to append. keys := make(account.StateProofKeys) keyBytes := protocol.Encode(keys) @@ -752,12 +748,13 @@ func TestAppendParticipationKeys(t *testing.T) { }) 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: dummyShutdownChan, + Shutdown: make(chan struct{}), } keys := make(account.StateProofKeys) From 12bebd5eb58a6675ca3f0c5488d8926d75298699 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 9 Dec 2021 15:46:20 -0500 Subject: [PATCH 17/19] id to ID --- daemon/algod/api/algod.oas2.json | 26 +- daemon/algod/api/algod.oas3.yml | 32 +- .../api/server/v2/generated/private/routes.go | 278 +++++++------- .../api/server/v2/generated/private/types.go | 2 +- .../algod/api/server/v2/generated/routes.go | 344 +++++++++--------- daemon/algod/api/server/v2/generated/types.go | 2 +- 6 files changed, 342 insertions(+), 342 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index cf81d16b43..a775e118c7 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -582,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": { @@ -628,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 by ID", "operationId": "GetParticipationKeyByID", "responses": { "200": { @@ -675,7 +675,7 @@ "tags": [ "private" ], - "description": "Given a participation id, append state proof keys to a particular set of participation keys.", + "description": "Given a participation ID, append state proof keys to a particular set of participation keys", "consumes": [ "application/msgpack" ], @@ -684,7 +684,7 @@ ], "parameters": [ { - "description": "The state proof keys to add to an existing participation id", + "description": "The state proof keys to add to an existing participation ID", "name": "keymap", "in": "body", "required": true, @@ -1008,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" @@ -1022,7 +1022,7 @@ { "pattern": "[A-Z0-9]+", "type": "string", - "description": "A transaction id", + "description": "A transaction ID", "name": "txid", "in": "path", "required": true @@ -1033,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" } @@ -1072,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" ], @@ -1135,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" ], @@ -2645,7 +2645,7 @@ } }, "ParticipationKeyResponse": { - "description": "A detailed description of a participation id", + "description": "A detailed description of a participation ID", "schema": { "$ref": "#/definitions/ParticipationKey" } @@ -2662,7 +2662,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 e69358f607..2b50980bed 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" } }, @@ -1893,7 +1893,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": [ { @@ -1967,7 +1967,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": [ { @@ -2620,7 +2620,7 @@ "schema": { "properties": { "partId": { - "description": "encoding of the participation id.", + "description": "encoding of the participation ID.", "type": "string" } }, @@ -2697,7 +2697,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": [ { @@ -2759,13 +2759,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": [ { @@ -2786,7 +2786,7 @@ } } }, - "description": "A detailed description of a participation id" + "description": "A detailed description of a participation ID" }, "400": { "content": { @@ -2833,13 +2833,13 @@ "description": "Unknown Error" } }, - "summary": "Get participation key info by id", + "summary": "Get participation key info by ID", "tags": [ "private" ] }, "post": { - "description": "Given a participation id, append state proof keys to a particular set of participation keys.", + "description": "Given a participation ID, append state proof keys to a particular set of participation keys", "operationId": "AppendKeys", "parameters": [ { @@ -2860,7 +2860,7 @@ } } }, - "description": "The state proof keys to add to an existing participation id", + "description": "The state proof keys to add to an existing participation ID", "required": true }, "responses": { @@ -2872,7 +2872,7 @@ } } }, - "description": "A detailed description of a participation id" + "description": "A detailed description of a participation ID" }, "400": { "content": { @@ -3706,11 +3706,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, @@ -3746,7 +3746,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 45c852d5f1..2e045d27e0 100644 --- a/daemon/algod/api/server/v2/generated/private/routes.go +++ b/daemon/algod/api/server/v2/generated/private/routes.go @@ -29,10 +29,10 @@ 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 by ID // (GET /v2/participation/{participation-id}) GetParticipationKeyByID(ctx echo.Context, participationId string) error // Append state proof keys to a participation key @@ -311,143 +311,143 @@ func RegisterHandlers(router interface { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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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 78137ac3ea..6b52870fc9 100644 --- a/daemon/algod/api/server/v2/generated/private/types.go +++ b/daemon/algod/api/server/v2/generated/private/types.go @@ -635,7 +635,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 2962a328a6..ad0f1d9f89 100644 --- a/daemon/algod/api/server/v2/generated/routes.go +++ b/daemon/algod/api/server/v2/generated/routes.go @@ -617,178 +617,178 @@ func RegisterHandlers(router interface { var swaggerSpec = []string{ "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", - "HcEdLqVk55g78cPl/wsAAP//K7GmSonxAAA=", + "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", + "cIdLKdk55k78cPn/AgAA//9JLTjIifEAAA==", } // 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 b9066ba0ec..4fad039004 100644 --- a/daemon/algod/api/server/v2/generated/types.go +++ b/daemon/algod/api/server/v2/generated/types.go @@ -635,7 +635,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"` } From 30008d2492da0a7a5546e824a2663bdbd144f876 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 9 Dec 2021 15:51:36 -0500 Subject: [PATCH 18/19] Add missing PartitionTest call. --- daemon/algod/api/server/v2/test/handlers_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index ec380e0642..ced3357d3a 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -674,6 +674,8 @@ func TestTealDryrun(t *testing.T) { } func TestAppendParticipationKeys(t *testing.T) { + partitiontest.PartitionTest(t) + mockLedger, _, _, _, releasefunc := testingenv(t, 1, 1, true) defer releasefunc() mockNode := makeMockNode(mockLedger, t.Name(), nil) From c62d6c0d961b3e882d443574c4ebb860c49401a6 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 10 Dec 2021 13:38:25 -0500 Subject: [PATCH 19/19] Update summary --- daemon/algod/api/algod.oas2.json | 2 +- daemon/algod/api/algod.oas3.yml | 2 +- .../api/server/v2/generated/private/routes.go | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index a775e118c7..8302f7b9fe 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -635,7 +635,7 @@ "schemes": [ "http" ], - "summary": "Get participation key info by ID", + "summary": "Get participation key info given a participation ID", "operationId": "GetParticipationKeyByID", "responses": { "200": { diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index 2b50980bed..ed2896fa6c 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -2833,7 +2833,7 @@ "description": "Unknown Error" } }, - "summary": "Get participation key info by ID", + "summary": "Get participation key info given a participation ID", "tags": [ "private" ] diff --git a/daemon/algod/api/server/v2/generated/private/routes.go b/daemon/algod/api/server/v2/generated/private/routes.go index 2e045d27e0..05ada92751 100644 --- a/daemon/algod/api/server/v2/generated/private/routes.go +++ b/daemon/algod/api/server/v2/generated/private/routes.go @@ -32,7 +32,7 @@ type ServerInterface interface { // 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 @@ -442,12 +442,12 @@ var swaggerSpec = []string{ "l8wYNyj3d5d+ff3eS1IXsCb2iNaH4t1Lim176PqWOuCTffTqsw75rEOkHf7p/Q1/CvKSpUDOoCiFpJLl", "a/Izr+/z3Nyty7Jo+ltb9Hs6zXgjqchgATxxCiuZiWzt69a0AF6ADRn3DJXpx3bxSRv+GgxL2afq6+cs", "+kjb5+e7FkzkhfsfYP3NGpt2PMaIT9hFcaNn2NVFA87Yjd7U/6x4PiuemxsvOwtPzH6JehM+kNPdk8f+", - "Ymvs6jfV/aF38Tn+UnH9t31D+LNK+KwSbq4SvoOIMKLUblQGm8O6fW2A6U9ZmGNsKwkZJ8M1r3IqiYJd", - "YxLHCNFFIu5DJdy3RxallXXIKCewYgpfNeiphLt20j7rs8/67BM6otquaNpWx95uzQWsC1rWzoxaVjoT", - "V7b6S1QrYhFYmrsqaljXrE6H0IJ4AM0tI/KTu1aXr80ULllmbDbNCjD2U63rTGefO9okrxoIzZtbC8Zx", - "AFQVOIotF0iD/H0FqeD28ZzOwZrD7I11AGNK9o8KUKM52jgcR+PWyYpbxkhxvlsbW/2DkOsNgfP6BZzW", - "39MrynQyF9Jd30EK9VMuNNB86uocdH61t5GDH9vP9kd+ndYVeKMfu4kksa8uz8M3ajK4wowoXKk6F+r9", - "B0NwLGrmFrFJ8DmaTjGzfSmUno6uxx87yT/hxw81jT/W+6uj9fWH6/8fAAD//2Luvp+npQAA", + "Ymvs6jfV/aF38Tn+UnH9t31D+LNK+KwSbq4SvoOIMKLUOiURYbqbRHr7CgIzorIw7dgWFzJ+h2te5VQS", + "BbuGKY4RogtO3IeWuG8nLUor66NRTmDFFD50EFmwu/XbPqu4zyruEzq12q5o2obI3p7OBawLWtb+jVpW", + "OhNXtiBMVCtiXViau8JqWOqszpDQgngAzcUj8pO7aZevzRQuWWbMOM0KMCZVretMZ59O2uSzGgjNM1wL", + "xnEAVBU4iq0gSIOUfgWp4PY9nc5Zm8PsjfUJY0r2jwpQoznaOBxH49Zhi1vGSL2+W9tf/bOR6w2x9PpR", + "nNbf0yvKdDIX0t3oQQr1szA00HzqSh90frUXlIMf2y/5R36d1kV5ox+7uSWxry71wzdqkrrCJClcqTo9", + "6v0HQ3Csc+YWscn5OZpOMdl9KZSejq7HHzv5QOHHDzWNP9b7q6P19Yfr/x8AAP//mUjHZrqlAAA=", } // GetSwagger returns the Swagger specification corresponding to the generated code