From 32f80606d82d26239cbf553b1a7bd3aade192766 Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Fri, 11 Dec 2020 16:29:40 +0300 Subject: [PATCH 1/4] fix: always return ordered participants --- .../dkg_proposal_fsm/actions.go | 16 ++++---- fsm/state_machines/internal/types.go | 41 ++++++++++++++----- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index 2985d691..ff68c4b3 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -56,9 +56,9 @@ func (m *DKGProposalFSM) actionInitDKGProposal(inEvent fsm.Event, args ...interf responseData := make(responses.DKGProposalPubKeysParticipantResponse, 0) - for participantId, participant := range m.payload.DKGProposalPayload.Quorum { + for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { responseEntry := &responses.DKGProposalPubKeysParticipantEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, DkgPubKey: participant.DkgPubKey, } @@ -155,9 +155,9 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event responseData := make(responses.DKGProposalCommitParticipantResponse, 0) - for participantId, participant := range m.payload.DKGProposalPayload.Quorum { + for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { responseEntry := &responses.DKGProposalCommitParticipantEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, DkgCommit: participant.DkgCommit, } @@ -257,12 +257,12 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event, responseData := make(responses.DKGProposalDealParticipantResponse, 0) - for participantId, participant := range m.payload.DKGProposalPayload.Quorum { + for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { if len(participant.DkgDeal) == 0 { continue } responseEntry := &responses.DKGProposalDealParticipantEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, DkgDeal: participant.DkgDeal, } @@ -362,9 +362,9 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Eve responseData := make(responses.DKGProposalResponseParticipantResponse, 0) - for participantId, participant := range m.payload.DKGProposalPayload.Quorum { + for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { responseEntry := &responses.DKGProposalResponseParticipantEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, DkgResponse: participant.DkgResponse, } diff --git a/fsm/state_machines/internal/types.go b/fsm/state_machines/internal/types.go index 790b9670..696a49f2 100644 --- a/fsm/state_machines/internal/types.go +++ b/fsm/state_machines/internal/types.go @@ -2,8 +2,10 @@ package internal import ( "crypto/ed25519" - "github.com/lidofinance/dc4bc/fsm/types/requests" + "sort" "time" + + "github.com/lidofinance/dc4bc/fsm/types/requests" ) type ParticipantStatus interface { @@ -88,15 +90,16 @@ const ( ) type DKGProposalParticipant struct { - Username string - DkgPubKey []byte - DkgCommit []byte - DkgDeal []byte - DkgResponse []byte - DkgMasterKey []byte - Status DKGParticipantStatus - Error *requests.FSMError - UpdatedAt time.Time + ParticipantID int + Username string + DkgPubKey []byte + DkgCommit []byte + DkgDeal []byte + DkgResponse []byte + DkgMasterKey []byte + Status DKGParticipantStatus + Error *requests.FSMError + UpdatedAt time.Time } func (dkgP DKGProposalParticipant) GetStatus() ParticipantStatus { @@ -109,6 +112,24 @@ func (dkgP DKGProposalParticipant) GetUsername() string { type DKGProposalQuorum map[int]*DKGProposalParticipant +func (q DKGProposalQuorum) GetOrderedParticipants() []*DKGProposalParticipant { + var sortedParticipantIDs []int + for participantID := range q { + sortedParticipantIDs = append(sortedParticipantIDs, participantID) + } + + sort.Ints(sortedParticipantIDs) + + var out []*DKGProposalParticipant + for _, participantID := range sortedParticipantIDs { + var participant = q[participantID] + participant.ParticipantID = participantID + out = append(out, participant) + } + + return out +} + type DKGConfirmation struct { Quorum DKGProposalQuorum CreatedAt time.Time From f61254c3c8a09af80403e457bec156dd4e596435 Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Sat, 12 Dec 2020 20:34:47 +0300 Subject: [PATCH 2/4] fix: make the initial step dump deterministic --- fsm/state_machines/dkg_proposal_fsm/actions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index ff68c4b3..2dc6bee7 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -127,7 +127,7 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event } unconfirmedParticipants := m.payload.DKGQuorumCount() - for _, participant := range m.payload.DKGProposalPayload.Quorum { + for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { if participant.Status == internal.CommitConfirmationError { isContainsError = true } else if participant.Status == internal.CommitConfirmed { From 7b408963505cbf6c135f0f17f6859162c97b1b05 Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Sat, 12 Dec 2020 21:54:43 +0300 Subject: [PATCH 3/4] fully deterministic dumps --- fsm/state_machines/internal/types.go | 54 ++++++++++++++++--- .../signature_proposal_fsm/actions.go | 4 +- .../signing_proposal_fsm/actions.go | 12 ++--- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/fsm/state_machines/internal/types.go b/fsm/state_machines/internal/types.go index 696a49f2..e02063c9 100644 --- a/fsm/state_machines/internal/types.go +++ b/fsm/state_machines/internal/types.go @@ -44,9 +44,10 @@ type SignatureConfirmation struct { } type SignatureProposalParticipant struct { - Username string - PubKey ed25519.PublicKey - DkgPubKey []byte + ParticipantID int + Username string + PubKey ed25519.PublicKey + DkgPubKey []byte // For validation user confirmation: sign(InvitationSecret, PubKey) => user InvitationSecret string Status ConfirmationParticipantStatus @@ -70,6 +71,24 @@ func (c *SignatureConfirmation) IsExpired() bool { // Excludes array merge and rotate operations type SignatureProposalQuorum map[int]*SignatureProposalParticipant +func (q SignatureProposalQuorum) GetOrderedParticipants() []*SignatureProposalParticipant { + var sortedParticipantIDs []int + for participantID := range q { + sortedParticipantIDs = append(sortedParticipantIDs, participantID) + } + + sort.Ints(sortedParticipantIDs) + + var out []*SignatureProposalParticipant + for _, participantID := range sortedParticipantIDs { + var participant = q[participantID] + participant.ParticipantID = participantID + out = append(out, participant) + } + + return out +} + // DKG proposal type DKGParticipantStatus uint8 @@ -194,6 +213,24 @@ func (c *SigningConfirmation) IsExpired() bool { type SigningProposalQuorum map[int]*SigningProposalParticipant +func (q SigningProposalQuorum) GetOrderedParticipants() []*SigningProposalParticipant { + var sortedParticipantIDs []int + for participantID := range q { + sortedParticipantIDs = append(sortedParticipantIDs, participantID) + } + + sort.Ints(sortedParticipantIDs) + + var out []*SigningProposalParticipant + for _, participantID := range sortedParticipantIDs { + var participant = q[participantID] + participant.ParticipantID = participantID + out = append(out, participant) + } + + return out +} + type SigningParticipantStatus uint8 const ( @@ -226,11 +263,12 @@ func (s SigningParticipantStatus) String() string { } type SigningProposalParticipant struct { - Username string - Status SigningParticipantStatus - PartialSign []byte - Error *requests.FSMError - UpdatedAt time.Time + ParticipantID int + Username string + Status SigningParticipantStatus + PartialSign []byte + Error *requests.FSMError + UpdatedAt time.Time } func (signingP SigningProposalParticipant) GetStatus() ParticipantStatus { diff --git a/fsm/state_machines/signature_proposal_fsm/actions.go b/fsm/state_machines/signature_proposal_fsm/actions.go index 64aae33c..d31214b4 100644 --- a/fsm/state_machines/signature_proposal_fsm/actions.go +++ b/fsm/state_machines/signature_proposal_fsm/actions.go @@ -64,9 +64,9 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0) - for participantId, participant := range m.payload.SignatureProposalPayload.Quorum { + for _, participant := range m.payload.SignatureProposalPayload.Quorum.GetOrderedParticipants() { responseEntry := &responses.SignatureProposalParticipantInvitationEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, Threshold: participant.Threshold, DkgPubKey: participant.DkgPubKey, diff --git a/fsm/state_machines/signing_proposal_fsm/actions.go b/fsm/state_machines/signing_proposal_fsm/actions.go index 83929587..391cb0d8 100644 --- a/fsm/state_machines/signing_proposal_fsm/actions.go +++ b/fsm/state_machines/signing_proposal_fsm/actions.go @@ -68,8 +68,8 @@ func (m *SigningProposalFSM) actionStartSigningProposal(inEvent fsm.Event, args m.payload.SigningProposalPayload.Quorum = make(internal.SigningProposalQuorum) // Initialize new quorum - for id, dkgEntry := range m.payload.DKGProposalPayload.Quorum { - m.payload.SigningProposalPayload.Quorum[id] = &internal.SigningProposalParticipant{ + for _, dkgEntry := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { + m.payload.SigningProposalPayload.Quorum[dkgEntry.ParticipantID] = &internal.SigningProposalParticipant{ Username: dkgEntry.Username, Status: internal.SigningAwaitConfirmation, UpdatedAt: request.CreatedAt, @@ -87,9 +87,9 @@ func (m *SigningProposalFSM) actionStartSigningProposal(inEvent fsm.Event, args Participants: make([]*responses.SigningProposalParticipantInvitationEntry, 0), } - for participantId, participant := range m.payload.SigningProposalPayload.Quorum { + for _, participant := range m.payload.SigningProposalPayload.Quorum.GetOrderedParticipants() { responseEntry := &responses.SigningProposalParticipantInvitationEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, Status: uint8(participant.Status), } @@ -282,13 +282,13 @@ func (m *SigningProposalFSM) actionValidateSigningPartialSignsAwaitConfirmations Participants: make([]*responses.SigningProcessParticipantEntry, 0), } - for participantId, participant := range m.payload.SigningProposalPayload.Quorum { + for _, participant := range m.payload.SigningProposalPayload.Quorum.GetOrderedParticipants() { // don't return participants who didn't broadcast partial signature if len(participant.PartialSign) == 0 { continue } responseEntry := &responses.SigningProcessParticipantEntry{ - ParticipantId: participantId, + ParticipantId: participant.ParticipantID, Username: participant.Username, PartialSign: participant.PartialSign, } From 60ee6f11d9ca71f5b4280960271890fd1f8eac0a Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Sun, 13 Dec 2020 12:30:12 +0300 Subject: [PATCH 4/4] removed redundant ordering --- fsm/state_machines/dkg_proposal_fsm/actions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index 2dc6bee7..ff68c4b3 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -127,7 +127,7 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event } unconfirmedParticipants := m.payload.DKGQuorumCount() - for _, participant := range m.payload.DKGProposalPayload.Quorum.GetOrderedParticipants() { + for _, participant := range m.payload.DKGProposalPayload.Quorum { if participant.Status == internal.CommitConfirmationError { isContainsError = true } else if participant.Status == internal.CommitConfirmed {