Skip to content

Commit

Permalink
Integrate stateproof with the updated account manager (algorand#3350)
Browse files Browse the repository at this point in the history
* Add ParticipationRoundSecrets and replace in all places that do not require all participation secrets

* [WIP] Major refactoring, replacing ParticipationRoundSecrets with ParticipationRecordForRound, implementing missing StateProof logic in participationRegistry

* [WIP] Fix and rewrite tests

* [WIP] Fix bugs in stateproof DB

* [WIP] Fix a bug, refactor and comment for the linter

* [WIP] Separate database methods from keystore.go to PersistentKeystore.go, some refactoring

* [WIP] Rename merklekeystore Signer and assorted types

* [WIP] refactor

* [WIP] more refactoring and some renaming of confusing types

* [WIP] Add unit test to participation registry

* Write keys to registry in one transaction instead of one-by-one

* Added unit test for PersistentKeystore.go and removed unused code

* .

* Refactoring

* integrate with MSS changes.

* fix test bug

* Update unit test

* give more time for statproof test

Co-authored-by: algoidan <[email protected]>
  • Loading branch information
Aharonee and algoidan authored Jan 13, 2022
1 parent b93f0e5 commit 2f2dd3d
Show file tree
Hide file tree
Showing 30 changed files with 832 additions and 681 deletions.
2 changes: 1 addition & 1 deletion agreement/abstractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ type KeyManager interface {
// VotingKeys returns an immutable array of voting keys that are
// valid for the provided votingRound, and were available at
// keysRound.
VotingKeys(votingRound, keysRound basics.Round) []account.Participation
VotingKeys(votingRound, keysRound basics.Round) []account.ParticipationRecordForRound

// Record indicates that the given participation action has been taken.
// The operation needs to be asynchronous to avoid impacting agreement.
Expand Down
33 changes: 30 additions & 3 deletions agreement/agreementtest/keyManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package agreementtest

import (
"github.com/algorand/go-algorand/crypto/merklekeystore"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
)
Expand All @@ -25,11 +26,37 @@ import (
type SimpleKeyManager []account.Participation

// VotingKeys implements KeyManager.VotingKeys.
func (m SimpleKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
var km []account.Participation
func (m SimpleKeyManager) VotingKeys(votingRound, _ basics.Round) []account.ParticipationRecordForRound {
var km []account.ParticipationRecordForRound
for _, acc := range m {
if acc.OverlapsInterval(votingRound, votingRound) {
km = append(km, acc)
record := account.ParticipationRecord{
ParticipationID: acc.ID(),
Account: acc.Parent,
FirstValid: acc.FirstValid,
LastValid: acc.LastValid,
KeyDilution: acc.KeyDilution,
LastVote: 0,
LastBlockProposal: 0,
LastStateProof: 0,
EffectiveFirst: acc.FirstValid,
EffectiveLast: acc.LastValid,
VRF: acc.VRF,
Voting: acc.Voting,
}
// Usually this struct will be retrieved from the registry, however in this test
// case we can allow ourselves to generate it from the data already in memory
// (within the Participation after calling FillDB)
var stateproofSinger *merklekeystore.Signer
stateproofSinger = nil
if acc.StateProofSecrets != nil {
stateproofSinger = acc.StateProofSecrets.GetSigner(uint64(votingRound))
}
partRecForRound := account.ParticipationRecordForRound{
ParticipationRecord: record,
StateProofSecrets: stateproofSinger,
}
km = append(km, partRecForRound)
}
}
return km
Expand Down
30 changes: 26 additions & 4 deletions agreement/keyManager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ package agreement
import (
"testing"

"github.com/algorand/go-deadlock"
"github.com/stretchr/testify/require"

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

func makeRecordingKeyManager(accounts []account.Participation) *recordingKeyManager {
Expand All @@ -41,11 +42,32 @@ type recordingKeyManager struct {
}

// VotingKeys implements KeyManager.VotingKeys.
func (m *recordingKeyManager) VotingKeys(votingRound, _ basics.Round) []account.Participation {
var km []account.Participation
func (m *recordingKeyManager) VotingKeys(votingRound, _ basics.Round) []account.ParticipationRecordForRound {
var km []account.ParticipationRecordForRound
for _, acc := range m.keys {
if acc.OverlapsInterval(votingRound, votingRound) {
km = append(km, acc)
var signerInRound merklekeystore.Signer
if acc.StateProofSecrets != nil {
acc.StateProofSecrets.GetSigner(uint64(votingRound))
}
partRecordForRound := account.ParticipationRecordForRound{
ParticipationRecord: account.ParticipationRecord{
ParticipationID: acc.ID(),
Account: acc.Parent,
FirstValid: acc.FirstValid,
LastValid: acc.LastValid,
KeyDilution: acc.KeyDilution,
LastVote: 0,
LastBlockProposal: 0,
LastStateProof: 0,
EffectiveFirst: 0,
EffectiveLast: acc.LastValid,
VRF: acc.VRF,
Voting: acc.Voting,
},
StateProofSecrets: &signerInRound,
}
km = append(km, partRecordForRound)
}
}
return km
Expand Down
28 changes: 14 additions & 14 deletions agreement/pseudonode.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ type asyncPseudonode struct {
quit chan struct{} // a quit signal for the verifier goroutines
closeWg *sync.WaitGroup // frontend waitgroup to get notified when all the verifier goroutines are done.
monitor *coserviceMonitor
participationKeysRound basics.Round // the round to which the participationKeys matches
participationKeys []account.Participation // the list of the participation keys for round participationKeysRound
participationKeysRound basics.Round // the round to which the participationKeys matches
participationKeys []account.ParticipationRecordForRound // the list of the participation keys for round participationKeysRound

proposalsVerifier *pseudonodeVerifier // dynamically generated verifier goroutine that manages incoming proposals making request.
votesVerifier *pseudonodeVerifier // dynamically generated verifier goroutine that manages incoming votes making request.
Expand All @@ -93,7 +93,7 @@ type pseudonodeBaseTask struct {
node *asyncPseudonode
context context.Context // the context associated with that task; context might expire for a single task but remain valid for others.
out chan externalEvent
participation []account.Participation
participation []account.ParticipationRecordForRound
}

type pseudonodeVotesTask struct {
Expand Down Expand Up @@ -198,7 +198,7 @@ func (n asyncPseudonode) MakeVotes(ctx context.Context, r round, p period, s ste

// load the participation keys from the account manager ( as needed ) for the
// current round.
func (n *asyncPseudonode) loadRoundParticipationKeys(voteRound basics.Round) []account.Participation {
func (n *asyncPseudonode) loadRoundParticipationKeys(voteRound basics.Round) []account.ParticipationRecordForRound {
// if we've already loaded up the keys, then just skip loading them.
if n.participationKeysRound == voteRound {
return n.participationKeys
Expand Down Expand Up @@ -267,7 +267,7 @@ func (n asyncPseudonode) makePseudonodeVerifier(voteVerifier *AsyncVoteVerifier)
}

// makeProposals creates a slice of block proposals for the given round and period.
func (n asyncPseudonode) makeProposals(round basics.Round, period period, accounts []account.Participation) ([]proposal, []unauthenticatedVote) {
func (n asyncPseudonode) makeProposals(round basics.Round, period period, accounts []account.ParticipationRecordForRound) ([]proposal, []unauthenticatedVote) {
ve, err := n.factory.AssembleBlock(round)
if err != nil {
if err != ErrAssembleBlockRoundStale {
Expand All @@ -278,16 +278,16 @@ func (n asyncPseudonode) makeProposals(round basics.Round, period period, accoun

votes := make([]unauthenticatedVote, 0, len(accounts))
proposals := make([]proposal, 0, len(accounts))
for _, account := range accounts {
payload, proposal, err := proposalForBlock(account.Address(), account.VRFSecrets(), ve, period, n.ledger)
for _, acc := range accounts {
payload, proposal, err := proposalForBlock(acc.Account, acc.VRF, ve, period, n.ledger)
if err != nil {
n.log.Errorf("pseudonode.makeProposals: could not create proposal for block (address %v): %v", account.Address(), err)
n.log.Errorf("pseudonode.makeProposals: could not create proposal for block (address %v): %v", acc.Account, err)
continue
}

// attempt to make the vote
rv := rawVote{Sender: account.Address(), Round: round, Period: period, Step: propose, Proposal: proposal}
uv, err := makeVote(rv, account.VotingSigner(), account.VRFSecrets(), n.ledger)
rv := rawVote{Sender: acc.Account, Round: round, Period: period, Step: propose, Proposal: proposal}
uv, err := makeVote(rv, acc.VotingSigner(), acc.VRF, n.ledger)
if err != nil {
n.log.Warnf("pseudonode.makeProposals: could not create vote: %v", err)
continue
Expand All @@ -303,11 +303,11 @@ func (n asyncPseudonode) makeProposals(round basics.Round, period period, accoun

// makeVotes creates a slice of votes for a given proposal value in a given
// round, period, and step.
func (n asyncPseudonode) makeVotes(round basics.Round, period period, step step, proposal proposalValue, participation []account.Participation) []unauthenticatedVote {
func (n asyncPseudonode) makeVotes(round basics.Round, period period, step step, proposal proposalValue, participation []account.ParticipationRecordForRound) []unauthenticatedVote {
votes := make([]unauthenticatedVote, 0)
for _, account := range participation {
rv := rawVote{Sender: account.Address(), Round: round, Period: period, Step: step, Proposal: proposal}
uv, err := makeVote(rv, account.VotingSigner(), account.VRFSecrets(), n.ledger)
for _, part := range participation {
rv := rawVote{Sender: part.Account, Round: round, Period: period, Step: step, Proposal: proposal}
uv, err := makeVote(rv, part.VotingSigner(), part.VRF, n.ledger)
if err != nil {
n.log.Warnf("pseudonode.makeVotes: could not create vote: %v", err)
continue
Expand Down
6 changes: 3 additions & 3 deletions agreement/pseudonode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,10 @@ func (n serializedPseudonode) Quit() {
}

type KeyManagerProxy struct {
target func(basics.Round, basics.Round) []account.Participation
target func(basics.Round, basics.Round) []account.ParticipationRecordForRound
}

func (k *KeyManagerProxy) VotingKeys(votingRound, balanceRound basics.Round) []account.Participation {
func (k *KeyManagerProxy) VotingKeys(votingRound, balanceRound basics.Round) []account.ParticipationRecordForRound {
return k.target(votingRound, balanceRound)
}

Expand Down Expand Up @@ -447,7 +447,7 @@ func TestPseudonodeLoadingOfParticipationKeys(t *testing.T) {
pb.keys = keyManagerProxy
cparams, _ := ledger.ConsensusParams(0)
for rnd := basics.Round(3); rnd < 1000; rnd += 43 {
keyManagerProxy.target = func(votingRound, balanceRnd basics.Round) []account.Participation {
keyManagerProxy.target = func(votingRound, balanceRnd basics.Round) []account.ParticipationRecordForRound {
require.Equal(t, rnd, votingRound)
require.Equal(t, balanceRound(rnd, cparams), balanceRnd)
return keyManager.VotingKeys(votingRound, balanceRnd)
Expand Down
2 changes: 1 addition & 1 deletion compactcert/abstractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ type Network interface {
// Accounts captures the aspects of the AccountManager that are used by
// this package.
type Accounts interface {
Keys(basics.Round) []account.Participation
Keys(basics.Round) []account.ParticipationRecordForRound
}
6 changes: 3 additions & 3 deletions compactcert/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ func (ccw *Worker) signBlock(hdr bookkeeping.BlockHeader) {
continue
}

sig, err := key.StateProofSecrets.Sign(hdr, uint64(hdr.Round))
sig, err := key.StateProofSecrets.Sign(hdr)
if err != nil {
ccw.log.Warnf("ccw.signBlock(%d): StateProof.Sign: %v", hdr.Round, err)
ccw.log.Warnf("ccw.signBlock(%d): StateProofSecrets.Sign: %v", hdr.Round, err)
continue
}

sigs = append(sigs, sigFromAddr{
Signer: key.Parent,
Signer: key.Account,
Round: hdr.Round,
Sig: sig,
})
Expand Down
23 changes: 21 additions & 2 deletions compactcert/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,29 @@ func (s *testWorkerStubs) addBlock(ccNextRound basics.Round) {
}
}

func (s *testWorkerStubs) Keys(rnd basics.Round) (out []account.Participation) {
func (s *testWorkerStubs) Keys(rnd basics.Round) (out []account.ParticipationRecordForRound) {
for _, part := range s.keys {
if part.OverlapsInterval(rnd, rnd) {
out = append(out, part)
partRecord := account.ParticipationRecord{
ParticipationID: part.ID(),
Account: part.Parent,
FirstValid: part.FirstValid,
LastValid: part.LastValid,
KeyDilution: part.KeyDilution,
LastVote: 0,
LastBlockProposal: 0,
LastStateProof: 0,
EffectiveFirst: 0,
EffectiveLast: 0,
VRF: part.VRF,
Voting: part.Voting,
}
signerInRound := part.StateProofSecrets.GetSigner(uint64(rnd))
partRecordForRound := account.ParticipationRecordForRound{
ParticipationRecord: partRecord,
StateProofSecrets: signerInRound,
}
out = append(out, partRecordForRound)
}
}
return
Expand Down
Loading

0 comments on commit 2f2dd3d

Please sign in to comment.