diff --git a/fsm/cmd/test/test.go b/fsm/cmd/test/test.go index d18e424..cc7c48b 100644 --- a/fsm/cmd/test/test.go +++ b/fsm/cmd/test/test.go @@ -10,11 +10,10 @@ import ( ) func main() { - fsmMachine, err := state_machines.New([]byte{}) + fsmMachine, err := state_machines.Create("d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257") log.Println(fsmMachine, err) resp, dump, err := fsmMachine.Do( "event_proposal_init", - "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257", requests.SignatureProposalParticipantsListRequest{ { "John Doe", @@ -40,7 +39,7 @@ func main() { func processResponse(resp *fsm.Response) { switch resp.State { // Await proposals - case fsm.State("validate_proposal"): + 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) diff --git a/fsm/state_machines/dkg_proposal_fsm/actions.go b/fsm/state_machines/dkg_proposal_fsm/actions.go index 7cbe073..e9faf68 100644 --- a/fsm/state_machines/dkg_proposal_fsm/actions.go +++ b/fsm/state_machines/dkg_proposal_fsm/actions.go @@ -6,49 +6,49 @@ import ( ) // Pub keys -func (s *DKGProposalFSM) actionDKGPubKeysSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGPubKeysSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGPubKeysSent") return } -func (s *DKGProposalFSM) actionDKGPubKeyConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGPubKeyConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGPubKeyConfirmationReceived") return } -func (s *DKGProposalFSM) actionDKGPubKeyConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGPubKeyConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGPubKeyConfirmationError") return } // Commits -func (s *DKGProposalFSM) actionDKGCommitsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGCommitsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGCommitsSent") return } -func (s *DKGProposalFSM) actionDKGCommitConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGCommitConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGCommitConfirmationReceived") return } -func (s *DKGProposalFSM) actionDKGCommitConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGCommitConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGCommitConfirmationError") return } // Deals -func (s *DKGProposalFSM) actionDKGDealsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGDealsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGDealsSent") return } -func (s *DKGProposalFSM) actionDKGDealConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGDealConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGDealConfirmationReceived") return } -func (s *DKGProposalFSM) actionDKGDealConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *DKGProposalFSM) actionDKGDealConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDKGDealConfirmationError") return } diff --git a/fsm/state_machines/dkg_proposal_fsm/init.go b/fsm/state_machines/dkg_proposal_fsm/init.go index f03d5cc..b6d35f1 100644 --- a/fsm/state_machines/dkg_proposal_fsm/init.go +++ b/fsm/state_machines/dkg_proposal_fsm/init.go @@ -2,8 +2,9 @@ package dkg_proposal_fsm import ( "github.com/depools/dc4bc/fsm/fsm" - "github.com/depools/dc4bc/fsm/fsm_pool" + "github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm" + "sync" ) const ( @@ -67,9 +68,11 @@ const ( type DKGProposalFSM struct { *fsm.FSM + payload *internal.DumpedMachineStatePayload + payloadMu sync.RWMutex } -func New() fsm_pool.MachineProvider { +func New() internal.DumpedMachineProvider { machine := &DKGProposalFSM{} machine.FSM = fsm.MustNewFSM( @@ -131,3 +134,10 @@ func New() fsm_pool.MachineProvider { ) return machine } + +func (m *DKGProposalFSM) SetUpPayload(payload *internal.DumpedMachineStatePayload) { + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + m.payload = payload +} diff --git a/fsm/state_machines/internal/provider.go b/fsm/state_machines/internal/provider.go index e8c0e0f..f771aba 100644 --- a/fsm/state_machines/internal/provider.go +++ b/fsm/state_machines/internal/provider.go @@ -1,13 +1,14 @@ package internal -type MachineStatePayload struct { - ConfirmationProposalPayload ConfirmationProposalPrivateQuorum - DKGProposalPayload DKGProposalPrivateQuorum +import "github.com/depools/dc4bc/fsm/fsm_pool" + +type DumpedMachineStatePayload struct { + TransactionId string + ConfirmationProposalPayload SignatureProposalQuorum + DKGProposalPayload DKGProposalQuorum } -// Using combine response for modify data with chain -// User value or pointer? How about memory state? -type MachineCombinedResponse struct { - Response interface{} - Payload *MachineStatePayload +type DumpedMachineProvider interface { + fsm_pool.MachineProvider + SetUpPayload(payload *DumpedMachineStatePayload) } diff --git a/fsm/state_machines/internal/types.go b/fsm/state_machines/internal/types.go index 7d98f5e..ee927b2 100644 --- a/fsm/state_machines/internal/types.go +++ b/fsm/state_machines/internal/types.go @@ -2,27 +2,58 @@ package internal import "time" -type ProposalParticipantPrivate struct { +const ( + SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota + SignatureConfirmed +) + +type ConfirmationProposal struct { + Quorum SignatureProposalQuorum + CreatedAt *time.Time + ExpiresAt *time.Time +} + +type SignatureProposalParticipant struct { // Public title for address, such as name, nickname, organization ParticipantId int Title string PublicKey []byte // For validation user confirmation: sign(InvitationSecret, PublicKey) => user InvitationSecret string - ConfirmedAt *time.Time + Status SignatureProposalParticipantStatus + UpdatedAt *time.Time } // Unique alias for map iteration - Public Key Fingerprint // Excludes array merge and rotate operations +type SignatureProposalQuorum map[string]SignatureProposalParticipant -type ConfirmationProposalPrivateQuorum map[string]ProposalParticipantPrivate +type SignatureProposalParticipantStatus uint8 -type ProposalDKGParticipantPrivate struct { +const ( + PubKeyConAwaitConfirmation DKGProposalParticipantStatus = iota + PubKeyConfirmed + CommitAwaitConfirmation + CommitConfirmed + DealAwaitConfirmation + DealConfirmed +) + +type DKGProposal struct { + Quorum map[int]DKGProposalParticipant + CreatedAt *time.Time + ExpiresAt *time.Time +} + +type DKGProposalParticipant struct { Title string PublicKey []byte Commit []byte Deal []byte + Status DKGProposalParticipantStatus UpdatedAt *time.Time } -type DKGProposalPrivateQuorum map[int]ProposalParticipantPrivate +type DKGProposalQuorum map[int]DKGProposalParticipant + +type DKGProposalParticipantStatus uint8 diff --git a/fsm/state_machines/provider.go b/fsm/state_machines/provider.go index 92850a4..26af6fc 100644 --- a/fsm/state_machines/provider.go +++ b/fsm/state_machines/provider.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm" + "strings" "github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/fsm_pool" @@ -13,13 +14,13 @@ import ( // Is machine state scope dump will be locked? type FSMDump struct { - Id string - State fsm.State - Payload internal.MachineStatePayload + TransactionId string + State fsm.State + Payload *internal.DumpedMachineStatePayload } type FSMInstance struct { - machine fsm_pool.MachineProvider + machine internal.DumpedMachineProvider dump *FSMDump } @@ -34,58 +35,76 @@ func init() { ) } -func New(data []byte) (*FSMInstance, error) { +// Transaction id required for unique identify dump +func Create(tid string) (*FSMInstance, error) { var err error i := &FSMInstance{} - if len(data) == 0 { - i.InitDump() - i.machine, err = fsmPoolProvider.EntryPointMachine() - return i, err // Create machine + err = i.InitDump(tid) + + if err != nil { + return nil, err } + machine, err := fsmPoolProvider.EntryPointMachine() + i.machine = machine.(internal.DumpedMachineProvider) + i.machine.SetUpPayload(i.dump.Payload) + return i, err +} + +func FromDump(data []byte) (*FSMInstance, error) { + var err error + + i := &FSMInstance{} err = i.dump.Unmarshal(data) if err != nil { return nil, errors.New("cannot read machine dump") } - i.machine, err = fsmPoolProvider.MachineByState(i.dump.State) + machine, err := fsmPoolProvider.MachineByState(i.dump.State) + i.machine = machine.(internal.DumpedMachineProvider) + i.machine.SetUpPayload(i.dump.Payload) return i, err } -func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (*fsm.Response, []byte, error) { - // Provide payload as first argument ever - result, err := i.machine.Do(event, append([]interface{}{i.dump.Payload}, args...)...) +func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Response, dump []byte, err error) { + var dumpErr error + + result, err = i.machine.Do(event, args...) // On route errors result will be nil if result != nil { - - // Proxying combined response, separate payload and data - if result.Data != nil { - if r, ok := result.Data.(internal.MachineCombinedResponse); ok { - i.dump.Payload = *r.Payload - result.Data = r.Response - } else { - return nil, []byte{}, errors.New("cannot cast callback response") - } - } - i.dump.State = result.State - } - dump, dumpErr := i.dump.Marshal() - if dumpErr != nil { - return result, []byte{}, err + + dump, dumpErr = i.dump.Marshal() + if dumpErr != nil { + return result, []byte{}, err + } } return result, dump, err } -func (i *FSMInstance) InitDump() { - if i.dump == nil { - i.dump = &FSMDump{ - State: fsm.StateGlobalIdle, - } +func (i *FSMInstance) InitDump(tid string) error { + if i.dump != nil { + return errors.New("dump already initialized") } + + tid = strings.TrimSpace(tid) + + if tid == "" { + return errors.New("empty transaction id") + } + + i.dump = &FSMDump{ + State: fsm.StateGlobalIdle, + Payload: &internal.DumpedMachineStatePayload{ + TransactionId: tid, + ConfirmationProposalPayload: nil, + DKGProposalPayload: nil, + }, + } + return nil } // TODO: Add encryption diff --git a/fsm/state_machines/signature_proposal_fsm/actions.go b/fsm/state_machines/signature_proposal_fsm/actions.go index 20c3845..f852c91 100644 --- a/fsm/state_machines/signature_proposal_fsm/actions.go +++ b/fsm/state_machines/signature_proposal_fsm/actions.go @@ -12,40 +12,16 @@ import ( // init -> awaitingConfirmations // args: payload, signing id, participants list -func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) { - var payload internal.MachineStatePayload - // Init proposal - log.Println("I'm actionInitProposal") +func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) { + m.payloadMu.Lock() + defer m.payloadMu.Unlock() - if len(args) < 3 { - err = errors.New("payload and signing id required and participants list required") + if len(args) != 1 { + err = errors.New("participants list required") return } - if len(args) > 3 { - err = errors.New("too many arguments") - return - } - - payload, ok := args[0].(internal.MachineStatePayload) - - if !ok { - err = errors.New("cannot cast payload") - return - } - - signingId, ok := args[1].(string) - if !ok { - err = errors.New("cannot cast signing id, awaiting string value") - return - } - - if len(signingId) < signingIdLen { - err = errors.New("signing id to short ") - return - } - - request, ok := args[2].(requests.SignatureProposalParticipantsListRequest) + request, ok := args[0].(requests.SignatureProposalParticipantsListRequest) if !ok { err = errors.New("cannot cast participants list") @@ -56,7 +32,7 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter return } - payload.ConfirmationProposalPayload = make(internal.ConfirmationProposalPrivateQuorum) + m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum) for participantIntId, participant := range request { participantId := createFingerprint(&participant.PublicKey) @@ -64,12 +40,12 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter if err != nil { return nil, errors.New("cannot generateRandomString") } - payload.ConfirmationProposalPayload[participantId] = internal.ProposalParticipantPrivate{ + m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{ ParticipantId: participantIntId, Title: participant.Title, PublicKey: participant.PublicKey, InvitationSecret: secret, - ConfirmedAt: nil, + UpdatedAt: nil, } } @@ -77,7 +53,7 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0) - for pubKeyFingerprint, proposal := range payload.ConfirmationProposalPayload { + for pubKeyFingerprint, proposal := range m.payload.ConfirmationProposalPayload { encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret) if err != nil { return nil, errors.New("cannot encryptWithPubKey") @@ -92,24 +68,21 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter // Change state - return internal.MachineCombinedResponse{ - Response: responseData, - Payload: &payload, - }, nil + return responseData, nil } // -func (s *SignatureProposalFSM) actionConfirmProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *SignatureProposalFSM) actionConfirmProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionConfirmProposalByParticipant") return } -func (s *SignatureProposalFSM) actionDeclineProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *SignatureProposalFSM) actionDeclineProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionDeclineProposalByParticipant") return } -func (s *SignatureProposalFSM) actionValidateProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) { +func (m *SignatureProposalFSM) actionValidateProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) { log.Println("I'm actionValidateProposal") return } diff --git a/fsm/state_machines/signature_proposal_fsm/helpers.go b/fsm/state_machines/signature_proposal_fsm/helpers.go index 9ea91b7..e3edc4b 100644 --- a/fsm/state_machines/signature_proposal_fsm/helpers.go +++ b/fsm/state_machines/signature_proposal_fsm/helpers.go @@ -11,7 +11,7 @@ import ( // Request and response mutators -func ProposalParticipantsQuorumToResponse(list *internal.ConfirmationProposalPrivateQuorum) responses.SignatureProposalParticipantInvitationsResponse { +func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum) responses.SignatureProposalParticipantInvitationsResponse { var response responses.SignatureProposalParticipantInvitationsResponse for quorumId, parcipant := range *list { response = append(response, &responses.SignatureProposalParticipantInvitationEntry{ diff --git a/fsm/state_machines/signature_proposal_fsm/init.go b/fsm/state_machines/signature_proposal_fsm/init.go index c3cdd29..c13eaa6 100644 --- a/fsm/state_machines/signature_proposal_fsm/init.go +++ b/fsm/state_machines/signature_proposal_fsm/init.go @@ -2,7 +2,8 @@ package signature_proposal_fsm import ( "github.com/depools/dc4bc/fsm/fsm" - "github.com/depools/dc4bc/fsm/fsm_pool" + "github.com/depools/dc4bc/fsm/state_machines/internal" + "sync" ) const ( @@ -30,9 +31,11 @@ const ( type SignatureProposalFSM struct { *fsm.FSM + payload *internal.DumpedMachineStatePayload + payloadMu sync.RWMutex } -func New() fsm_pool.MachineProvider { +func New() internal.DumpedMachineProvider { machine := &SignatureProposalFSM{} machine.FSM = fsm.MustNewFSM( @@ -68,3 +71,10 @@ func New() fsm_pool.MachineProvider { ) return machine } + +func (m *SignatureProposalFSM) SetUpPayload(payload *internal.DumpedMachineStatePayload) { + m.payloadMu.Lock() + defer m.payloadMu.Unlock() + + m.payload = payload +}