diff --git a/fsm/cmd/test/test.go b/fsm/cmd/test/test.go deleted file mode 100644 index 2647b35..0000000 --- a/fsm/cmd/test/test.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "github.com/depools/dc4bc/fsm/fsm" - "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm" - "github.com/depools/dc4bc/fsm/types/responses" - "log" - "time" - - "github.com/depools/dc4bc/fsm/state_machines" - "github.com/depools/dc4bc/fsm/types/requests" -) - -func main() { - tm := time.Now() - fsmMachine, err := state_machines.Create("d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257") - log.Println(fsmMachine, err) - resp, dump, err := fsmMachine.Do( - signature_proposal_fsm.EventInitProposal, - requests.SignatureProposalParticipantsListRequest{ - Participants: []*requests.SignatureProposalParticipantsEntry{ - { - "John Doe", - []byte("pubkey123123"), - }, - { - "Crypto Billy", - []byte("pubkey456456"), - }, - { - "Matt", - []byte("pubkey789789"), - }, - }, - CreatedAt: &tm, - }, - ) - if err != nil { - log.Println("Err", err) - return - } - - log.Println("Dump", string(dump)) - - processResponse(resp) - -} - -func processResponse(resp *fsm.Response) { - switch resp.State { - // Await proposals - case fsm.State("state_validation_await_participants_confirmations"): - data, ok := resp.Data.(responses.SignatureProposalParticipantInvitationsResponse) - if !ok { - log.Printf("undefined response type for state \"%s\"\n", resp.State) - return - } - sendInvitations(data) - - case fsm.State("validation_canceled_by_participant"): - updateDashboardWithCanceled("Participant") - case fsm.State("validation_canceled_by_timeout"): - updateDashboardWithCanceled("Timeout") - default: - log.Printf("undefined response type for state \"%s\"\n", resp.State) - } -} - -func sendInvitations(invitations responses.SignatureProposalParticipantInvitationsResponse) { - for _, invitation := range invitations { - log.Printf( - "Dear %s, please encrypt value \"%s\" with your key, fingerprint: %s\n", - invitation.Title, - invitation.EncryptedInvitation, - invitation.PubKeyFingerprint, - ) - } -} - -func updateDashboardWithCanceled(msg string) { - log.Printf("Breaking news! Proposal canceled with reason: %s\n", msg) -} diff --git a/fsm/config/config.go b/fsm/config/config.go index 6234e6a..bb77b16 100644 --- a/fsm/config/config.go +++ b/fsm/config/config.go @@ -1,6 +1,10 @@ package config +import "time" + const ( // TODO: Move to machine level configs? - ParticipantsMinCount = 3 + ParticipantsMinCount = 3 + SignatureProposalConfirmationDeadline = time.Hour * 24 + DkgConfirmationDeadline = time.Hour * 24 ) diff --git a/fsm/fsm/fsm.go b/fsm/fsm/fsm.go index d6907ee..2af2abd 100644 --- a/fsm/fsm/fsm.go +++ b/fsm/fsm/fsm.go @@ -21,7 +21,9 @@ import ( const ( StateGlobalIdle = State("__idle") StateGlobalDone = State("__done") +) +const ( EventRunDefault EventRunMode = iota EventRunBefore EventRunAfter @@ -189,6 +191,10 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call panic("duplicate dst for pair `source + event`") } + if event.IsAuto && event.AutoRunMode == EventRunDefault { + event.AutoRunMode = EventRunAfter + } + trEvent := &trEvent{ tKey.event, event.DstState, @@ -197,10 +203,6 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call event.AutoRunMode, } - if trEvent.isAuto && trEvent.runMode == EventRunDefault { - trEvent.runMode = EventRunAfter - } - f.transitions[tKey] = trEvent // For using provider, event must use with IsGlobal = true @@ -245,7 +247,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call } if _, ok := allEvents[event]; !ok { - panic("callback has no event") + panic("callback has empty event") } f.callbacks[event] = callback @@ -380,7 +382,7 @@ func (f *FSM) SetState(event Event) error { trEvent, ok := f.transitions[trKey{f.currentState, event}] if !ok { - return errors.New("cannot change state") + return errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState)) } f.currentState = trEvent.dstState diff --git a/fsm/fsm/fsm_machines_test.go b/fsm/fsm/fsm_machines_test.go index b068dd2..55ebc48 100644 --- a/fsm/fsm/fsm_machines_test.go +++ b/fsm/fsm/fsm_machines_test.go @@ -12,9 +12,9 @@ const ( stateStage1 = State("state_stage1") // Process data stateStage2 = State("state_stage2") - // Cancelled with internal event + // Canceled with internal event stateCanceledByInternal = State("state_canceled") - // Cancelled with external event + // Canceled with external event stateCanceled2 = State("state_canceled2") // Out endpoint to switch stateOutToFSM2 = State("state_out_to_fsm2") diff --git a/fsm/fsm_pool/fsm_pool_test.go b/fsm/fsm_pool/fsm_pool_test.go index 586bafc..a3d7f73 100644 --- a/fsm/fsm_pool/fsm_pool_test.go +++ b/fsm/fsm_pool/fsm_pool_test.go @@ -23,9 +23,9 @@ const ( fsm1StateStage1 = fsm.State("state_fsm1_stage1") // Process data fsm1StateStage2 = fsm.State("state_fsm1_stage2") - // Cancelled with internal event + // Canceled with internal event fsm1StateCanceledByInternal = fsm.State("state_fsm1_canceled") - // Cancelled with external event + // Canceled with external event fsm1StateCanceled2 = fsm.State("state_fsm1_canceled2") // Out endpoint to switch fsm1StateOutToFSM2 = fsm.State("state_fsm1_out_to_fsm2") @@ -108,7 +108,7 @@ const ( // Process data fsm2StateStage1 = fsm.State("state_fsm2_stage1") fsm2StateStage2 = fsm.State("state_fsm2_stage2") - // Cancelled with internal event + // Canceled with internal event fsm2StateCanceledByInternal = fsm.State("state_fsm2_canceled") // Out endpoint to switch fsm2StateOutToFSM3 = fsm.State("state_fsm2_out_to_fsm3") diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index 8c015ee..3fc1913 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -3,17 +3,16 @@ package dkg_proposal_fsm import ( "errors" "fmt" + "github.com/depools/dc4bc/fsm/config" "github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/types/requests" + "reflect" + "time" ) // Pub keys -func (m *DKGProposalFSM) actionPubKeyPrepareConfirmations(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { - return -} - func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { m.payloadMu.Lock() defer m.payloadMu.Unlock() @@ -34,18 +33,72 @@ func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, arg return } - dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId] - - if !ok { + if !m.payload.DKGQuorumExists(request.ParticipantId) { err = errors.New("{ParticipantId} not exist in quorum") return } - copy(dkgProposalParticipant.PublicKey, request.PubKey) - dkgProposalParticipant.UpdatedAt = request.CreatedAt + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + + if dkgProposalParticipant.Status != internal.PubKeyAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot confirm pubkey with {Status} = {\"%s\"}", dkgProposalParticipant.Status)) + return + } + + copy(dkgProposalParticipant.PubKey, request.PubKey) + dkgProposalParticipant.UpdatedAt = &request.CreatedAt dkgProposalParticipant.Status = internal.PubKeyConfirmed - m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) + + return +} + +func (m *DKGProposalFSM) actionValidateDkgProposalPubKeys(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsError, isContainsExpired bool + ) + + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.DKGQuorumCount() + for _, participant := range m.payload.DKGProposalPayload.Quorum { + if participant.Status == internal.PubKeyAwaitConfirmation { + if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.PubKeyConfirmationError { + isContainsError = true + } else if participant.Status == internal.PubKeyConfirmed { + unconfirmedParticipants-- + } + } + } + + if isContainsError { + outEvent = eventDKGSetPubKeysConfirmationCanceledByErrorInternal + return + } + + if isContainsExpired { + outEvent = eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal + return + } + + // The are no declined and timed out participants, check for all confirmations + if unconfirmedParticipants > 0 { + return + } + + outEvent = eventDKGSetPubKeysConfirmedInternal + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.CommitAwaitConfirmation + } return } @@ -72,18 +125,72 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg return } - dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId] - - if !ok { + if !m.payload.DKGQuorumExists(request.ParticipantId) { err = errors.New("{ParticipantId} not exist in quorum") return } + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + + if dkgProposalParticipant.Status != internal.CommitAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", dkgProposalParticipant.Status)) + return + } + copy(dkgProposalParticipant.Commit, request.Commit) - dkgProposalParticipant.UpdatedAt = request.CreatedAt + dkgProposalParticipant.UpdatedAt = &request.CreatedAt dkgProposalParticipant.Status = internal.CommitConfirmed - m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) + + return +} + +func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsError, isContainsExpired bool + ) + + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.DKGQuorumCount() + for _, participant := range m.payload.DKGProposalPayload.Quorum { + if participant.Status == internal.CommitAwaitConfirmation { + if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.CommitConfirmationError { + isContainsError = true + } else if participant.Status == internal.CommitConfirmed { + unconfirmedParticipants-- + } + } + } + + if isContainsError { + outEvent = eventDKGCommitsConfirmationCancelByTimeoutInternal + return + } + + if isContainsExpired { + outEvent = eventDKGCommitsConfirmationCancelByErrorInternal + return + } + + // The are no declined and timed out participants, check for all confirmations + if unconfirmedParticipants > 0 { + return + } + + outEvent = eventDKGCommitsConfirmedInternal + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.DealAwaitConfirmation + } return } @@ -110,18 +217,72 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args return } - dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId] - - if !ok { + if !m.payload.DKGQuorumExists(request.ParticipantId) { err = errors.New("{ParticipantId} not exist in quorum") return } + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + + if dkgProposalParticipant.Status != internal.DealAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot confirm deal with {Status} = {\"%s\"}", dkgProposalParticipant.Status)) + return + } + copy(dkgProposalParticipant.Deal, request.Deal) - dkgProposalParticipant.UpdatedAt = request.CreatedAt + dkgProposalParticipant.UpdatedAt = &request.CreatedAt dkgProposalParticipant.Status = internal.DealConfirmed - m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) + + return +} + +func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsError, isContainsExpired bool + ) + + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.DKGQuorumCount() + for _, participant := range m.payload.DKGProposalPayload.Quorum { + if participant.Status == internal.DealAwaitConfirmation { + if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.DealConfirmationError { + isContainsError = true + } else if participant.Status == internal.DealConfirmed { + unconfirmedParticipants-- + } + } + } + + if isContainsError { + outEvent = eventDKGDealsConfirmationCancelByErrorInternal + return + } + + if isContainsExpired { + outEvent = eventDKGDealsConfirmationCancelByTimeoutInternal + return + } + + // The are no declined and timed out participants, check for all confirmations + if unconfirmedParticipants > 0 { + return + } + + outEvent = eventDKGDealsConfirmedInternal + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.ResponseAwaitConfirmation + } return } @@ -148,18 +309,188 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a return } - dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId] - - if !ok { + if !m.payload.DKGQuorumExists(request.ParticipantId) { err = errors.New("{ParticipantId} not exist in quorum") return } + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + + if dkgProposalParticipant.Status != internal.ResponseAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status)) + return + } + copy(dkgProposalParticipant.Response, request.Response) - dkgProposalParticipant.UpdatedAt = request.CreatedAt + dkgProposalParticipant.UpdatedAt = &request.CreatedAt dkgProposalParticipant.Status = internal.ResponseConfirmed - m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) + + return +} + +func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsError, isContainsExpired bool + ) + + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.DKGQuorumCount() + for _, participant := range m.payload.DKGProposalPayload.Quorum { + if participant.Status == internal.ResponseAwaitConfirmation { + if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.ResponseConfirmationError { + isContainsError = true + } else if participant.Status == internal.ResponseConfirmed { + unconfirmedParticipants-- + } + } + } + + if isContainsError { + outEvent = eventDKGResponseConfirmationCancelByErrorInternal + return + } + + if isContainsExpired { + outEvent = eventDKGResponseConfirmationCancelByTimeoutInternal + return + } + + // The are no declined and timed out participants, check for all confirmations + if unconfirmedParticipants > 0 { + return + } + + outEvent = eventDKGResponsesConfirmedInternal + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.MasterKeyAwaitConfirmation + } + + return +} + +// Master key + +func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + if len(args) != 1 { + err = errors.New("{arg0} required {DKGProposalMasterKeyConfirmationRequest}") + return + } + + request, ok := args[0].(requests.DKGProposalMasterKeyConfirmationRequest) + + if !ok { + err = errors.New("cannot cast {arg0} to type {DKGProposalMasterKeyConfirmationRequest}") + return + } + + if err = request.Validate(); err != nil { + return + } + + if !m.payload.DKGQuorumExists(request.ParticipantId) { + err = errors.New("{ParticipantId} not exist in quorum") + return + } + + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + + if dkgProposalParticipant.Status != internal.MasterKeyAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status)) + return + } + + copy(dkgProposalParticipant.MasterKey, request.MasterKey) + dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.Status = internal.MasterKeyConfirmed + + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) + + return +} + +func (m *DKGProposalFSM) actionValidateDkgProposalAwaitMasterKey(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsError, isContainsExpired bool + masterKeys [][]byte + ) + + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.DKGQuorumCount() + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + if participant.Status == internal.MasterKeyAwaitConfirmation { + if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.MasterKeyConfirmationError { + isContainsError = true + } else if participant.Status == internal.MasterKeyConfirmed { + masterKeys = append(masterKeys, participant.MasterKey) + unconfirmedParticipants-- + } + } + } + + if isContainsError { + outEvent = eventDKGMasterKeyConfirmationCancelByErrorInternal + return + } + + if isContainsExpired { + outEvent = eventDKGMasterKeyConfirmationCancelByTimeoutInternal + return + } + + // Temporary simplest match master keys + if len(masterKeys) > 1 { + for i, masterKey := range masterKeys { + for j := range masterKeys { + if i == j { + continue + } + + if !reflect.DeepEqual(masterKey, masterKeys[i]) { + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.MasterKeyConfirmationError + participant.Error = errors.New("master key is mismatched") + } + + outEvent = eventDKGMasterKeyConfirmationCancelByErrorInternal + return + } + } + } + } + + // The are no declined and timed out participants, check for all confirmations + if unconfirmedParticipants > 0 { + return + } + + outEvent = eventDKGMasterKeyConfirmedInternal + + for _, participant := range m.payload.DKGProposalPayload.Quorum { + participant.Status = internal.MasterKeyConfirmed + } return } @@ -185,18 +516,18 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte return } - dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId] - - if !ok { + if !m.payload.DKGQuorumExists(request.ParticipantId) { err = errors.New("{ParticipantId} not exist in quorum") return } + dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId) + // TODO: Move to methods switch inEvent { case EventDKGPubKeyConfirmationError: switch dkgProposalParticipant.Status { - case internal.PubKeyConAwaitConfirmation: + case internal.PubKeyAwaitConfirmation: dkgProposalParticipant.Status = internal.PubKeyConfirmationError case internal.PubKeyConfirmed: err = errors.New("{Status} already confirmed") @@ -221,13 +552,13 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte err = errors.New(fmt.Sprintf( "{Status} now is \"%s\" and cannot set to {\"%s\"}", dkgProposalParticipant.Status, - internal.PubKeyConfirmationError, + internal.CommitConfirmationError, )) } case EventDKGDealConfirmationError: switch dkgProposalParticipant.Status { case internal.DealAwaitConfirmation: - dkgProposalParticipant.Status = internal.PubKeyConfirmationError + dkgProposalParticipant.Status = internal.DealConfirmationError case internal.DealConfirmed: err = errors.New("{Status} already confirmed") case internal.DealConfirmationError: @@ -236,13 +567,13 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte err = errors.New(fmt.Sprintf( "{Status} now is \"%s\" and cannot set to {\"%s\"}", dkgProposalParticipant.Status, - internal.PubKeyConfirmationError, + internal.DealConfirmationError, )) } case EventDKGResponseConfirmationError: switch dkgProposalParticipant.Status { case internal.ResponseAwaitConfirmation: - dkgProposalParticipant.Status = internal.PubKeyConfirmationError + dkgProposalParticipant.Status = internal.ResponseConfirmationError case internal.ResponseConfirmed: err = errors.New("{Status} already confirmed") case internal.ResponseConfirmationError: @@ -251,7 +582,22 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte err = errors.New(fmt.Sprintf( "{Status} now is \"%s\" and cannot set to {\"%s\"}", dkgProposalParticipant.Status, - internal.PubKeyConfirmationError, + internal.ResponseConfirmationError, + )) + } + case EventDKGMasterKeyConfirmationError: + switch dkgProposalParticipant.Status { + case internal.MasterKeyAwaitConfirmation: + dkgProposalParticipant.Status = internal.MasterKeyConfirmationError + case internal.MasterKeyConfirmed: + err = errors.New("{Status} already confirmed") + case internal.MasterKeyConfirmationError: + err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.MasterKeyConfirmationError)) + default: + err = errors.New(fmt.Sprintf( + "{Status} now is \"%s\" and cannot set to {\"%s\"}", + dkgProposalParticipant.Status, + internal.MasterKeyConfirmationError, )) } default: @@ -262,9 +608,10 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte return } - dkgProposalParticipant.UpdatedAt = request.CreatedAt + dkgProposalParticipant.Error = request.Error + dkgProposalParticipant.UpdatedAt = &request.CreatedAt - m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant + m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) // TODO: Add outEvent diff --git a/fsm/state_machines/dkg_proposal_fsm/init.go b/fsm/state_machines/dkg_proposal_fsm/init.go index 97e47a5..7fa89ed 100644 --- a/fsm/state_machines/dkg_proposal_fsm/init.go +++ b/fsm/state_machines/dkg_proposal_fsm/init.go @@ -12,61 +12,79 @@ const ( StateDkgInitial = StateDkgPubKeysAwaitConfirmations StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations") - // Cancelled - StateDkgPubKeysAwaitCancelled = fsm.State("state_dkg_pub_keys_await_cancelled") - StateDkgPubKeysAwaitCancelledByTimeout = fsm.State("state_dkg_pub_keys_await_cancelled_by_timeout") + // Canceled + StateDkgPubKeysAwaitCanceled = fsm.State("state_dkg_pub_keys_await_canceled") + StateDkgPubKeysAwaitCanceledByTimeout = fsm.State("state_dkg_pub_keys_await_canceled_by_timeout") // Confirmed - StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed") + // StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed") // Sending dkg commits - StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations") - // Cancelled - StateDkgCommitsAwaitCancelled = fsm.State("state_dkg_commits_await_cancelled") - StateDkgCommitsAwaitCancelledByTimeout = fsm.State("state_dkg_commits_await_cancelled_by_timeout") + StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations") + // Canceled + StateDkgCommitsAwaitCanceled = fsm.State("state_dkg_commits_await_canceled") + StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout") // Confirmed StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed") // Sending dkg deals StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations") - // Cancelled - StateDkgDealsAwaitCancelled = fsm.State("state_dkg_deals_await_cancelled") - StateDkgDealsAwaitCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout") + // Canceled + StateDkgDealsAwaitCanceled = fsm.State("state_dkg_deals_await_canceled") + StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_await_canceled_by_timeout") // Confirmed - StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_confirmed") + //StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_confirmed") StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations") - // Cancelled - StateDkgResponsesAwaitCancelled = fsm.State("state_dkg_responses_await_cancelled") - StateDkgResponsesAwaitCancelledByTimeout = fsm.State("state_dkg_responses_sending_cancelled_by_timeout") + // Canceled + StateDkgResponsesAwaitCanceled = fsm.State("state_dkg_responses_await_canceled") + StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout") // Confirmed StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed") + StateDkgMasterKeyAwaitConfirmations = fsm.State("state_dkg_master_key_await_confirmations") + StateDkgMasterKeyAwaitCanceled = fsm.State("state_dkg_master_key_await_canceled") + StateDkgMasterKeyAwaitCanceledByTimeout = fsm.State("state_dkg_master_key_await_canceled_by_timeout") + // Events - eventDKGPubKeysSendingRequiredAuto = fsm.Event("event_dkg_pub_key_sending_required_internal") - EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received") - EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error") - EventDKGPubKeysConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal") - EventDKGPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal") + eventAutoDKGInitialInternal = fsm.Event("event_dkg_init_internal") - EventDKGCommitsSendingRequiredInternal = fsm.Event("event_dkg_commits_sending_required_internal") + EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received") + EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error") + + eventAutoDKGValidatePubKeysConfirmationInternal = fsm.Event("event_dkg_pub_keys_validate_internal") + + eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal") + eventDKGSetPubKeysConfirmationCanceledByErrorInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_error_internal") + eventDKGSetPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal") EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received") EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error") - EventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal") - EventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal") - - EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal") + eventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal") + eventDKGCommitsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_error_internal") + eventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal") + eventAutoDKGValidateConfirmationCommitsInternal = fsm.Event("event_dkg_commits_validate_internal") EventDKGDealConfirmationReceived = fsm.Event("event_dkg_deal_confirm_received") EventDKGDealConfirmationError = fsm.Event("event_dkg_deal_confirm_canceled_by_error") - EventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal") - - EventDKGResponsesSendingRequiredInternal = fsm.Event("event_dkg_responses_sending_required_internal") + eventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal") + eventDKGDealsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_error_internal") + eventDKGDealsConfirmedInternal = fsm.Event("event_dkg_deals_confirmed_internal") + eventAutoDKGValidateConfirmationDealsInternal = fsm.Event("event_dkg_deals_validate_internal") EventDKGResponseConfirmationReceived = fsm.Event("event_dkg_response_confirm_received") EventDKGResponseConfirmationError = fsm.Event("event_dkg_response_confirm_canceled_by_error") - EventDKGResponseConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_response_confirm_canceled_by_timeout_internal") + eventDKGResponseConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_response_confirm_canceled_by_timeout_internal") + eventDKGResponseConfirmationCancelByErrorInternal = fsm.Event("event_dkg_response_confirm_canceled_by_error_internal") + eventDKGResponsesConfirmedInternal = fsm.Event("event_dkg_responses_confirmed_internal") + eventAutoDKGValidateResponsesConfirmationInternal = fsm.Event("event_dkg_responses_validate_internal") + + EventDKGMasterKeyConfirmationReceived = fsm.Event("event_dkg_master_key_confirm_received") + EventDKGMasterKeyConfirmationError = fsm.Event("event_dkg_master_key_confirm_canceled_by_error") + eventDKGMasterKeyConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_master_key_confirm_canceled_by_timeout_internal") + eventDKGMasterKeyConfirmationCancelByErrorInternal = fsm.Event("event_dkg_master_key_confirm_canceled_by_error_internal") + eventDKGMasterKeyConfirmedInternal = fsm.Event("event_dkg_master_key_confirmed_internal") + eventAutoDKGValidateMasterKeyConfirmationInternal = fsm.Event("event_dkg_master_key_validate_internal") EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal") ) @@ -89,58 +107,85 @@ func New() internal.DumpedMachineProvider { // Switch to pub keys required // {Name: eventDKGPubKeysSendingRequiredAuto, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunAfter}, + // {Name: eventAutoDKGInitialInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore}, + // Pub keys sending {Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations}, - // Cancelled - {Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelled}, - {Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelledByTimeout, IsInternal: true}, + // Canceled + {Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceled}, + + {Name: eventAutoDKGValidatePubKeysConfirmationInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true}, + + {Name: eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceledByTimeout, IsInternal: true}, // Confirmed - {Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmed, IsInternal: true}, + {Name: eventDKGSetPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true}, // Switch to commits required - {Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true}, // Commits {Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations}, - // Cancelled - {Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelled}, - {Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelledByTimeout, IsInternal: true}, - // Confirmed - {Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmed, IsInternal: true}, + // Canceled + {Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceled}, + {Name: eventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByTimeout, IsInternal: true}, - // Switch to deals required - {Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true}, + {Name: eventAutoDKGValidateConfirmationCommitsInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true}, + + // Confirmed + {Name: eventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true}, // Deals {Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations}, - // Cancelled - {Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelled}, - {Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelledByTimeout, IsInternal: true}, + // Canceled + {Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceled}, + {Name: eventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true}, + {Name: eventAutoDKGValidateConfirmationDealsInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true, IsAuto: true}, - // Switch to responses required - {Name: EventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true}, + {Name: eventDKGDealsConfirmedInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true}, - // Deals + // Responses {Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations}, - // Cancelled - {Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelled}, - {Name: EventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelledByTimeout, IsInternal: true}, + // Canceled + {Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceled}, + {Name: eventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByTimeout, IsInternal: true}, + + {Name: eventAutoDKGValidateResponsesConfirmationInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true, IsAuto: true}, + + {Name: eventDKGResponsesConfirmedInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true}, + + // Master key + + {Name: EventDKGMasterKeyConfirmationReceived, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations}, + {Name: EventDKGMasterKeyConfirmationError, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceled}, + {Name: eventDKGMasterKeyConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceledByTimeout, IsInternal: true}, + + {Name: eventAutoDKGValidateMasterKeyConfirmationInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true, IsAuto: true}, + + {Name: eventDKGMasterKeyConfirmedInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true}, // Done - {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true}, + // {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true}, }, fsm.Callbacks{ - EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived, - EventDKGPubKeyConfirmationError: machine.actionConfirmationError, - EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived, - EventDKGCommitConfirmationError: machine.actionConfirmationError, + EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived, + EventDKGPubKeyConfirmationError: machine.actionConfirmationError, + eventAutoDKGValidatePubKeysConfirmationInternal: machine.actionValidateDkgProposalPubKeys, - EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived, - EventDKGDealConfirmationError: machine.actionConfirmationError, + EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived, + EventDKGCommitConfirmationError: machine.actionConfirmationError, + eventAutoDKGValidateConfirmationCommitsInternal: machine.actionValidateDkgProposalAwaitCommits, - EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived, - EventDKGResponseConfirmationError: machine.actionConfirmationError, + EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived, + EventDKGDealConfirmationError: machine.actionConfirmationError, + eventAutoDKGValidateConfirmationDealsInternal: machine.actionValidateDkgProposalAwaitDeals, + + EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived, + EventDKGResponseConfirmationError: machine.actionConfirmationError, + eventAutoDKGValidateResponsesConfirmationInternal: machine.actionValidateDkgProposalAwaitResponses, + + EventDKGMasterKeyConfirmationReceived: machine.actionMasterKeyConfirmationReceived, + EventDKGMasterKeyConfirmationError: machine.actionConfirmationError, + eventAutoDKGValidateMasterKeyConfirmationInternal: machine.actionValidateDkgProposalAwaitMasterKey, }, ) return machine diff --git a/fsm/state_machines/internal/provider.go b/fsm/state_machines/internal/provider.go index f771aba..f78c25f 100644 --- a/fsm/state_machines/internal/provider.go +++ b/fsm/state_machines/internal/provider.go @@ -2,13 +2,79 @@ package internal import "github.com/depools/dc4bc/fsm/fsm_pool" -type DumpedMachineStatePayload struct { - TransactionId string - ConfirmationProposalPayload SignatureProposalQuorum - DKGProposalPayload DKGProposalQuorum -} - type DumpedMachineProvider interface { fsm_pool.MachineProvider SetUpPayload(payload *DumpedMachineStatePayload) } + +// DKG and other stages quorums are separated, +// because unnecessary data may be unset +type DumpedMachineStatePayload struct { + TransactionId string + SignatureProposalPayload *SignatureConfirmation + DKGProposalPayload *DKGConfirmation +} + +// Signature quorum + +func (p *DumpedMachineStatePayload) SigQuorumCount() int { + var count int + if p.SignatureProposalPayload.Quorum != nil { + count = len(p.SignatureProposalPayload.Quorum) + } + return count +} + +func (p *DumpedMachineStatePayload) SigQuorumExists(id string) bool { + var exists bool + if p.SignatureProposalPayload.Quorum != nil { + _, exists = p.SignatureProposalPayload.Quorum[id] + } + return exists +} + +func (p *DumpedMachineStatePayload) SigQuorumGet(id string) (participant *SignatureProposalParticipant) { + if p.SignatureProposalPayload.Quorum != nil { + participant, _ = p.SignatureProposalPayload.Quorum[id] + } + return +} + +func (p *DumpedMachineStatePayload) SigQuorumUpdate(id string, participant *SignatureProposalParticipant) { + if p.SignatureProposalPayload.Quorum != nil { + p.SignatureProposalPayload.Quorum[id] = participant + } + return +} + +// DKG quorum + +func (p *DumpedMachineStatePayload) DKGQuorumCount() int { + var count int + if p.DKGProposalPayload.Quorum != nil { + count = len(p.DKGProposalPayload.Quorum) + } + return count +} + +func (p *DumpedMachineStatePayload) DKGQuorumExists(id int) bool { + var exists bool + if p.DKGProposalPayload.Quorum != nil { + _, exists = p.DKGProposalPayload.Quorum[id] + } + return exists +} + +func (p *DumpedMachineStatePayload) DKGQuorumGet(id int) (participant *DKGProposalParticipant) { + if p.DKGProposalPayload.Quorum != nil { + participant, _ = p.DKGProposalPayload.Quorum[id] + } + return +} + +func (p *DumpedMachineStatePayload) DKGQuorumUpdate(id int, participant *DKGProposalParticipant) { + if p.DKGProposalPayload.Quorum != nil { + p.DKGProposalPayload.Quorum[id] = participant + } + return +} diff --git a/fsm/state_machines/internal/types.go b/fsm/state_machines/internal/types.go index 8b9834a..ed24ecb 100644 --- a/fsm/state_machines/internal/types.go +++ b/fsm/state_machines/internal/types.go @@ -5,12 +5,7 @@ import ( "time" ) -const ( - SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota - SignatureConfirmed -) - -type ConfirmationProposal struct { +type SignatureConfirmation struct { Quorum SignatureProposalQuorum CreatedAt *time.Time ExpiresAt *time.Time @@ -23,22 +18,22 @@ type SignatureProposalParticipant struct { PublicKey *rsa.PublicKey // For validation user confirmation: sign(InvitationSecret, PublicKey) => user InvitationSecret string - Status SignatureProposalParticipantStatus + Status ParticipantStatus UpdatedAt *time.Time } // Unique alias for map iteration - Public Key Fingerprint // Excludes array merge and rotate operations -type SignatureProposalQuorum map[string]SignatureProposalParticipant +type SignatureProposalQuorum map[string]*SignatureProposalParticipant -type SignatureProposalParticipantStatus uint8 +type ParticipantStatus uint8 const ( - SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota + SignatureConfirmationAwaitConfirmation ParticipantStatus = iota SignatureConfirmationConfirmed SignatureConfirmationDeclined SignatureConfirmationError - PubKeyConAwaitConfirmation + PubKeyAwaitConfirmation PubKeyConfirmed PubKeyConfirmationError CommitAwaitConfirmation @@ -50,29 +45,34 @@ const ( ResponseAwaitConfirmation ResponseConfirmed ResponseConfirmationError + MasterKeyAwaitConfirmation + MasterKeyConfirmed + MasterKeyConfirmationError ) -type DKGProposal struct { - Quorum map[int]DKGProposalParticipant +type DKGProposalParticipant struct { + Title string + PubKey []byte + Commit []byte + Deal []byte + Response []byte + MasterKey []byte + Status ParticipantStatus + Error error + UpdatedAt *time.Time +} + +type DKGProposalQuorum map[int]*DKGProposalParticipant + +type DKGConfirmation struct { + Quorum DKGProposalQuorum CreatedAt *time.Time ExpiresAt *time.Time } -type DKGProposalParticipant struct { - Title string - PublicKey []byte - Commit []byte - Deal []byte - Response []byte - Status DKGProposalParticipantStatus - UpdatedAt *time.Time -} - -type DKGProposalQuorum map[int]DKGProposalParticipant - type DKGProposalParticipantStatus uint8 -func (s DKGProposalParticipantStatus) String() string { +func (s ParticipantStatus) String() string { var str = "undefined" switch s { case SignatureConfirmationAwaitConfirmation: @@ -83,8 +83,8 @@ func (s DKGProposalParticipantStatus) String() string { str = "SignatureConfirmationDeclined" case SignatureConfirmationError: str = "SignatureConfirmationError" - case PubKeyConAwaitConfirmation: - str = "PubKeyConAwaitConfirmation" + case PubKeyAwaitConfirmation: + str = "PubKeyAwaitConfirmation" case PubKeyConfirmed: str = "PubKeyConfirmed" case PubKeyConfirmationError: diff --git a/fsm/state_machines/provider.go b/fsm/state_machines/provider.go index 93f4ce9..56f7bb4 100644 --- a/fsm/state_machines/provider.go +++ b/fsm/state_machines/provider.go @@ -66,7 +66,7 @@ func Create() (*FSMInstance, error) { return i, err } -// Get fsm from dump +// DKGQuorumGet fsm from dump func FromDump(data []byte) (*FSMInstance, error) { var err error @@ -127,9 +127,9 @@ func (i *FSMInstance) InitDump(transactionId string) error { TransactionId: transactionId, State: fsm.StateGlobalIdle, Payload: &internal.DumpedMachineStatePayload{ - TransactionId: transactionId, - ConfirmationProposalPayload: nil, - DKGProposalPayload: nil, + TransactionId: transactionId, + SignatureProposalPayload: nil, + DKGProposalPayload: nil, }, } return nil @@ -149,6 +149,13 @@ func (i *FSMInstance) Id() string { return "" } +func (i *FSMInstance) Dump() ([]byte, error) { + if i.dump == nil { + return []byte{}, errors.New("dump is not initialized") + } + return i.dump.Marshal() +} + // TODO: Add encryption func (d *FSMDump) Marshal() ([]byte, error) { return json.Marshal(d) @@ -157,7 +164,7 @@ func (d *FSMDump) Marshal() ([]byte, error) { // TODO: Add decryption func (d *FSMDump) Unmarshal(data []byte) error { if d == nil { - return errors.New("dump struct is not initialized") + return errors.New("dump is not initialized") } return json.Unmarshal(data, d) diff --git a/fsm/state_machines/provider_test.go b/fsm/state_machines/provider_test.go index 5a17911..e080caf 100644 --- a/fsm/state_machines/provider_test.go +++ b/fsm/state_machines/provider_test.go @@ -36,6 +36,8 @@ var ( Participants: []*requests.SignatureProposalParticipantsEntry{}, CreatedAt: &tm, } + + testFSMDump []byte ) func init() { @@ -118,21 +120,41 @@ func compareState(t *testing.T, expected fsm.State, got fsm.State) { } } -func Test_Workflow(t *testing.T) { - testFSMInstance, err := Create() +// Test Workflow - log.Println(testFSMInstance.Id()) +func Test_SignatureProposal_Init(t *testing.T) { + testFSMInstance, err := Create() compareErrNil(t, err) compareFSMInstanceNotNil(t, testFSMInstance) + transactionId := testFSMInstance.Id() + + if transactionId == "" { + t.Fatalf("expected {transactionId for dump}") + } + if testFSMInstance.machine.Name() != spf.FsmName { t.Fatalf("expected machine name {%s}", spf.FsmName) } compareState(t, spf.StateParticipantsConfirmationsInit, testFSMInstance.machine.State()) + testFSMDump, err = testFSMInstance.Dump() + + compareErrNil(t, err) + + compareDumpNotZero(t, testFSMDump) +} + +func Test_SignatureProposal_Positive(t *testing.T) { + testFSMInstance, err := FromDump(testFSMDump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest) compareErrNil(t, err) @@ -143,10 +165,6 @@ func Test_Workflow(t *testing.T) { compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State) - if testFSMInstance.Id() != testTransactionId { - t.Fatalf("expected {testTransactionId}") - } - testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse) if !ok { @@ -179,7 +197,7 @@ func Test_Workflow(t *testing.T) { participantsMap[participant.ParticipantId] = participant } - tm = tm.Add(10 * time.Second) + tm = tm.Add(10 * time.Hour) participantsCount := len(participantsMap) @@ -217,18 +235,194 @@ func Test_Workflow(t *testing.T) { compareFSMResponseNotNil(t, fsmResponse) if participantCounter > 0 { - if fsmResponse.State != spf.StateAwaitParticipantsConfirmations { - t.Fatalf("expected state {%s} got {%s}", spf.StateAwaitParticipantsConfirmations, fsmResponse.State) - } + compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State) } else { - if fsmResponse.State != dpf.StateDkgInitial { - t.Fatalf("expected state {%s} got {%s}", dpf.StateDkgInitial, fsmResponse.State) - } + compareState(t, dpf.StateDkgInitial, fsmResponse.State) } - state, err := testFSMInstance.State() + } - log.Println(err, state) + // PubKeys + + for _, participant := range participantsMap { + participantCounter-- + testFSMInstance, err = FromDump(dump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + + if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok { + t.Fatalf("not found external user data for response fingerprint") + } + + pubKeyMock := make([]byte, 128) + _, err := rand.Read(pubKeyMock) + if err != nil { + compareErrNil(t, err) + } + + fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGPubKeyConfirmationReceived, requests.DKGProposalPubKeyConfirmationRequest{ + ParticipantId: participant.ParticipantId, + PubKey: pubKeyMock, + CreatedAt: tm, + }) + + compareErrNil(t, err) + + compareDumpNotZero(t, dump) + + compareFSMResponseNotNil(t, fsmResponse) } + + compareState(t, dpf.StateDkgCommitsAwaitConfirmations, fsmResponse.State) + + // Commits + + for _, participant := range participantsMap { + participantCounter-- + testFSMInstance, err = FromDump(dump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + + if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok { + t.Fatalf("not found external user data for response fingerprint") + } + + commitMock := make([]byte, 128) + _, err := rand.Read(commitMock) + if err != nil { + compareErrNil(t, err) + } + + fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGCommitConfirmationReceived, requests.DKGProposalCommitConfirmationRequest{ + ParticipantId: participant.ParticipantId, + Commit: commitMock, + CreatedAt: tm, + }) + + compareErrNil(t, err) + + compareDumpNotZero(t, dump) + + compareFSMResponseNotNil(t, fsmResponse) + + } + + compareState(t, dpf.StateDkgDealsAwaitConfirmations, fsmResponse.State) + + // Deals + + for _, participant := range participantsMap { + participantCounter-- + testFSMInstance, err = FromDump(dump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + + if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok { + t.Fatalf("not found external user data for response fingerprint") + } + + dealMock := make([]byte, 128) + _, err := rand.Read(dealMock) + if err != nil { + compareErrNil(t, err) + } + + fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGDealConfirmationReceived, requests.DKGProposalDealConfirmationRequest{ + ParticipantId: participant.ParticipantId, + Deal: dealMock, + CreatedAt: tm, + }) + + compareErrNil(t, err) + + compareDumpNotZero(t, dump) + + compareFSMResponseNotNil(t, fsmResponse) + + } + + compareState(t, dpf.StateDkgResponsesAwaitConfirmations, fsmResponse.State) + + // Responses + + for _, participant := range participantsMap { + participantCounter-- + testFSMInstance, err = FromDump(dump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + + if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok { + t.Fatalf("not found external user data for response fingerprint") + } + + responseMock := make([]byte, 128) + _, err := rand.Read(responseMock) + if err != nil { + compareErrNil(t, err) + } + + fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGResponseConfirmationReceived, requests.DKGProposalResponseConfirmationRequest{ + ParticipantId: participant.ParticipantId, + Response: responseMock, + CreatedAt: tm, + }) + + compareErrNil(t, err) + + compareDumpNotZero(t, dump) + + compareFSMResponseNotNil(t, fsmResponse) + + } + + compareState(t, dpf.StateDkgMasterKeyAwaitConfirmations, fsmResponse.State) + + // Master keys + + masterKeyMock := make([]byte, 128) + _, err = rand.Read(masterKeyMock) + if err != nil { + compareErrNil(t, err) + } + + for _, participant := range participantsMap { + participantCounter-- + testFSMInstance, err = FromDump(dump) + + compareErrNil(t, err) + + compareFSMInstanceNotNil(t, testFSMInstance) + + if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok { + t.Fatalf("not found external user data for response fingerprint") + } + + fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGMasterKeyConfirmationReceived, requests.DKGProposalMasterKeyConfirmationRequest{ + ParticipantId: participant.ParticipantId, + MasterKey: masterKeyMock, + CreatedAt: tm, + }) + + compareErrNil(t, err) + + compareDumpNotZero(t, dump) + + compareFSMResponseNotNil(t, fsmResponse) + + } + + compareState(t, fsm.StateGlobalDone, fsmResponse.State) +} + +func Test_DKGProposal_Positive(t *testing.T) { + } diff --git a/fsm/state_machines/signature_proposal_fsm/actions.go b/fsm/state_machines/signature_proposal_fsm/actions.go index b601bbb..46a8c8d 100644 --- a/fsm/state_machines/signature_proposal_fsm/actions.go +++ b/fsm/state_machines/signature_proposal_fsm/actions.go @@ -3,10 +3,13 @@ package signature_proposal_fsm import ( "crypto/x509" "errors" + "fmt" + "github.com/depools/dc4bc/fsm/config" "github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/types/requests" "github.com/depools/dc4bc/fsm/types/responses" + "time" ) // init -> awaitingConfirmations @@ -31,7 +34,9 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int return } - m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum) + m.payload.SignatureProposalPayload = &internal.SignatureConfirmation{ + Quorum: make(internal.SignatureProposalQuorum), + } for index, participant := range request.Participants { participantId := createFingerprint(&participant.PubKey) @@ -46,18 +51,18 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int return inEvent, nil, errors.New("cannot parse {PubKey}") } - m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{ + m.payload.SignatureProposalPayload.Quorum[participantId] = &internal.SignatureProposalParticipant{ ParticipantId: index, Title: participant.Title, PublicKey: parsedPubKey, InvitationSecret: secret, - Status: internal.SignatureAwaitConfirmation, + Status: internal.SignatureConfirmationAwaitConfirmation, UpdatedAt: request.CreatedAt, } } // Checking fo quorum length - if len(m.payload.ConfirmationProposalPayload) != len(request.Participants) { + if m.payload.SigQuorumCount() != len(request.Participants) { err = errors.New("error with creating {SignatureProposalQuorum}") return } @@ -66,7 +71,7 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0) - for pubKeyFingerprint, proposal := range m.payload.ConfirmationProposalPayload { + for pubKeyFingerprint, proposal := range m.payload.SignatureProposalPayload.Quorum { encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret) if err != nil { return inEvent, nil, errors.New("cannot encryptWithPubKey") @@ -86,8 +91,8 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int // TODO: Add timeout checking func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { - // m.payloadMu.Lock() - // defer m.payloadMu.Unlock() + m.payloadMu.Lock() + defer m.payloadMu.Unlock() if len(args) != 1 { err = errors.New("{arg0} required {SignatureProposalParticipantRequest}") @@ -105,42 +110,134 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E return } - signatureProposalParticipant, ok := m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] - - if !ok { + if !m.payload.SigQuorumExists(request.PubKeyFingerprint) { err = errors.New("{PubKeyFingerprint} not exist in quorum") return } + signatureProposalParticipant := m.payload.SigQuorumGet(request.PubKeyFingerprint) + if signatureProposalParticipant.InvitationSecret != request.DecryptedInvitation { err = errors.New("{InvitationSecret} not match {DecryptedInvitation}") return } - signatureProposalParticipant.Status = internal.SignatureConfirmed + if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(*request.CreatedAt) { + outEvent = eventSetValidationCanceledByTimeout + return + } + + if signatureProposalParticipant.Status != internal.SignatureConfirmationAwaitConfirmation { + err = errors.New(fmt.Sprintf("cannot apply reply participant with {Status} = {\"%s\"}", signatureProposalParticipant.Status)) + return + } + + switch inEvent { + case EventConfirmProposal: + signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed + case EventDeclineProposal: + signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined + default: + err = errors.New("undefined {Event} for action") + return + } + signatureProposalParticipant.UpdatedAt = request.CreatedAt - m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant - - outEvent, response, err = m.actionValidateProposal(eventValidateProposalInternal) + m.payload.SigQuorumUpdate(request.PubKeyFingerprint, signatureProposalParticipant) return } -func (m *SignatureProposalFSM) actionValidateProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { - // m.payloadMu.Lock() - // defer m.payloadMu.Unlock() +func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + var ( + isContainsDeclined, isContainsExpired bool + ) - unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload) - for _, participant := range m.payload.ConfirmationProposalPayload { - if participant.Status == internal.SignatureConfirmed { - unconfirmedParticipants-- + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + tm := time.Now() + + unconfirmedParticipants := m.payload.SigQuorumCount() + for _, participant := range m.payload.SignatureProposalPayload.Quorum { + if participant.Status == internal.SignatureConfirmationAwaitConfirmation { + if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(tm) { + isContainsExpired = true + } + } else { + if participant.Status == internal.SignatureConfirmationConfirmed { + unconfirmedParticipants-- + } else if participant.Status == internal.SignatureConfirmationDeclined { + isContainsDeclined = true + } } } + if isContainsDeclined { + outEvent = eventSetValidationCanceledByParticipant + return + } + + if isContainsExpired { + outEvent = eventSetValidationCanceledByTimeout + return + } + + // The are no declined and timed out participants, check for all confirmations if unconfirmedParticipants > 0 { return } + outEvent = eventSetProposalValidatedInternal + + m.actionSetValidatedSignatureProposal(outEvent) + return } + +func (m *SignatureProposalFSM) actionSetValidatedSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + // m.payloadMu.Lock() + // defer m.payloadMu.Unlock() + + // TODO: Run once after validation + if m.payload.DKGProposalPayload != nil { + return + } + + m.payload.DKGProposalPayload = &internal.DKGConfirmation{ + Quorum: make(internal.DKGProposalQuorum), + } + + for _, participant := range m.payload.SignatureProposalPayload.Quorum { + m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{ + Title: participant.Title, + Status: internal.PubKeyAwaitConfirmation, + UpdatedAt: participant.UpdatedAt, + } + } + + // Remove m.payload.SignatureProposalPayload? + + return +} + +func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + responseData := make(responses.SignatureProposalParticipantStatusResponse, 0) + + for pubKeyFingerprint, participant := range m.payload.SignatureProposalPayload.Quorum { + responseEntry := &responses.SignatureProposalParticipantStatusEntry{ + ParticipantId: participant.ParticipantId, + Title: participant.Title, + PubKeyFingerprint: pubKeyFingerprint, + Status: uint8(participant.Status), + } + responseData = append(responseData, responseEntry) + } + + return inEvent, responseData, nil + +} diff --git a/fsm/state_machines/signature_proposal_fsm/init.go b/fsm/state_machines/signature_proposal_fsm/init.go index a338920..72312e2 100644 --- a/fsm/state_machines/signature_proposal_fsm/init.go +++ b/fsm/state_machines/signature_proposal_fsm/init.go @@ -19,15 +19,15 @@ const ( StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout") // Out state - StateValidationCompleted = fsm.State("state_sig_proposal_completed") EventInitProposal = fsm.Event("event_sig_proposal_init") EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant") EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant") - eventValidateProposalInternal = fsm.Event("event_sig_proposal_validate") + eventAutoValidateProposalInternal = fsm.Event("event_sig_proposal_validate") eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated") - eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout") + eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout") + eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_declined_timeout") // Switch to next fsm @@ -55,9 +55,10 @@ func New() internal.DumpedMachineProvider { {Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations}, // Is decline event should auto change state to default, or it process will initiated by client (external emit)? // Now set for external emitting. - {Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant}, + {Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations}, + {Name: eventSetValidationCanceledByParticipant, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant, IsInternal: true}, - {Name: eventValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true}, + {Name: eventAutoValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true, IsAuto: true}, // eventProposalValidate internal or from client? // yay @@ -67,10 +68,11 @@ func New() internal.DumpedMachineProvider { {Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true}, }, fsm.Callbacks{ - EventInitProposal: machine.actionInitProposal, - EventConfirmProposal: machine.actionProposalResponseByParticipant, - EventDeclineProposal: machine.actionProposalResponseByParticipant, - eventValidateProposalInternal: machine.actionValidateProposal, + EventInitProposal: machine.actionInitProposal, + EventConfirmProposal: machine.actionProposalResponseByParticipant, + EventDeclineProposal: machine.actionProposalResponseByParticipant, + eventAutoValidateProposalInternal: machine.actionValidateSignatureProposal, + eventSetProposalValidatedInternal: machine.actionSetValidatedSignatureProposal, }, ) return machine diff --git a/fsm/types/requests/dkg_proposal.go b/fsm/types/requests/dkg_proposal.go index b59d37e..65bc61a 100644 --- a/fsm/types/requests/dkg_proposal.go +++ b/fsm/types/requests/dkg_proposal.go @@ -7,7 +7,7 @@ import "time" type DKGProposalPubKeyConfirmationRequest struct { ParticipantId int PubKey []byte - CreatedAt *time.Time + CreatedAt time.Time } // States: "state_dkg_commits_sending_await_confirmations" @@ -15,7 +15,7 @@ type DKGProposalPubKeyConfirmationRequest struct { type DKGProposalCommitConfirmationRequest struct { ParticipantId int Commit []byte - CreatedAt *time.Time + CreatedAt time.Time } // States: "state_dkg_deals_await_confirmations" @@ -23,7 +23,7 @@ type DKGProposalCommitConfirmationRequest struct { type DKGProposalDealConfirmationRequest struct { ParticipantId int Deal []byte - CreatedAt *time.Time + CreatedAt time.Time } // States: "state_dkg_responses_await_confirmations" @@ -31,20 +31,30 @@ type DKGProposalDealConfirmationRequest struct { type DKGProposalResponseConfirmationRequest struct { ParticipantId int Response []byte - CreatedAt *time.Time + CreatedAt time.Time +} + +// States: "state_dkg_master_key_await_confirmations" +// Events: "event_dkg_master_key_confirm_received" +type DKGProposalMasterKeyConfirmationRequest struct { + ParticipantId int + MasterKey []byte + CreatedAt time.Time } // States: "state_dkg_pub_keys_await_confirmations" // "state_dkg_commits_sending_await_confirmations" // "state_dkg_deals_await_confirmations" // "state_dkg_responses_await_confirmations" +// "state_dkg_master_key_await_confirmations" // // Events: "event_dkg_pub_key_confirm_canceled_by_error", // "event_dkg_commit_confirm_canceled_by_error" // "event_dkg_deal_confirm_canceled_by_error" // "event_dkg_response_confirm_canceled_by_error" +// "event_dkg_master_key_confirm_canceled_by_error" type DKGProposalConfirmationErrorRequest struct { ParticipantId int Error error - CreatedAt *time.Time + CreatedAt time.Time } diff --git a/fsm/types/requests/dkg_proposal_validation.go b/fsm/types/requests/dkg_proposal_validation.go index 931255f..a9ce43d 100644 --- a/fsm/types/requests/dkg_proposal_validation.go +++ b/fsm/types/requests/dkg_proposal_validation.go @@ -11,8 +11,8 @@ func (r *DKGProposalPubKeyConfirmationRequest) Validate() error { return errors.New("{PubKey} cannot zero length") } - if r.CreatedAt == nil { - return errors.New("{CreatedAt} cannot be a nil") + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") } return nil @@ -27,8 +27,8 @@ func (r *DKGProposalCommitConfirmationRequest) Validate() error { return errors.New("{Commit} cannot zero length") } - if r.CreatedAt == nil { - return errors.New("{CreatedAt} cannot be a nil") + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") } return nil @@ -43,8 +43,8 @@ func (r *DKGProposalDealConfirmationRequest) Validate() error { return errors.New("{Deal} cannot zero length") } - if r.CreatedAt == nil { - return errors.New("{CreatedAt} cannot be a nil") + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") } return nil @@ -59,8 +59,24 @@ func (r *DKGProposalResponseConfirmationRequest) Validate() error { return errors.New("{Response} cannot zero length") } - if r.CreatedAt == nil { - return errors.New("{CreatedAt} cannot be a nil") + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") + } + + return nil +} + +func (r *DKGProposalMasterKeyConfirmationRequest) Validate() error { + if r.ParticipantId < 0 { + return errors.New("{ParticipantId} cannot be a negative number") + } + + if len(r.MasterKey) == 0 { + return errors.New("{MasterKey} cannot zero length") + } + + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") } return nil @@ -71,8 +87,12 @@ func (r *DKGProposalConfirmationErrorRequest) Validate() error { return errors.New("{ParticipantId} cannot be a negative number") } - if r.CreatedAt == nil { - return errors.New("{CreatedAt} cannot be a nil") + if r.Error == nil { + return errors.New("{Error} cannot be a nil") + } + + if r.CreatedAt.IsZero() { + return errors.New("{CreatedAt} is not set") } return nil diff --git a/fsm/types/responses/signature_proposal.go b/fsm/types/responses/signature_proposal.go index 6a1248e..855a59a 100644 --- a/fsm/types/responses/signature_proposal.go +++ b/fsm/types/responses/signature_proposal.go @@ -2,14 +2,8 @@ package responses // Response -const ( - ProposalConfirmationStatusIdle = iota - ProposalConfirmationStatusAccepted - ProposalConfirmationStatusCanceled - ProposalConfirmationStatusTimeout -) - -// States: "validate_proposal" +// Event: "event_sig_proposal_init" +// States: "__idle" type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry @@ -31,5 +25,5 @@ type SignatureProposalParticipantStatusEntry struct { ParticipantId int Title string PubKeyFingerprint string - Status int + Status uint8 }