Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
60d761a
WIP participation metrics.
winder Jul 30, 2021
6d46f94
Complete interface changes for storing participation metrics.
winder Aug 2, 2021
db9a420
Use a single record + type instead of separate functions. Move regist…
winder Aug 2, 2021
decdd0d
Reuse SimpleKeyManager instead of having 3 implementations of it.
winder Aug 2, 2021
bdb95b9
Another SimpleKeyManager fix.
winder Aug 2, 2021
56d4796
Missed another SimpleKeyManager
winder Aug 3, 2021
83a3065
Rename participationRegistry
winder Aug 3, 2021
626a56e
fmt and lint
winder Aug 3, 2021
53be105
Add db initialize helper and tests.
winder Aug 3, 2021
7c1c7da
Participation registry created with db initialize function.
winder Aug 3, 2021
97870ec
fmt and lint
winder Aug 3, 2021
18f060d
Fix some error names.
winder Aug 3, 2021
ad0f283
Insert, Get, GetAll
winder Aug 4, 2021
aab152f
Delete
winder Aug 4, 2021
6c111ef
Register
winder Aug 4, 2021
5ac6446
Record
winder Aug 5, 2021
ea2badf
fmt and lint
winder Aug 5, 2021
d8b8258
sql.Row.Err does not exist in go 1.14.7
winder Aug 5, 2021
608ea43
Remove unreachable code
winder Aug 5, 2021
6c97d0d
Initialize registry to a real file.
winder Aug 5, 2021
76e8a46
Don't return error in KeyManager agreement abstraction.
winder Aug 5, 2021
fe01a6f
Improve error message.
winder Aug 5, 2021
e0ab245
Use db.Pair, cleanup things.
winder Aug 5, 2021
cd22527
Fix unit test.
winder Aug 5, 2021
25e526b
Fix error
winder Aug 5, 2021
d4bd5d3
Another fix...
winder Aug 5, 2021
3fb1e2c
Use map for participation actions, more tests.
winder Aug 5, 2021
ed38e01
fmt and lint
winder Aug 5, 2021
861cb75
Move record location.
winder Aug 5, 2021
e5020d3
Add test for record function and make sure test helpers aren't in the…
winder Aug 6, 2021
1c9639f
Add closer and close resources in tests.
winder Aug 6, 2021
5bf5c05
Record -> RecordAsync
winder Aug 6, 2021
0f6a581
Add FullNode test.
winder Aug 6, 2021
07810f5
Use Start and Stop instead of manually starting the monitoring routines.
winder Aug 6, 2021
0c3cd8f
Final pass over code.
winder Aug 6, 2021
cd820eb
Fix race.
winder Aug 6, 2021
89c2a88
Fix lock
winder Aug 6, 2021
80b8f84
Don't worry about max bal lookback.
winder Aug 6, 2021
74eaa2b
Remove channel, non blocking will be done in a future PR
winder Aug 6, 2021
e6d88a0
deadlock
winder Aug 6, 2021
dd20a36
Fix import...
winder Aug 6, 2021
ef1b8c4
Add newline to imports...
winder Aug 6, 2021
8703a00
Really fix the race.
winder Aug 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions agreement/abstractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ type KeyManager interface {
// valid for the provided votingRound, and were available at
// keysRound.
VotingKeys(votingRound, keysRound basics.Round) []account.Participation

// RecordAsync indicates that the given participation action has been taken.
// The operation needs to be asynchronous to avoid impacting agreement.
RecordAsync(account basics.Address, round basics.Round, participationType account.ParticipationAction)
}

// MessageHandle is an ID referring to a specific message.
Expand Down
9 changes: 5 additions & 4 deletions agreement/agreementtest/keyManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/algorand/go-algorand/data/basics"
)

// SimpleKeyManager provides a simple implementation of a KeyManager.
// SimpleKeyManager provides a simple implementation of a KeyManager for unit tests.
type SimpleKeyManager []account.Participation

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

// DeleteOldKeys implements KeyManager.DeleteOldKeys.
func (m SimpleKeyManager) DeleteOldKeys(r basics.Round) {
// for _, acc := range m {
// acc.DeleteOldKeys(r)
// }
}

// RecordAsync implements KeyManager.RecordAsync.
func (m SimpleKeyManager) RecordAsync(account basics.Address, round basics.Round, action account.ParticipationAction) {
}
2 changes: 1 addition & 1 deletion agreement/cryptoVerifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ func BenchmarkCryptoVerifierProposalVertification(b *testing.B) {
pn := &asyncPseudonode{
factory: testBlockFactory{Owner: 0},
validator: testBlockValidator{},
keys: simpleKeyManager(participations),
keys: makeRecordingKeyManager(participations),
ledger: ledger,
log: serviceLogger{logging.Base()},
}
Expand Down
3 changes: 2 additions & 1 deletion agreement/fuzzer/fuzzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/algorand/go-deadlock"

"github.com/algorand/go-algorand/agreement"
"github.com/algorand/go-algorand/agreement/agreementtest"
"github.com/algorand/go-algorand/agreement/gossip"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
Expand Down Expand Up @@ -132,7 +133,7 @@ func (n *Fuzzer) initAgreementNode(nodeID int, filters ...NetworkFilterFactory)
Logger: logger,
Ledger: n.ledgers[nodeID],
Network: gossip.WrapNetwork(n.facades[nodeID], logger),
KeyManager: simpleKeyManager(n.accounts[nodeID : nodeID+1]),
KeyManager: agreementtest.SimpleKeyManager(n.accounts[nodeID : nodeID+1]),
BlockValidator: n.blockValidator,
BlockFactory: testBlockFactory{Owner: nodeID},
Clock: n.clocks[nodeID],
Expand Down
34 changes: 0 additions & 34 deletions agreement/fuzzer/keyManager_test.go

This file was deleted.

63 changes: 63 additions & 0 deletions agreement/keyManager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 <https://www.gnu.org/licenses/>.

package agreement

import (
"github.com/algorand/go-deadlock"

"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
)

func makeRecordingKeyManager(accounts []account.Participation) *recordingKeyManager {
return &recordingKeyManager{
keys: accounts,
recording: make(map[basics.Address]map[account.ParticipationAction]basics.Round),
}
}

// recordingKeyManager provides a simple implementation of a KeyManager for unit tests.
type recordingKeyManager struct {
keys []account.Participation
recording map[basics.Address]map[account.ParticipationAction]basics.Round
mutex deadlock.Mutex
}

// VotingKeys implements KeyManager.VotingKeys.
func (m *recordingKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
var km []account.Participation
for _, acc := range m.keys {
if acc.OverlapsInterval(votingRound, votingRound) {
km = append(km, acc)
}
}
return km
}

// DeleteOldKeys implements KeyManager.DeleteOldKeys.
func (m *recordingKeyManager) DeleteOldKeys(r basics.Round) {
}

// Record implements KeyManager.Record.
func (m *recordingKeyManager) RecordAsync(acct basics.Address, round basics.Round, action account.ParticipationAction) {
m.mutex.Lock()
defer m.mutex.Unlock()
if _, ok := m.recording[acct]; !ok {
m.recording[acct] = make(map[account.ParticipationAction]basics.Round)
}
m.recording[acct][action] = round
}
2 changes: 2 additions & 0 deletions agreement/pseudonode.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ func (t pseudonodeVotesTask) execute(verifier *AsyncVoteVerifier, quit chan stru
for _, r := range verifiedResults {
select {
case t.out <- messageEvent{T: voteVerified, Input: r.message, Err: makeSerErr(r.err)}:
t.node.keys.RecordAsync(r.v.R.Sender, r.v.R.Round, account.Vote)
case <-quit:
return
case <-t.context.Done():
Expand Down Expand Up @@ -531,6 +532,7 @@ func (t pseudonodeProposalsTask) execute(verifier *AsyncVoteVerifier, quit chan
for _, r := range verifiedVotes {
select {
case t.out <- messageEvent{T: voteVerified, Input: r.message, Err: makeSerErr(r.err)}:
t.node.keys.RecordAsync(r.v.R.Sender, r.v.R.Round, account.BlockProposal)
case <-quit:
return
case <-t.context.Done():
Expand Down
12 changes: 10 additions & 2 deletions agreement/pseudonode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func TestPseudonode(t *testing.T) {
sLogger := serviceLogger{logging.NewLogger()}
sLogger.SetLevel(logging.Warn)

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

func (k *KeyManagerProxy) RecordAsync(account basics.Address, round basics.Round, action account.ParticipationAction) {
}

func TestPseudonodeLoadingOfParticipationKeys(t *testing.T) {
partitiontest.PartitionTest(t)

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

keyManager := simpleKeyManager(accounts)
keyManager := makeRecordingKeyManager(accounts)
pb := makePseudonode(pseudonodeParams{
factory: testBlockFactory{Owner: 0},
validator: testBlockValidator{},
Expand Down
18 changes: 1 addition & 17 deletions agreement/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,6 @@ func (c *testingClock) fire(d time.Duration) {
close(c.TA[d])
}

type simpleKeyManager []account.Participation

func (m simpleKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
var km []account.Participation
for _, acc := range m {
if acc.OverlapsInterval(votingRound, votingRound) {
km = append(km, acc)
}
}
return km
}

func (m simpleKeyManager) DeleteOldKeys(basics.Round) {
// noop
}

type testingNetwork struct {
validator BlockValidator

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

Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ const CrashFilename = "crash.sqlite"
// It is used to track in-progress compact certificates.
const CompactCertFilename = "compactcert.sqlite"

// ParticipationRegistryFilename is the name of the participation registry database file.
// It is used for tracking participation key metadata.
const ParticipationRegistryFilename = "partregistry.sqlite"

// ConfigurableConsensusProtocolsFilename defines a set of consensus prototocols that
// are to be loaded from the data directory ( if present ), to override the
// built-in supported consensus protocols.
Expand Down
20 changes: 20 additions & 0 deletions data/account/participation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package account

import (
"bytes"
"context"
"database/sql"
"encoding/binary"
"fmt"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: leave this spacing line.

"github.com/algorand/go-algorand/config"
Expand Down Expand Up @@ -56,6 +58,24 @@ type Participation struct {
KeyDilution uint64
}

// ParticipationID computes a ParticipationID.
func (part Participation) ParticipationID() ParticipationID {
data := new(bytes.Buffer)

data.Write(part.Parent[:])
binary.Write(data, binary.LittleEndian, part.FirstValid)
binary.Write(data, binary.LittleEndian, part.LastValid)
binary.Write(data, binary.LittleEndian, part.KeyDilution)
if part.VRF != nil {
data.Write(part.VRF.PK[:])
}

// this too?
//part.Write(part.Voting.SubKeyPK[:])

return ParticipationID(crypto.Hash(data.Bytes()))
}
Comment on lines +62 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The common way we calculate a hash of a structure is by msgp-ing it. Are you thinking of any particular reason we shouldn't do it here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that the subkeys will change over time, so this was an attempt to make sure the same keyset wouldn't produce different ParticipationIDs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While what you just wrote is correct, it's not what I meant.
I was thinking of -

serializing := Participation{
   FirstValid: part.FirstValid,
   ...

while omitting the changing parts ( i.e. Voting keys ).

then

   return ParticipationID(crypto.Hash(serializing.Marshal(nil)))


// PersistedParticipation encapsulates the static state of the participation
// for a single address at any given moment, while providing the ability
// to handle persistence and deletion of secrets.
Expand Down
Loading