Skip to content

Commit 6029629

Browse files
authored
Participation Metrics (#2677)
New database for storing participation metadata and some initial integration with agreement layer.
1 parent bfeba29 commit 6029629

18 files changed

+1285
-60
lines changed

agreement/abstractions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ type KeyManager interface {
229229
// valid for the provided votingRound, and were available at
230230
// keysRound.
231231
VotingKeys(votingRound, keysRound basics.Round) []account.Participation
232+
233+
// RecordAsync indicates that the given participation action has been taken.
234+
// The operation needs to be asynchronous to avoid impacting agreement.
235+
RecordAsync(account basics.Address, round basics.Round, participationType account.ParticipationAction)
232236
}
233237

234238
// MessageHandle is an ID referring to a specific message.

agreement/agreementtest/keyManager.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/algorand/go-algorand/data/basics"
2222
)
2323

24-
// SimpleKeyManager provides a simple implementation of a KeyManager.
24+
// SimpleKeyManager provides a simple implementation of a KeyManager for unit tests.
2525
type SimpleKeyManager []account.Participation
2626

2727
// VotingKeys implements KeyManager.VotingKeys.
@@ -37,7 +37,8 @@ func (m SimpleKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Part
3737

3838
// DeleteOldKeys implements KeyManager.DeleteOldKeys.
3939
func (m SimpleKeyManager) DeleteOldKeys(r basics.Round) {
40-
// for _, acc := range m {
41-
// acc.DeleteOldKeys(r)
42-
// }
40+
}
41+
42+
// RecordAsync implements KeyManager.RecordAsync.
43+
func (m SimpleKeyManager) RecordAsync(account basics.Address, round basics.Round, action account.ParticipationAction) {
4344
}

agreement/cryptoVerifier_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func BenchmarkCryptoVerifierProposalVertification(b *testing.B) {
314314
pn := &asyncPseudonode{
315315
factory: testBlockFactory{Owner: 0},
316316
validator: testBlockValidator{},
317-
keys: simpleKeyManager(participations),
317+
keys: makeRecordingKeyManager(participations),
318318
ledger: ledger,
319319
log: serviceLogger{logging.Base()},
320320
}

agreement/fuzzer/fuzzer_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/algorand/go-deadlock"
3030

3131
"github.com/algorand/go-algorand/agreement"
32+
"github.com/algorand/go-algorand/agreement/agreementtest"
3233
"github.com/algorand/go-algorand/agreement/gossip"
3334
"github.com/algorand/go-algorand/config"
3435
"github.com/algorand/go-algorand/crypto"
@@ -132,7 +133,7 @@ func (n *Fuzzer) initAgreementNode(nodeID int, filters ...NetworkFilterFactory)
132133
Logger: logger,
133134
Ledger: n.ledgers[nodeID],
134135
Network: gossip.WrapNetwork(n.facades[nodeID], logger),
135-
KeyManager: simpleKeyManager(n.accounts[nodeID : nodeID+1]),
136+
KeyManager: agreementtest.SimpleKeyManager(n.accounts[nodeID : nodeID+1]),
136137
BlockValidator: n.blockValidator,
137138
BlockFactory: testBlockFactory{Owner: nodeID},
138139
Clock: n.clocks[nodeID],

agreement/fuzzer/keyManager_test.go

Lines changed: 0 additions & 34 deletions
This file was deleted.

agreement/keyManager_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (C) 2019-2021 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package agreement
18+
19+
import (
20+
"github.com/algorand/go-deadlock"
21+
22+
"github.com/algorand/go-algorand/data/account"
23+
"github.com/algorand/go-algorand/data/basics"
24+
)
25+
26+
func makeRecordingKeyManager(accounts []account.Participation) *recordingKeyManager {
27+
return &recordingKeyManager{
28+
keys: accounts,
29+
recording: make(map[basics.Address]map[account.ParticipationAction]basics.Round),
30+
}
31+
}
32+
33+
// recordingKeyManager provides a simple implementation of a KeyManager for unit tests.
34+
type recordingKeyManager struct {
35+
keys []account.Participation
36+
recording map[basics.Address]map[account.ParticipationAction]basics.Round
37+
mutex deadlock.Mutex
38+
}
39+
40+
// VotingKeys implements KeyManager.VotingKeys.
41+
func (m *recordingKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
42+
var km []account.Participation
43+
for _, acc := range m.keys {
44+
if acc.OverlapsInterval(votingRound, votingRound) {
45+
km = append(km, acc)
46+
}
47+
}
48+
return km
49+
}
50+
51+
// DeleteOldKeys implements KeyManager.DeleteOldKeys.
52+
func (m *recordingKeyManager) DeleteOldKeys(r basics.Round) {
53+
}
54+
55+
// Record implements KeyManager.Record.
56+
func (m *recordingKeyManager) RecordAsync(acct basics.Address, round basics.Round, action account.ParticipationAction) {
57+
m.mutex.Lock()
58+
defer m.mutex.Unlock()
59+
if _, ok := m.recording[acct]; !ok {
60+
m.recording[acct] = make(map[account.ParticipationAction]basics.Round)
61+
}
62+
m.recording[acct][action] = round
63+
}

agreement/pseudonode.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ func (t pseudonodeVotesTask) execute(verifier *AsyncVoteVerifier, quit chan stru
444444
for _, r := range verifiedResults {
445445
select {
446446
case t.out <- messageEvent{T: voteVerified, Input: r.message, Err: makeSerErr(r.err)}:
447+
t.node.keys.RecordAsync(r.v.R.Sender, r.v.R.Round, account.Vote)
447448
case <-quit:
448449
return
449450
case <-t.context.Done():
@@ -531,6 +532,7 @@ func (t pseudonodeProposalsTask) execute(verifier *AsyncVoteVerifier, quit chan
531532
for _, r := range verifiedVotes {
532533
select {
533534
case t.out <- messageEvent{T: voteVerified, Input: r.message, Err: makeSerErr(r.err)}:
535+
t.node.keys.RecordAsync(r.v.R.Sender, r.v.R.Round, account.BlockProposal)
534536
case <-quit:
535537
return
536538
case <-t.context.Done():

agreement/pseudonode_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func TestPseudonode(t *testing.T) {
145145
sLogger := serviceLogger{logging.NewLogger()}
146146
sLogger.SetLevel(logging.Warn)
147147

148-
keyManager := simpleKeyManager(accounts)
148+
keyManager := makeRecordingKeyManager(accounts)
149149
pb := makePseudonode(pseudonodeParams{
150150
factory: testBlockFactory{Owner: 0},
151151
validator: testBlockValidator{},
@@ -222,7 +222,12 @@ func TestPseudonode(t *testing.T) {
222222
}
223223
messageEvent, typeOk := ev.(messageEvent)
224224
assert.True(t, true, typeOk)
225+
// Verify votes are recorded - everyone is voting and proposing blocks.
226+
keyManager.mutex.Lock()
227+
assert.Equal(t, startRound, keyManager.recording[messageEvent.Input.Vote.R.Sender][account.Vote])
228+
assert.Equal(t, startRound, keyManager.recording[messageEvent.Input.Vote.R.Sender][account.BlockProposal])
225229
events[messageEvent.t()] = append(events[messageEvent.t()], messageEvent)
230+
keyManager.mutex.Unlock()
226231
}
227232
assert.Subset(t, []int{5, 6, 7, 8, 9, 10}, []int{len(events[voteVerified])})
228233
assert.Equal(t, 0, len(events[payloadVerified]))
@@ -390,6 +395,9 @@ func (k *KeyManagerProxy) VotingKeys(votingRound, balanceRound basics.Round) []a
390395
return k.target(votingRound, balanceRound)
391396
}
392397

398+
func (k *KeyManagerProxy) RecordAsync(account basics.Address, round basics.Round, action account.ParticipationAction) {
399+
}
400+
393401
func TestPseudonodeLoadingOfParticipationKeys(t *testing.T) {
394402
partitiontest.PartitionTest(t)
395403

@@ -403,7 +411,7 @@ func TestPseudonodeLoadingOfParticipationKeys(t *testing.T) {
403411
sLogger := serviceLogger{logging.NewLogger()}
404412
sLogger.SetLevel(logging.Warn)
405413

406-
keyManager := simpleKeyManager(accounts)
414+
keyManager := makeRecordingKeyManager(accounts)
407415
pb := makePseudonode(pseudonodeParams{
408416
factory: testBlockFactory{Owner: 0},
409417
validator: testBlockValidator{},

agreement/service_test.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,6 @@ func (c *testingClock) fire(d time.Duration) {
105105
close(c.TA[d])
106106
}
107107

108-
type simpleKeyManager []account.Participation
109-
110-
func (m simpleKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
111-
var km []account.Participation
112-
for _, acc := range m {
113-
if acc.OverlapsInterval(votingRound, votingRound) {
114-
km = append(km, acc)
115-
}
116-
}
117-
return km
118-
}
119-
120-
func (m simpleKeyManager) DeleteOldKeys(basics.Round) {
121-
// noop
122-
}
123-
124108
type testingNetwork struct {
125109
validator BlockValidator
126110

@@ -743,7 +727,7 @@ func setupAgreementWithValidator(t *testing.T, numNodes int, traceLevel traceLev
743727
m.coserviceListener = am.coserviceListener(nodeID(i))
744728
clocks[i] = makeTestingClock(m)
745729
ledgers[i] = ledgerFactory(balances)
746-
keys := simpleKeyManager(accounts[i : i+1])
730+
keys := makeRecordingKeyManager(accounts[i : i+1])
747731
endpoint := baseNetwork.testingNetworkEndpoint(nodeID(i))
748732
ilog := log.WithFields(logging.Fields{"Source": "service-" + strconv.Itoa(i)})
749733

config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,10 @@ const CrashFilename = "crash.sqlite"
439439
// It is used to track in-progress compact certificates.
440440
const CompactCertFilename = "compactcert.sqlite"
441441

442+
// ParticipationRegistryFilename is the name of the participation registry database file.
443+
// It is used for tracking participation key metadata.
444+
const ParticipationRegistryFilename = "partregistry.sqlite"
445+
442446
// ConfigurableConsensusProtocolsFilename defines a set of consensus prototocols that
443447
// are to be loaded from the data directory ( if present ), to override the
444448
// built-in supported consensus protocols.

0 commit comments

Comments
 (0)