From 33ecea0a9a2ef3e4653b9b6e2f8a2e66af730f06 Mon Sep 17 00:00:00 2001 From: x88 Date: Thu, 13 Aug 2020 18:22:06 +0300 Subject: [PATCH] feat: updated flow --- fsm/fsm/fsm.go | 138 +++++++++++------- fsm/fsm_pool/fsm_pool.go | 14 -- .../dkg_proposal_fsm/actions.go | 116 +++------------ fsm/state_machines/dkg_proposal_fsm/init.go | 47 ++---- fsm/state_machines/internal/types.go | 22 +-- fsm/state_machines/provider_test.go | 71 +++------ .../signature_proposal_fsm/actions.go | 53 +++---- .../signature_proposal_fsm/init.go | 20 ++- fsm/types/requests/dkg_proposal.go | 8 - fsm/types/requests/dkg_proposal_validation.go | 16 -- fsm/types/requests/signature_proposal.go | 9 +- .../requests/signature_proposal_validation.go | 8 +- fsm/types/responses/dkg_proposal.go | 8 - fsm/types/responses/signature_proposal.go | 8 +- 14 files changed, 185 insertions(+), 353 deletions(-) diff --git a/fsm/fsm/fsm.go b/fsm/fsm/fsm.go index 2af2abd..e0c788a 100644 --- a/fsm/fsm/fsm.go +++ b/fsm/fsm/fsm.go @@ -41,7 +41,7 @@ func (e *Event) String() string { return string(*e) } -func (e *Event) IsEmpty() bool { +func (e Event) IsEmpty() bool { return e.String() == "" } @@ -63,7 +63,7 @@ type FSM struct { // May be mapping must require pair source + event? transitions map[trKey]*trEvent - autoTransitions map[State]*trEvent + autoTransitions map[trAutoKeyEvent]*trEvent callbacks Callbacks @@ -94,6 +94,11 @@ type trEvent struct { runMode EventRunMode } +type trAutoKeyEvent struct { + state State + runMode EventRunMode +} + type EventDesc struct { Name Event @@ -138,7 +143,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call currentState: initialState, initialState: initialState, transitions: make(map[trKey]*trEvent), - autoTransitions: make(map[State]*trEvent), + autoTransitions: make(map[trAutoKeyEvent]*trEvent), finStates: make(map[State]bool), callbacks: make(map[Event]Callback), } @@ -217,14 +222,15 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call panic("{AutoRunMode} not set for auto event") } - if _, ok := f.autoTransitions[sourceState]; ok { + trAutoKey := trAutoKeyEvent{sourceState, event.AutoRunMode} + if _, ok := f.autoTransitions[trAutoKey]; ok { panic(fmt.Sprintf( "auto event \"%s\" already exists for state \"%s\"", event.Name, sourceState, )) } - f.autoTransitions[sourceState] = trEvent + f.autoTransitions[trAutoKey] = trEvent } allSources[sourceState] = true @@ -247,7 +253,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call } if _, ok := allEvents[event]; !ok { - panic("callback has empty event") + panic(fmt.Sprintf("callback has unused event \"%s\"", event)) } f.callbacks[event] = callback @@ -273,7 +279,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call func (f *FSM) DoInternal(event Event, args ...interface{}) (resp *Response, err error) { trEvent, ok := f.transitions[trKey{f.currentState, event}] if !ok { - return nil, errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState)) + return nil, errors.New(fmt.Sprintf("cannot execute internal event \"%s\" for state \"%s\"", event, f.currentState)) } return f.do(trEvent, args...) @@ -290,41 +296,55 @@ func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) { return f.do(trEvent, args...) } + +// Check and execute auto event +func (f *FSM) processAutoEvent(mode EventRunMode, args ...interface{}) (exists bool, outEvent Event, response interface{}, err error) { + autoEvent, exists := f.autoTransitions[trAutoKeyEvent{f.State(), mode}] + if !exists { + return + } + + if f.isCallbackExists(autoEvent.event) { + outEvent, response, err = f.execCallback(autoEvent.event, args...) + // Do not try change state on error + if err != nil { + return exists, "", response, err + } + + } + + if outEvent.IsEmpty() || autoEvent.event == outEvent { + err = f.SetState(autoEvent.event) + } else { + err = f.SetState(outEvent) + } + + return +} + func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) { var outEvent Event // f.eventMu.Lock() // defer f.eventMu.Unlock() + resp = &Response{} + // Process auto event - if autoEvent, ok := f.autoTransitions[f.State()]; ok { - autoEventResp := &Response{ - State: f.State(), + isAutoEventExecuted, outEvent, data, err := f.processAutoEvent(EventRunBefore, args...) + + if isAutoEventExecuted { + resp.State = f.State() + if data != nil { + resp.Data = data } - if autoEvent.runMode == EventRunBefore { - if callback, ok := f.callbacks[autoEvent.event]; ok { - outEvent, autoEventResp.Data, err = callback(autoEvent.event, args...) - if err != nil { - return autoEventResp, err - } - } - if outEvent.IsEmpty() || autoEvent.event == outEvent { - err = f.SetState(autoEvent.event) - } else { - err = f.SetState(outEvent) - } - if err != nil { - return autoEventResp, err - } + + if err != nil { + return resp, err } - outEvent = "" } - resp = &Response{ - State: f.State(), - } - - if callback, ok := f.callbacks[trEvent.event]; ok { - outEvent, resp.Data, err = callback(trEvent.event, args...) + if f.isCallbackExists(trEvent.event) { + outEvent, resp.Data, err = f.execCallback(trEvent.event, args...) // Do not try change state on error if err != nil { return resp, err @@ -334,36 +354,32 @@ func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err err // Set state when callback executed if outEvent.IsEmpty() || trEvent.event == outEvent { err = f.SetState(trEvent.event) + if err != nil { + return resp, err + } } else { err = f.SetState(outEvent) - } - - // Process auto event - if autoEvent, ok := f.autoTransitions[f.State()]; ok { - autoEventResp := &Response{ - State: f.State(), + if err != nil { + return resp, err } - if autoEvent.runMode == EventRunAfter { - if callback, ok := f.callbacks[autoEvent.event]; ok { - outEvent, autoEventResp.Data, err = callback(autoEvent.event, args...) - if err != nil { - return autoEventResp, err - } - } - if outEvent.IsEmpty() || autoEvent.event == outEvent { - err = f.SetState(autoEvent.event) - } else { - err = f.SetState(outEvent) - } - if err != nil { - return autoEventResp, err - } - } - outEvent = "" } resp.State = f.State() + // Process auto event + isAutoEventExecuted, outEvent, data, err = f.processAutoEvent(EventRunAfter, args...) + + if isAutoEventExecuted { + resp.State = f.State() + if data != nil { + resp.Data = data + } + + if err != nil { + return resp, err + } + } + return } @@ -382,7 +398,7 @@ func (f *FSM) SetState(event Event) error { trEvent, ok := f.transitions[trKey{f.currentState, event}] if !ok { - return errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState)) + return errors.New(fmt.Sprintf("cannot set state by event \"%s\" for state \"%s\"", event, f.currentState)) } f.currentState = trEvent.dstState @@ -457,6 +473,16 @@ func (f *FSM) StatesSourcesList() (states []State) { return } +func (f *FSM) isCallbackExists(event Event) bool { + _, exists := f.callbacks[event] + return exists +} + +func (f *FSM) execCallback(event Event, args ...interface{}) (Event, interface{}, error) { + callback, _ := f.callbacks[event] + return callback(event, args...) +} + func (f *FSM) IsFinState(state State) bool { _, exists := f.finStates[state] return exists diff --git a/fsm/fsm_pool/fsm_pool.go b/fsm/fsm_pool/fsm_pool.go index a6d2dbf..4fcb087 100644 --- a/fsm/fsm_pool/fsm_pool.go +++ b/fsm/fsm_pool/fsm_pool.go @@ -160,17 +160,3 @@ func (p *FSMPool) MachineByState(state fsm.State) (MachineProvider, error) { } return machine, nil } - -/*func (p *FSMPool) Do(machine MachineProvider, event fsm.Event, args ...interface{}) (resp *fsm.Response, err error) { - panic("llslsl") - resp, err = machine.Do(event, args...) - if err != nil { - return resp, err - } - - if machine.IsFinState(resp.State) { - log.Println("Final!!!!") - } - return -} -*/ diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index 3fc1913..60ac93f 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -11,94 +11,27 @@ import ( "time" ) -// Pub keys +// Init -func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(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 {DKGProposalPubKeyConfirmationRequest}") +func (m *DKGProposalFSM) actionInitDKGProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { + if m.payload.DKGProposalPayload != nil { return } - request, ok := args[0].(requests.DKGProposalPubKeyConfirmationRequest) - - if !ok { - err = errors.New("cannot cast {arg0} to type {DKGProposalPubKeyConfirmationRequest}") - return + m.payload.DKGProposalPayload = &internal.DKGConfirmation{ + Quorum: make(internal.DKGProposalQuorum), } - 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.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.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-- - } + for _, participant := range m.payload.SignatureProposalPayload.Quorum { + m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{ + Title: participant.Title, + Status: internal.CommitAwaitConfirmation, + UpdatedAt: participant.UpdatedAt, } + copy(m.payload.DKGProposalPayload.Quorum[participant.ParticipantId].PubKey, participant.DkgPubKey) } - 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 - } + // Remove m.payload.SignatureProposalPayload? return } @@ -138,7 +71,7 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg } copy(dkgProposalParticipant.Commit, request.Commit) - dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.UpdatedAt = request.CreatedAt dkgProposalParticipant.Status = internal.CommitConfirmed m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) @@ -230,7 +163,7 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args } copy(dkgProposalParticipant.Deal, request.Deal) - dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.UpdatedAt = request.CreatedAt dkgProposalParticipant.Status = internal.DealConfirmed m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) @@ -322,7 +255,7 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a } copy(dkgProposalParticipant.Response, request.Response) - dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.UpdatedAt = request.CreatedAt dkgProposalParticipant.Status = internal.ResponseConfirmed m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) @@ -414,7 +347,7 @@ func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event, } copy(dkgProposalParticipant.MasterKey, request.MasterKey) - dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.UpdatedAt = request.CreatedAt dkgProposalParticipant.Status = internal.MasterKeyConfirmed m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) @@ -525,21 +458,6 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte // TODO: Move to methods switch inEvent { - case EventDKGPubKeyConfirmationError: - switch dkgProposalParticipant.Status { - case internal.PubKeyAwaitConfirmation: - dkgProposalParticipant.Status = internal.PubKeyConfirmationError - case internal.PubKeyConfirmed: - err = errors.New("{Status} already confirmed") - case internal.PubKeyConfirmationError: - err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.PubKeyConfirmationError)) - default: - err = errors.New(fmt.Sprintf( - "{Status} now is \"%s\" and cannot set to {\"%s\"}", - dkgProposalParticipant.Status, - internal.PubKeyConfirmationError, - )) - } case EventDKGCommitConfirmationError: switch dkgProposalParticipant.Status { case internal.CommitAwaitConfirmation: @@ -609,7 +527,7 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte } dkgProposalParticipant.Error = request.Error - dkgProposalParticipant.UpdatedAt = &request.CreatedAt + dkgProposalParticipant.UpdatedAt = request.CreatedAt m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant) diff --git a/fsm/state_machines/dkg_proposal_fsm/init.go b/fsm/state_machines/dkg_proposal_fsm/init.go index 7fa89ed..78bbe33 100644 --- a/fsm/state_machines/dkg_proposal_fsm/init.go +++ b/fsm/state_machines/dkg_proposal_fsm/init.go @@ -9,14 +9,7 @@ import ( const ( FsmName = "dkg_proposal_fsm" - StateDkgInitial = StateDkgPubKeysAwaitConfirmations - - StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations") - // 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") + StateDkgInitial = StateDkgCommitsAwaitConfirmations // Sending dkg commits StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations") @@ -24,7 +17,7 @@ const ( 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") + StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected") // Sending dkg deals StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations") @@ -32,31 +25,23 @@ const ( 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") + //StateDkgDealsCollected = fsm.State("state_dkg_deals_collected") StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations") // 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") + StateDkgResponsesCollected = fsm.State("state_dkg_responses_collected") 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") + StateDkgMasterKeyCollected = fsm.State("state_dkg_master_key_collected") + // Events - - eventAutoDKGInitialInternal = fsm.Event("event_dkg_init_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") + eventAutoDKGInitialInternal = fsm.Event("event_dkg_init") EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received") EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error") @@ -109,18 +94,11 @@ func New() internal.DumpedMachineProvider { // {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}, - // Canceled - {Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceled}, + // StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected") - {Name: eventAutoDKGValidatePubKeysConfirmationInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true}, + // {Name: eventAutoDKGInitInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore}, - {Name: eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceledByTimeout, IsInternal: true}, - // Confirmed - {Name: eventDKGSetPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true}, - - // Switch to commits required + {Name: eventAutoDKGInitialInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore}, // Commits {Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations}, @@ -166,10 +144,7 @@ func New() internal.DumpedMachineProvider { // {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true}, }, fsm.Callbacks{ - - EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived, - EventDKGPubKeyConfirmationError: machine.actionConfirmationError, - eventAutoDKGValidatePubKeysConfirmationInternal: machine.actionValidateDkgProposalPubKeys, + eventAutoDKGInitialInternal: machine.actionInitDKGProposal, EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived, EventDKGCommitConfirmationError: machine.actionConfirmationError, diff --git a/fsm/state_machines/internal/types.go b/fsm/state_machines/internal/types.go index ed24ecb..ea45971 100644 --- a/fsm/state_machines/internal/types.go +++ b/fsm/state_machines/internal/types.go @@ -7,19 +7,20 @@ import ( type SignatureConfirmation struct { Quorum SignatureProposalQuorum - CreatedAt *time.Time - ExpiresAt *time.Time + CreatedAt time.Time + ExpiresAt time.Time } type SignatureProposalParticipant struct { // Public title for address, such as name, nickname, organization ParticipantId int Title string - PublicKey *rsa.PublicKey - // For validation user confirmation: sign(InvitationSecret, PublicKey) => user + PubKey *rsa.PublicKey + DkgPubKey []byte + // For validation user confirmation: sign(InvitationSecret, PubKey) => user InvitationSecret string Status ParticipantStatus - UpdatedAt *time.Time + UpdatedAt time.Time } // Unique alias for map iteration - Public Key Fingerprint @@ -33,9 +34,6 @@ const ( SignatureConfirmationConfirmed SignatureConfirmationDeclined SignatureConfirmationError - PubKeyAwaitConfirmation - PubKeyConfirmed - PubKeyConfirmationError CommitAwaitConfirmation CommitConfirmed CommitConfirmationError @@ -59,7 +57,7 @@ type DKGProposalParticipant struct { MasterKey []byte Status ParticipantStatus Error error - UpdatedAt *time.Time + UpdatedAt time.Time } type DKGProposalQuorum map[int]*DKGProposalParticipant @@ -83,12 +81,6 @@ func (s ParticipantStatus) String() string { str = "SignatureConfirmationDeclined" case SignatureConfirmationError: str = "SignatureConfirmationError" - case PubKeyAwaitConfirmation: - str = "PubKeyAwaitConfirmation" - case PubKeyConfirmed: - str = "PubKeyConfirmed" - case PubKeyConfirmationError: - str = "PubKeyConfirmationError" case CommitAwaitConfirmation: str = "CommitAwaitConfirmation" case CommitConfirmed: diff --git a/fsm/state_machines/provider_test.go b/fsm/state_machines/provider_test.go index e080caf..67f001f 100644 --- a/fsm/state_machines/provider_test.go +++ b/fsm/state_machines/provider_test.go @@ -17,14 +17,11 @@ import ( "time" ) -const ( - testTransactionId = "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257" -) - type testExternalParticipants struct { - Title string - PrivKey *rsa.PrivateKey - PubKey *rsa.PublicKey + Title string + PrivKey *rsa.PrivateKey + PubKey *rsa.PublicKey + DkgPubKey []byte } var ( @@ -34,7 +31,7 @@ var ( testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{ Participants: []*requests.SignatureProposalParticipantsEntry{}, - CreatedAt: &tm, + CreatedAt: tm, } testFSMDump []byte @@ -59,10 +56,15 @@ func init() { fingerprint := base64.StdEncoding.EncodeToString(hash[:]) + pubKeyMock := make([]byte, 128) + + rand.Read(pubKeyMock) + participant := &testExternalParticipants{ - Title: fmt.Sprintf("User %d", i), - PrivKey: key, - PubKey: &key.PublicKey, + Title: fmt.Sprintf("User %d", i), + PrivKey: key, + PubKey: &key.PublicKey, + DkgPubKey: pubKeyMock, } testParticipants[fingerprint] = participant } @@ -72,8 +74,9 @@ func init() { for _, participant := range testParticipants { participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{ - Title: participant.Title, - PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey), + Title: participant.Title, + PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey), + DkgPubKey: participant.DkgPubKey, }) } testParticipantsListRequest.Participants = participantsForRequest @@ -197,7 +200,7 @@ func Test_SignatureProposal_Positive(t *testing.T) { participantsMap[participant.ParticipantId] = participant } - tm = tm.Add(10 * time.Hour) + tm = tm.Add(1 * time.Hour) participantsCount := len(participantsMap) @@ -222,10 +225,10 @@ func Test_SignatureProposal_Positive(t *testing.T) { t.Fatalf("cannot encrypt {DecryptedInvitation} with private key") } - fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{ + fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmSignatureProposal, requests.SignatureProposalParticipantRequest{ PubKeyFingerprint: participant.PubKeyFingerprint, DecryptedInvitation: string(encrypted), - CreatedAt: &tm, + CreatedAt: tm, }) compareErrNil(t, err) @@ -237,45 +240,11 @@ func Test_SignatureProposal_Positive(t *testing.T) { if participantCounter > 0 { compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State) } else { - compareState(t, dpf.StateDkgInitial, fsmResponse.State) + compareState(t, spf.StateSignatureProposalCollected, fsmResponse.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 diff --git a/fsm/state_machines/signature_proposal_fsm/actions.go b/fsm/state_machines/signature_proposal_fsm/actions.go index 46a8c8d..f768732 100644 --- a/fsm/state_machines/signature_proposal_fsm/actions.go +++ b/fsm/state_machines/signature_proposal_fsm/actions.go @@ -14,7 +14,7 @@ import ( // init -> awaitingConfirmations // args: payload, signing id, participants list -func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { +func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { m.payloadMu.Lock() defer m.payloadMu.Unlock() @@ -54,7 +54,8 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int m.payload.SignatureProposalPayload.Quorum[participantId] = &internal.SignatureProposalParticipant{ ParticipantId: index, Title: participant.Title, - PublicKey: parsedPubKey, + PubKey: parsedPubKey, + DkgPubKey: participant.DkgPubKey, InvitationSecret: secret, Status: internal.SignatureConfirmationAwaitConfirmation, UpdatedAt: request.CreatedAt, @@ -72,7 +73,7 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0) for pubKeyFingerprint, proposal := range m.payload.SignatureProposalPayload.Quorum { - encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret) + encryptedInvitationSecret, err := encryptWithPubKey(proposal.PubKey, proposal.InvitationSecret) if err != nil { return inEvent, nil, errors.New("cannot encryptWithPubKey") } @@ -122,7 +123,7 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E return } - if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(*request.CreatedAt) { + if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(request.CreatedAt) { outEvent = eventSetValidationCanceledByTimeout return } @@ -133,7 +134,7 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E } switch inEvent { - case EventConfirmProposal: + case EventConfirmSignatureProposal: signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed case EventDeclineProposal: signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined @@ -189,37 +190,24 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event 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 { + err = m.SetState(eventSetProposalValidatedInternal) + if err != nil { return } - m.payload.DKGProposalPayload = &internal.DKGConfirmation{ - Quorum: make(internal.DKGProposalQuorum), - } + responseData := make(responses.SignatureProposalParticipantStatusResponse, 0) for _, participant := range m.payload.SignatureProposalPayload.Quorum { - m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{ - Title: participant.Title, - Status: internal.PubKeyAwaitConfirmation, - UpdatedAt: participant.UpdatedAt, + responseEntry := &responses.SignatureProposalParticipantStatusEntry{ + ParticipantId: participant.ParticipantId, + Title: participant.Title, + DkgPubKey: participant.DkgPubKey, + Status: uint8(participant.Status), } + responseData = append(responseData, responseEntry) } - // Remove m.payload.SignatureProposalPayload? - - return + return eventDoneInternal, responseData, nil } func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) { @@ -228,12 +216,11 @@ func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent responseData := make(responses.SignatureProposalParticipantStatusResponse, 0) - for pubKeyFingerprint, participant := range m.payload.SignatureProposalPayload.Quorum { + for _, participant := range m.payload.SignatureProposalPayload.Quorum { responseEntry := &responses.SignatureProposalParticipantStatusEntry{ - ParticipantId: participant.ParticipantId, - Title: participant.Title, - PubKeyFingerprint: pubKeyFingerprint, - Status: uint8(participant.Status), + ParticipantId: participant.ParticipantId, + Title: participant.Title, + Status: uint8(participant.Status), } responseData = append(responseData, responseEntry) } diff --git a/fsm/state_machines/signature_proposal_fsm/init.go b/fsm/state_machines/signature_proposal_fsm/init.go index 72312e2..b45f0b8 100644 --- a/fsm/state_machines/signature_proposal_fsm/init.go +++ b/fsm/state_machines/signature_proposal_fsm/init.go @@ -2,7 +2,7 @@ package signature_proposal_fsm import ( "github.com/depools/dc4bc/fsm/fsm" - "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm" + dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm" "github.com/depools/dc4bc/fsm/state_machines/internal" "sync" ) @@ -21,14 +21,18 @@ const ( // Out state EventInitProposal = fsm.Event("event_sig_proposal_init") - EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant") + EventConfirmSignatureProposal = fsm.Event("event_sig_proposal_confirm_by_participant") EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant") eventAutoValidateProposalInternal = fsm.Event("event_sig_proposal_validate") eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated") + eventDoneInternal = fsm.Event("event_sig_proposal_done") + eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout") eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_declined_timeout") + StateSignatureProposalCollected = fsm.State("state_sig_proposal_collected") + // Switch to next fsm ) @@ -52,7 +56,7 @@ func New() internal.DumpedMachineProvider { {Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations}, // Validate by participants - {Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations}, + {Name: EventConfirmSignatureProposal, 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: StateAwaitParticipantsConfirmations}, @@ -62,17 +66,19 @@ func New() internal.DumpedMachineProvider { // eventProposalValidate internal or from client? // yay + + {Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateSignatureProposalCollected, IsInternal: true}, + // Exit point - {Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: dkg_proposal_fsm.StateDkgPubKeysAwaitConfirmations, IsInternal: true}, + {Name: eventDoneInternal, SrcState: []fsm.State{StateSignatureProposalCollected}, DstState: dpf.StateDkgInitial, IsInternal: true}, // nan {Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true}, }, fsm.Callbacks{ - EventInitProposal: machine.actionInitProposal, - EventConfirmProposal: machine.actionProposalResponseByParticipant, + EventInitProposal: machine.actionInitSignatureProposal, + EventConfirmSignatureProposal: 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 65bc61a..201a3b0 100644 --- a/fsm/types/requests/dkg_proposal.go +++ b/fsm/types/requests/dkg_proposal.go @@ -2,14 +2,6 @@ package requests import "time" -// States: "state_dkg_pub_keys_await_confirmations" -// Events: "event_dkg_pub_key_confirm_received" -type DKGProposalPubKeyConfirmationRequest struct { - ParticipantId int - PubKey []byte - CreatedAt time.Time -} - // States: "state_dkg_commits_sending_await_confirmations" // Events: "event_dkg_commit_confirm_received" type DKGProposalCommitConfirmationRequest struct { diff --git a/fsm/types/requests/dkg_proposal_validation.go b/fsm/types/requests/dkg_proposal_validation.go index a9ce43d..0d7aa30 100644 --- a/fsm/types/requests/dkg_proposal_validation.go +++ b/fsm/types/requests/dkg_proposal_validation.go @@ -2,22 +2,6 @@ package requests import "errors" -func (r *DKGProposalPubKeyConfirmationRequest) Validate() error { - if r.ParticipantId < 0 { - return errors.New("{ParticipantId} cannot be a negative number") - } - - if len(r.PubKey) == 0 { - return errors.New("{PubKey} cannot zero length") - } - - if r.CreatedAt.IsZero() { - return errors.New("{CreatedAt} is not set") - } - - return nil -} - func (r *DKGProposalCommitConfirmationRequest) Validate() error { if r.ParticipantId < 0 { return errors.New("{ParticipantId} cannot be a negative number") diff --git a/fsm/types/requests/signature_proposal.go b/fsm/types/requests/signature_proposal.go index 73f1fcf..8de5b8d 100644 --- a/fsm/types/requests/signature_proposal.go +++ b/fsm/types/requests/signature_proposal.go @@ -8,13 +8,14 @@ import "time" // Events: "event_sig_proposal_init" type SignatureProposalParticipantsListRequest struct { Participants []*SignatureProposalParticipantsEntry - CreatedAt *time.Time + CreatedAt time.Time } type SignatureProposalParticipantsEntry struct { // Public title for address, such as name, nickname, organization - Title string - PubKey []byte + Title string + PubKey []byte + DkgPubKey []byte } // States: "__idle" @@ -24,5 +25,5 @@ type SignatureProposalParticipantRequest struct { // Key for link invitations to participants PubKeyFingerprint string DecryptedInvitation string - CreatedAt *time.Time + CreatedAt time.Time } diff --git a/fsm/types/requests/signature_proposal_validation.go b/fsm/types/requests/signature_proposal_validation.go index ae3b712..accbb62 100644 --- a/fsm/types/requests/signature_proposal_validation.go +++ b/fsm/types/requests/signature_proposal_validation.go @@ -23,9 +23,13 @@ func (r *SignatureProposalParticipantsListRequest) Validate() error { if len(participant.PubKey) < 10 { return errors.New("{PubKey} too short") } + + if len(participant.DkgPubKey) < 10 { + return errors.New("{DkgPubKey} too short") + } } - if r.CreatedAt == nil { + if r.CreatedAt.IsZero() { return errors.New("{CreatedAt} cannot be a nil") } @@ -41,7 +45,7 @@ func (r *SignatureProposalParticipantRequest) Validate() error { return errors.New("{DecryptedInvitation} cannot zero length") } - if r.CreatedAt == nil { + if r.CreatedAt.IsZero() { return errors.New("{CreatedAt} cannot be a nil") } return nil diff --git a/fsm/types/responses/dkg_proposal.go b/fsm/types/responses/dkg_proposal.go index 18dcdb2..ee4dd94 100644 --- a/fsm/types/responses/dkg_proposal.go +++ b/fsm/types/responses/dkg_proposal.go @@ -1,13 +1,5 @@ package responses -type DKGProposalPubKeyParticipantResponse []*DKGProposalPubKeyParticipantEntry - -type DKGProposalPubKeyParticipantEntry struct { - ParticipantId int - Title string - PubKey []byte -} - type DKGProposalCommitParticipantResponse []*DKGProposalCommitParticipantEntry type DKGProposalCommitParticipantEntry struct { diff --git a/fsm/types/responses/signature_proposal.go b/fsm/types/responses/signature_proposal.go index 855a59a..22375f1 100644 --- a/fsm/types/responses/signature_proposal.go +++ b/fsm/types/responses/signature_proposal.go @@ -22,8 +22,8 @@ type SignatureProposalParticipantInvitationEntry struct { type SignatureProposalParticipantStatusResponse []*SignatureProposalParticipantStatusEntry type SignatureProposalParticipantStatusEntry struct { - ParticipantId int - Title string - PubKeyFingerprint string - Status uint8 + ParticipantId int + Title string + DkgPubKey []byte + Status uint8 }