mirror of https://github.com/certusone/dc4bc.git
commit
bd4c22eb4e
|
@ -2,19 +2,23 @@ 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(
|
||||
"event_proposal_init",
|
||||
signature_proposal_fsm.EventInitProposal,
|
||||
requests.SignatureProposalParticipantsListRequest{
|
||||
Participants: []*requests.SignatureProposalParticipantsEntry{
|
||||
{
|
||||
"John Doe",
|
||||
[]byte("pubkey123123"),
|
||||
|
@ -28,8 +32,14 @@ func main() {
|
|||
[]byte("pubkey789789"),
|
||||
},
|
||||
},
|
||||
CreatedAt: &tm,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Println("Err", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Dump", string(dump))
|
||||
|
||||
processResponse(resp)
|
||||
|
|
111
fsm/fsm/fsm.go
111
fsm/fsm/fsm.go
|
@ -21,6 +21,10 @@ import (
|
|||
const (
|
||||
StateGlobalIdle = State("__idle")
|
||||
StateGlobalDone = State("__done")
|
||||
|
||||
EventRunDefault EventRunMode = iota
|
||||
EventRunBefore
|
||||
EventRunAfter
|
||||
)
|
||||
|
||||
type State string
|
||||
|
@ -35,6 +39,12 @@ func (e *Event) String() string {
|
|||
return string(*e)
|
||||
}
|
||||
|
||||
func (e *Event) IsEmpty() bool {
|
||||
return e.String() == ""
|
||||
}
|
||||
|
||||
type EventRunMode uint8
|
||||
|
||||
// Response returns result for processing with clientMocks events
|
||||
type Response struct {
|
||||
// Returns machine execution result state
|
||||
|
@ -51,6 +61,8 @@ type FSM struct {
|
|||
// May be mapping must require pair source + event?
|
||||
transitions map[trKey]*trEvent
|
||||
|
||||
autoTransitions map[State]*trEvent
|
||||
|
||||
callbacks Callbacks
|
||||
|
||||
initialEvent Event
|
||||
|
@ -76,7 +88,8 @@ type trEvent struct {
|
|||
event Event
|
||||
dstState State
|
||||
isInternal bool
|
||||
isDstInit bool
|
||||
isAuto bool
|
||||
runMode EventRunMode
|
||||
}
|
||||
|
||||
type EventDesc struct {
|
||||
|
@ -90,11 +103,13 @@ type EventDesc struct {
|
|||
// Internal events, cannot be emitted from external call
|
||||
IsInternal bool
|
||||
|
||||
// Set dst state before execute action
|
||||
IsDstInit bool
|
||||
// Event must run without manual call
|
||||
IsAuto bool
|
||||
|
||||
AutoRunMode EventRunMode
|
||||
}
|
||||
|
||||
type Callback func(event Event, args ...interface{}) (interface{}, error)
|
||||
type Callback func(event Event, args ...interface{}) (Event, interface{}, error)
|
||||
|
||||
type Callbacks map[Event]Callback
|
||||
|
||||
|
@ -121,6 +136,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
|||
currentState: initialState,
|
||||
initialState: initialState,
|
||||
transitions: make(map[trKey]*trEvent),
|
||||
autoTransitions: make(map[State]*trEvent),
|
||||
finStates: make(map[State]bool),
|
||||
callbacks: make(map[Event]Callback),
|
||||
}
|
||||
|
@ -173,20 +189,41 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
|||
panic("duplicate dst for pair `source + event`")
|
||||
}
|
||||
|
||||
f.transitions[tKey] = &trEvent{
|
||||
trEvent := &trEvent{
|
||||
tKey.event,
|
||||
event.DstState,
|
||||
event.IsInternal,
|
||||
event.IsDstInit,
|
||||
event.IsAuto,
|
||||
event.AutoRunMode,
|
||||
}
|
||||
|
||||
if trEvent.isAuto && trEvent.runMode == EventRunDefault {
|
||||
trEvent.runMode = EventRunAfter
|
||||
}
|
||||
|
||||
f.transitions[tKey] = trEvent
|
||||
|
||||
// For using provider, event must use with IsGlobal = true
|
||||
if sourceState == initialState {
|
||||
if f.initialEvent != "" {
|
||||
panic("machine entry event already exist")
|
||||
}
|
||||
if f.initialEvent == "" {
|
||||
f.initialEvent = event.Name
|
||||
}
|
||||
}
|
||||
|
||||
if event.IsAuto {
|
||||
if event.AutoRunMode != EventRunBefore && event.AutoRunMode != EventRunAfter {
|
||||
panic("{AutoRunMode} not set for auto event")
|
||||
}
|
||||
|
||||
if _, ok := f.autoTransitions[sourceState]; ok {
|
||||
panic(fmt.Sprintf(
|
||||
"auto event \"%s\" already exists for state \"%s\"",
|
||||
event.Name,
|
||||
sourceState,
|
||||
))
|
||||
}
|
||||
f.autoTransitions[sourceState] = trEvent
|
||||
}
|
||||
|
||||
allSources[sourceState] = true
|
||||
trimmedSourcesCounter++
|
||||
|
@ -252,33 +289,75 @@ func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) {
|
|||
return f.do(trEvent, args...)
|
||||
}
|
||||
func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) {
|
||||
var outEvent Event
|
||||
// f.eventMu.Lock()
|
||||
// defer f.eventMu.Unlock()
|
||||
|
||||
if trEvent.isDstInit {
|
||||
err = f.SetState(trEvent.event)
|
||||
if err != nil {
|
||||
resp = &Response{
|
||||
// Process auto event
|
||||
if autoEvent, ok := f.autoTransitions[f.State()]; ok {
|
||||
autoEventResp := &Response{
|
||||
State: f.State(),
|
||||
}
|
||||
return resp, err
|
||||
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
|
||||
}
|
||||
}
|
||||
outEvent = ""
|
||||
}
|
||||
|
||||
resp = &Response{
|
||||
State: f.State(),
|
||||
}
|
||||
|
||||
if callback, ok := f.callbacks[trEvent.event]; ok {
|
||||
resp.Data, err = callback(trEvent.event, args...)
|
||||
outEvent, resp.Data, err = callback(trEvent.event, args...)
|
||||
// Do not try change state on error
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
if !trEvent.isDstInit {
|
||||
// Set state when callback executed
|
||||
if outEvent.IsEmpty() || trEvent.event == outEvent {
|
||||
err = f.SetState(trEvent.event)
|
||||
} else {
|
||||
err = f.SetState(outEvent)
|
||||
}
|
||||
|
||||
// Process auto event
|
||||
if autoEvent, ok := f.autoTransitions[f.State()]; ok {
|
||||
autoEventResp := &Response{
|
||||
State: f.State(),
|
||||
}
|
||||
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()
|
||||
|
|
|
@ -48,14 +48,14 @@ var (
|
|||
}
|
||||
|
||||
testingCallbacks = Callbacks{
|
||||
eventInit: func(event Event, args ...interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
eventInit: func(event Event, args ...interface{}) (Event, interface{}, error) {
|
||||
return event, nil, nil
|
||||
},
|
||||
eventInternalOut2: func(event Event, args ...interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
eventInternalOut2: func(event Event, args ...interface{}) (Event, interface{}, error) {
|
||||
return event, nil, nil
|
||||
},
|
||||
eventProcess: func(event Event, args ...interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
eventProcess: func(event Event, args ...interface{}) (Event, interface{}, error) {
|
||||
return event, nil, nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ package fsm_pool
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
)
|
||||
|
||||
|
@ -23,6 +22,8 @@ type MachineProvider interface {
|
|||
|
||||
GlobalInitialEvent() fsm.Event
|
||||
|
||||
EntryEvent() fsm.Event
|
||||
|
||||
EventsList() []fsm.Event
|
||||
|
||||
StatesSourcesList() []fsm.State
|
||||
|
@ -42,6 +43,7 @@ type FSMPool struct {
|
|||
mapper FSMMapper
|
||||
events FSMEventsMapper
|
||||
states FSMStatesMapper
|
||||
entryEvents map[fsm.State]fsm.Event
|
||||
}
|
||||
|
||||
func Init(machines ...MachineProvider) *FSMPool {
|
||||
|
@ -52,6 +54,7 @@ func Init(machines ...MachineProvider) *FSMPool {
|
|||
mapper: make(FSMMapper),
|
||||
events: make(FSMEventsMapper),
|
||||
states: make(FSMStatesMapper),
|
||||
entryEvents: make(map[fsm.State]fsm.Event),
|
||||
}
|
||||
|
||||
allInitStatesMap := make(map[fsm.State]string)
|
||||
|
@ -84,7 +87,7 @@ func Init(machines ...MachineProvider) *FSMPool {
|
|||
}
|
||||
|
||||
// Setup entry event for machines pool if available
|
||||
if initialEvent := machine.GlobalInitialEvent(); initialEvent != "" {
|
||||
if initialEvent := machine.GlobalInitialEvent(); !initialEvent.IsEmpty() {
|
||||
if p.fsmInitialEvent != "" {
|
||||
panic("duplicate entry event initialization")
|
||||
}
|
||||
|
@ -92,6 +95,10 @@ func Init(machines ...MachineProvider) *FSMPool {
|
|||
p.fsmInitialEvent = initialEvent
|
||||
}
|
||||
|
||||
if machineEntryEvent := machine.EntryEvent(); !machineEntryEvent.IsEmpty() {
|
||||
p.entryEvents[machine.InitialState()] = machineEntryEvent
|
||||
}
|
||||
|
||||
p.mapper[machineName] = machine
|
||||
}
|
||||
|
||||
|
@ -153,3 +160,17 @@ 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
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ const (
|
|||
var (
|
||||
testing1Events = []fsm.EventDesc{
|
||||
// Init
|
||||
{Name: eventFSM1Init, SrcState: []fsm.State{fsm1StateInit}, DstState: fsm1StateStage1, IsDstInit: true},
|
||||
{Name: eventFSM1Init, SrcState: []fsm.State{fsm1StateInit}, DstState: fsm1StateStage1, IsAuto: true, AutoRunMode: fsm.EventRunAfter},
|
||||
{Name: eventFSM1Internal, SrcState: []fsm.State{fsm1StateStage1}, DstState: fsm1StateStage2, IsInternal: true},
|
||||
|
||||
// Cancellation events
|
||||
|
@ -73,21 +73,24 @@ func NewFSM1() MachineProvider {
|
|||
return machine
|
||||
}
|
||||
|
||||
func (m *testMachineFSM1) actionFSM1SetUpData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
func (m *testMachineFSM1) actionFSM1SetUpData(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
m.data = testVal1
|
||||
return m.DoInternal(eventFSM1Internal)
|
||||
outEvent = eventFSM1Internal
|
||||
return
|
||||
}
|
||||
|
||||
func (m *testMachineFSM1) actionFSM1ProcessData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
func (m *testMachineFSM1) actionFSM1ProcessData(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
if len(args) == 1 {
|
||||
if val, ok := args[0].(int); ok {
|
||||
m.data -= val
|
||||
}
|
||||
}
|
||||
return m.data, nil
|
||||
|
||||
response = m.data
|
||||
return
|
||||
}
|
||||
|
||||
func (m *testMachineFSM1) actionFSM1EmitOut2(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
func (m *testMachineFSM1) actionFSM1EmitOut2(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,54 +1,272 @@
|
|||
package dkg_proposal_fsm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"log"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
)
|
||||
|
||||
// Pub keys
|
||||
func (m *DKGProposalFSM) actionDKGPubKeysSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGPubKeysSent")
|
||||
|
||||
func (m *DKGProposalFSM) actionPubKeyPrepareConfirmations(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGPubKeyConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGPubKeyConfirmationReceived")
|
||||
return
|
||||
}
|
||||
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}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalPubKeyConfirmationRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalPubKeyConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.PublicKey, request.PubKey)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGPubKeyConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGPubKeyConfirmationError")
|
||||
return
|
||||
}
|
||||
|
||||
// Commits
|
||||
func (m *DKGProposalFSM) actionDKGCommitsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGCommitsSent")
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGCommitConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGCommitConfirmationReceived")
|
||||
return
|
||||
}
|
||||
func (m *DKGProposalFSM) actionCommitConfirmationReceived(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 {DKGProposalCommitConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalCommitConfirmationRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalCommitConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Commit, request.Commit)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.CommitConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGCommitConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGCommitConfirmationError")
|
||||
return
|
||||
}
|
||||
|
||||
// Deals
|
||||
func (m *DKGProposalFSM) actionDKGDealsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGDealsSent")
|
||||
|
||||
func (m *DKGProposalFSM) actionDealConfirmationReceived(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 {DKGProposalDealConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalDealConfirmationRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalDealConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Deal, request.Deal)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.DealConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGDealConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGDealConfirmationReceived")
|
||||
// Responses
|
||||
|
||||
func (m *DKGProposalFSM) actionResponseConfirmationReceived(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 {DKGProposalResponseConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalResponseConfirmationRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalResponseConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Response, request.Response)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionDKGDealConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDKGDealConfirmationError")
|
||||
// Errors
|
||||
func (m *DKGProposalFSM) actionConfirmationError(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 {DKGProposalConfirmationErrorRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalConfirmationErrorRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalConfirmationErrorRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Move to methods
|
||||
switch inEvent {
|
||||
case EventDKGPubKeyConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.PubKeyConAwaitConfirmation:
|
||||
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:
|
||||
dkgProposalParticipant.Status = internal.CommitConfirmationError
|
||||
case internal.CommitConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.CommitConfirmationError:
|
||||
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.CommitConfirmationError))
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
))
|
||||
}
|
||||
case EventDKGDealConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.DealAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||
case internal.DealConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.DealConfirmationError:
|
||||
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.DealConfirmationError))
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
))
|
||||
}
|
||||
case EventDKGResponseConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.ResponseAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||
case internal.ResponseConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.ResponseConfirmationError:
|
||||
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.ResponseConfirmationError))
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
))
|
||||
}
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf("{%s} event cannot be used for action {actionConfirmationError}", inEvent))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
|
||||
// TODO: Add outEvent
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package dkg_proposal_fsm
|
|
@ -3,46 +3,47 @@ package dkg_proposal_fsm
|
|||
import (
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
fsmName = "dkg_proposal_fsm"
|
||||
FsmName = "dkg_proposal_fsm"
|
||||
|
||||
StateDkgInitial = signature_proposal_fsm.StateValidationCompleted
|
||||
// Sending dkg pub keys
|
||||
StateDkgPubKeysSendingRequired = fsm.State("state_dkg_pub_keys_sending_required")
|
||||
StateDkgInitial = StateDkgPubKeysAwaitConfirmations
|
||||
|
||||
StateDkgPubKeysSendingAwaitConfirmations = fsm.State("state_dkg_pub_keys_sending_await_confirmations")
|
||||
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgPubKeysSendingCancelled = fsm.State("state_dkg_pub_keys_sending_cancelled")
|
||||
StateDkgPubKeysSendingCancelledByTimeout = fsm.State("state_dkg_pub_keys_sending_cancelled_by_timeout")
|
||||
StateDkgPubKeysAwaitCancelled = fsm.State("state_dkg_pub_keys_await_cancelled")
|
||||
StateDkgPubKeysAwaitCancelledByTimeout = fsm.State("state_dkg_pub_keys_await_cancelled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgPubKeysSendingConfirmed = fsm.State("state_dkg_pub_keys_sending_confirmed")
|
||||
StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
|
||||
|
||||
// Sending dkg commits
|
||||
StateDkgCommitsSendingRequired = fsm.State("state_dkg_commits_sending_required")
|
||||
StateDkgCommitsSendingAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
|
||||
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgCommitsSendingCancelled = fsm.State("state_dkg_commits_sending_cancelled")
|
||||
StateDkgCommitsSendingCancelledByTimeout = fsm.State("state_dkg_commits_sending_cancelled_by_timeout")
|
||||
StateDkgCommitsAwaitCancelled = fsm.State("state_dkg_commits_await_cancelled")
|
||||
StateDkgCommitsAwaitCancelledByTimeout = fsm.State("state_dkg_commits_await_cancelled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgCommitsSendingConfirmed = fsm.State("state_dkg_commits_sending_confirmed")
|
||||
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
|
||||
|
||||
// Sending dkg deals
|
||||
StateDkgDealsSendingRequired = fsm.State("state_dkg_deals_sending_required")
|
||||
StateDkgDealsSendingAwaitConfirmations = fsm.State("state_dkg_deals_sending_await_confirmations")
|
||||
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgDealsSendingCancelled = fsm.State("state_dkg_deals_sending_cancelled")
|
||||
StateDkgDealsSendingCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
|
||||
StateDkgDealsAwaitCancelled = fsm.State("state_dkg_deals_await_cancelled")
|
||||
StateDkgDealsAwaitCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgDealsSendingConfirmed = fsm.State("state_dkg_deals_sending_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")
|
||||
// Confirmed
|
||||
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed")
|
||||
|
||||
// Events
|
||||
EventDKGPubKeysSendingRequiredInternal = fsm.Event("event_dkg_pub_key_sending_required_internal")
|
||||
eventDKGPubKeysSendingRequiredAuto = fsm.Event("event_dkg_pub_key_sending_required_internal")
|
||||
|
||||
EventDKGPubKeysSent = fsm.Event("event_dkg_pub_keys_sent")
|
||||
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")
|
||||
|
@ -50,7 +51,6 @@ const (
|
|||
|
||||
EventDKGCommitsSendingRequiredInternal = fsm.Event("event_dkg_commits_sending_required_internal")
|
||||
|
||||
EventDKGCommitsSent = fsm.Event("event_dkg_commits_sent")
|
||||
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")
|
||||
|
@ -58,11 +58,16 @@ const (
|
|||
|
||||
EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal")
|
||||
|
||||
EventDKGDealsSent = fsm.Event("event_dkg_deals_sent")
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
|
||||
)
|
||||
|
||||
|
@ -76,60 +81,66 @@ func New() internal.DumpedMachineProvider {
|
|||
machine := &DKGProposalFSM{}
|
||||
|
||||
machine.FSM = fsm.MustNewFSM(
|
||||
fsmName,
|
||||
FsmName,
|
||||
StateDkgInitial,
|
||||
[]fsm.EventDesc{
|
||||
|
||||
// Init
|
||||
// Switch to pub keys required
|
||||
{Name: EventDKGPubKeysSendingRequiredInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysSendingRequired, IsInternal: true},
|
||||
// {Name: eventDKGPubKeysSendingRequiredAuto, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunAfter},
|
||||
|
||||
// Pub keys sending
|
||||
{Name: EventDKGPubKeysSent, SrcState: []fsm.State{StateDkgPubKeysSendingRequired}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
|
||||
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
|
||||
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelled},
|
||||
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelledByTimeout, IsInternal: true},
|
||||
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelled},
|
||||
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Confirmed
|
||||
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingConfirmed, IsInternal: true},
|
||||
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmed, IsInternal: true},
|
||||
|
||||
// Switch to commits required
|
||||
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysSendingConfirmed}, DstState: StateDkgCommitsSendingRequired, IsInternal: true},
|
||||
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Commits
|
||||
{Name: EventDKGCommitsSent, SrcState: []fsm.State{StateDkgCommitsSendingRequired}, DstState: StateDkgCommitsSendingAwaitConfirmations},
|
||||
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingAwaitConfirmations},
|
||||
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelled},
|
||||
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelledByTimeout, IsInternal: true},
|
||||
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelled},
|
||||
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Confirmed
|
||||
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingConfirmed, IsInternal: true},
|
||||
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmed, IsInternal: true},
|
||||
|
||||
// Switch to deals required
|
||||
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsSendingConfirmed}, DstState: StateDkgDealsSendingRequired, IsInternal: true},
|
||||
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Deals
|
||||
{Name: EventDKGDealsSent, SrcState: []fsm.State{StateDkgDealsSendingRequired}, DstState: StateDkgDealsSendingAwaitConfirmations},
|
||||
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingAwaitConfirmations},
|
||||
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelled},
|
||||
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelledByTimeout, IsInternal: true},
|
||||
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelled},
|
||||
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelledByTimeout, IsInternal: true},
|
||||
|
||||
// Switch to responses required
|
||||
{Name: EventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Deals
|
||||
{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},
|
||||
|
||||
// Done
|
||||
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
|
||||
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
|
||||
},
|
||||
fsm.Callbacks{
|
||||
EventDKGPubKeysSent: machine.actionDKGPubKeysSent,
|
||||
EventDKGPubKeyConfirmationReceived: machine.actionDKGPubKeyConfirmationReceived,
|
||||
EventDKGPubKeyConfirmationError: machine.actionDKGPubKeyConfirmationError,
|
||||
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
|
||||
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
|
||||
|
||||
EventDKGCommitsSent: machine.actionDKGCommitsSent,
|
||||
EventDKGCommitConfirmationReceived: machine.actionDKGCommitConfirmationReceived,
|
||||
EventDKGCommitConfirmationError: machine.actionDKGCommitConfirmationError,
|
||||
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
|
||||
EventDKGCommitConfirmationError: machine.actionConfirmationError,
|
||||
|
||||
EventDKGDealsSent: machine.actionDKGDealsSent,
|
||||
EventDKGDealConfirmationReceived: machine.actionDKGDealConfirmationReceived,
|
||||
EventDKGDealConfirmationError: machine.actionDKGDealConfirmationError,
|
||||
EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived,
|
||||
EventDKGDealConfirmationError: machine.actionConfirmationError,
|
||||
|
||||
EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived,
|
||||
EventDKGResponseConfirmationError: machine.actionConfirmationError,
|
||||
},
|
||||
)
|
||||
return machine
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package internal
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota
|
||||
|
@ -17,7 +20,7 @@ type SignatureProposalParticipant struct {
|
|||
// Public title for address, such as name, nickname, organization
|
||||
ParticipantId int
|
||||
Title string
|
||||
PublicKey []byte
|
||||
PublicKey *rsa.PublicKey
|
||||
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
|
||||
InvitationSecret string
|
||||
Status SignatureProposalParticipantStatus
|
||||
|
@ -31,12 +34,22 @@ type SignatureProposalQuorum map[string]SignatureProposalParticipant
|
|||
type SignatureProposalParticipantStatus uint8
|
||||
|
||||
const (
|
||||
PubKeyConAwaitConfirmation DKGProposalParticipantStatus = iota
|
||||
SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota
|
||||
SignatureConfirmationConfirmed
|
||||
SignatureConfirmationDeclined
|
||||
SignatureConfirmationError
|
||||
PubKeyConAwaitConfirmation
|
||||
PubKeyConfirmed
|
||||
PubKeyConfirmationError
|
||||
CommitAwaitConfirmation
|
||||
CommitConfirmed
|
||||
CommitConfirmationError
|
||||
DealAwaitConfirmation
|
||||
DealConfirmed
|
||||
DealConfirmationError
|
||||
ResponseAwaitConfirmation
|
||||
ResponseConfirmed
|
||||
ResponseConfirmationError
|
||||
)
|
||||
|
||||
type DKGProposal struct {
|
||||
|
@ -50,6 +63,7 @@ type DKGProposalParticipant struct {
|
|||
PublicKey []byte
|
||||
Commit []byte
|
||||
Deal []byte
|
||||
Response []byte
|
||||
Status DKGProposalParticipantStatus
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
@ -57,3 +71,42 @@ type DKGProposalParticipant struct {
|
|||
type DKGProposalQuorum map[int]DKGProposalParticipant
|
||||
|
||||
type DKGProposalParticipantStatus uint8
|
||||
|
||||
func (s DKGProposalParticipantStatus) String() string {
|
||||
var str = "undefined"
|
||||
switch s {
|
||||
case SignatureConfirmationAwaitConfirmation:
|
||||
str = "SignatureConfirmationAwaitConfirmation"
|
||||
case SignatureConfirmationConfirmed:
|
||||
str = "SignatureConfirmationConfirmed"
|
||||
case SignatureConfirmationDeclined:
|
||||
str = "SignatureConfirmationDeclined"
|
||||
case SignatureConfirmationError:
|
||||
str = "SignatureConfirmationError"
|
||||
case PubKeyConAwaitConfirmation:
|
||||
str = "PubKeyConAwaitConfirmation"
|
||||
case PubKeyConfirmed:
|
||||
str = "PubKeyConfirmed"
|
||||
case PubKeyConfirmationError:
|
||||
str = "PubKeyConfirmationError"
|
||||
case CommitAwaitConfirmation:
|
||||
str = "CommitAwaitConfirmation"
|
||||
case CommitConfirmed:
|
||||
str = "CommitConfirmed"
|
||||
case CommitConfirmationError:
|
||||
str = "CommitConfirmationError"
|
||||
case DealAwaitConfirmation:
|
||||
str = "DealAwaitConfirmation"
|
||||
case DealConfirmed:
|
||||
str = "DealConfirmed"
|
||||
case DealConfirmationError:
|
||||
str = "DealConfirmationError"
|
||||
case ResponseAwaitConfirmation:
|
||||
str = "ResponseAwaitConfirmation"
|
||||
case ResponseConfirmed:
|
||||
str = "ResponseConfirmed"
|
||||
case ResponseConfirmationError:
|
||||
str = "ResponseConfirmationError"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -35,11 +35,12 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
// Transaction id required for unique identify dump
|
||||
func Create(tid string) (*FSMInstance, error) {
|
||||
// Create new fsm with unique id
|
||||
// transactionId required for unique identify dump
|
||||
func Create(transactionId string) (*FSMInstance, error) {
|
||||
var err error
|
||||
i := &FSMInstance{}
|
||||
err = i.InitDump(tid)
|
||||
err = i.InitDump(transactionId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -51,12 +52,20 @@ func Create(tid string) (*FSMInstance, error) {
|
|||
return i, err
|
||||
}
|
||||
|
||||
// Get fsm from dump
|
||||
func FromDump(data []byte) (*FSMInstance, error) {
|
||||
var err error
|
||||
|
||||
i := &FSMInstance{}
|
||||
if len(data) < 2 {
|
||||
return nil, errors.New("machine dump is empty")
|
||||
}
|
||||
|
||||
i := &FSMInstance{
|
||||
dump: &FSMDump{},
|
||||
}
|
||||
err = i.dump.Unmarshal(data)
|
||||
|
||||
// TODO: Add logger
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot read machine dump")
|
||||
}
|
||||
|
@ -85,21 +94,21 @@ func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Resp
|
|||
return result, dump, err
|
||||
}
|
||||
|
||||
func (i *FSMInstance) InitDump(tid string) error {
|
||||
func (i *FSMInstance) InitDump(transactionId string) error {
|
||||
if i.dump != nil {
|
||||
return errors.New("dump already initialized")
|
||||
}
|
||||
|
||||
tid = strings.TrimSpace(tid)
|
||||
transactionId = strings.TrimSpace(transactionId)
|
||||
|
||||
if tid == "" {
|
||||
if transactionId == "" {
|
||||
return errors.New("empty transaction id")
|
||||
}
|
||||
|
||||
i.dump = &FSMDump{
|
||||
State: fsm.StateGlobalIdle,
|
||||
Payload: &internal.DumpedMachineStatePayload{
|
||||
TransactionId: tid,
|
||||
TransactionId: transactionId,
|
||||
ConfirmationProposalPayload: nil,
|
||||
DKGProposalPayload: nil,
|
||||
},
|
||||
|
@ -114,5 +123,9 @@ 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 json.Unmarshal(data, d)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
package state_machines
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
testTransactionId = "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257"
|
||||
)
|
||||
|
||||
type testExternalParticipants struct {
|
||||
Title string
|
||||
PrivKey *rsa.PrivateKey
|
||||
PubKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
var (
|
||||
tm = time.Now()
|
||||
|
||||
testParticipants = map[string]*testExternalParticipants{}
|
||||
|
||||
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
|
||||
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
||||
CreatedAt: &tm,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
r := rand.Reader
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
key, err := rsa.GenerateKey(r, 2048)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Cannot generate key for user:", err)
|
||||
return
|
||||
}
|
||||
|
||||
key.Precompute()
|
||||
|
||||
marshaledPubKey := x509.MarshalPKCS1PublicKey(&key.PublicKey)
|
||||
hash := sha1.Sum(marshaledPubKey)
|
||||
|
||||
fingerprint := base64.StdEncoding.EncodeToString(hash[:])
|
||||
|
||||
participant := &testExternalParticipants{
|
||||
Title: fmt.Sprintf("User %d", i),
|
||||
PrivKey: key,
|
||||
PubKey: &key.PublicKey,
|
||||
}
|
||||
testParticipants[fingerprint] = participant
|
||||
}
|
||||
|
||||
participantsForRequest := make([]*requests.SignatureProposalParticipantsEntry, 0)
|
||||
|
||||
for _, participant := range testParticipants {
|
||||
|
||||
participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{
|
||||
Title: participant.Title,
|
||||
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
|
||||
})
|
||||
}
|
||||
testParticipantsListRequest.Participants = participantsForRequest
|
||||
}
|
||||
|
||||
func TestCreate_Positive(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
|
||||
if testFSMInstance == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_Negative(t *testing.T) {
|
||||
_, err := Create("")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for empty {transactionId}")
|
||||
}
|
||||
}
|
||||
|
||||
func compareErrNil(t *testing.T, got error) {
|
||||
if got != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", got)
|
||||
}
|
||||
}
|
||||
|
||||
func compareFSMInstanceNotNil(t *testing.T, got *FSMInstance) {
|
||||
if got == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
}
|
||||
|
||||
func compareDumpNotZero(t *testing.T, got []byte) {
|
||||
if len(got) == 0 {
|
||||
t.Fatalf("expected non zero dump, when executed without error")
|
||||
}
|
||||
}
|
||||
|
||||
func compareFSMResponseNotNil(t *testing.T, got *fsm.Response) {
|
||||
if got == nil {
|
||||
t.Fatalf("expected {*fsm.FSMResponse} got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func compareState(t *testing.T, expected fsm.State, got fsm.State) {
|
||||
if got != expected {
|
||||
t.Fatalf("expected state {%s} got {%s}", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Workflow(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if testFSMInstance.machine.Name() != spf.FsmName {
|
||||
t.Fatalf("expected machine name {%s}", spf.FsmName)
|
||||
}
|
||||
|
||||
compareState(t, spf.StateParticipantsConfirmationsInit, testFSMInstance.machine.State())
|
||||
|
||||
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||
|
||||
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("expected response {SignatureProposalParticipantInvitationsResponse}")
|
||||
}
|
||||
|
||||
if len(testParticipantsListResponse) != len(testParticipantsListRequest.Participants) {
|
||||
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(testParticipantsListResponse))
|
||||
}
|
||||
|
||||
participantsMap := map[int]*responses.SignatureProposalParticipantInvitationEntry{}
|
||||
|
||||
for _, participant := range testParticipantsListResponse {
|
||||
if _, ok := participantsMap[participant.ParticipantId]; ok {
|
||||
t.Fatalf("expected unique {ParticipantId}")
|
||||
}
|
||||
|
||||
if participant.Title == "" {
|
||||
t.Fatalf("expected not empty {Title}")
|
||||
}
|
||||
|
||||
if participant.EncryptedInvitation == "" {
|
||||
t.Fatalf("expected not empty {DecryptedInvitation}")
|
||||
}
|
||||
|
||||
if participant.PubKeyFingerprint == "" {
|
||||
t.Fatalf("expected not empty {PubKeyFingerprint}")
|
||||
}
|
||||
|
||||
participantsMap[participant.ParticipantId] = participant
|
||||
}
|
||||
|
||||
tm = tm.Add(10 * time.Second)
|
||||
|
||||
participantsCount := len(participantsMap)
|
||||
|
||||
participantCounter := participantsCount
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
r := rand.Reader
|
||||
encrypted, err := rsa.DecryptPKCS1v15(r, testParticipants[participant.PubKeyFingerprint].PrivKey, []byte(participant.EncryptedInvitation))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("cannot encrypt {DecryptedInvitation} with private key")
|
||||
}
|
||||
|
||||
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{
|
||||
PubKeyFingerprint: participant.PubKeyFingerprint,
|
||||
DecryptedInvitation: string(encrypted),
|
||||
CreatedAt: &tm,
|
||||
})
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
if participantCounter > 0 {
|
||||
if fsmResponse.State != spf.StateAwaitParticipantsConfirmations {
|
||||
t.Fatalf("expected state {%s} got {%s}", spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||
}
|
||||
} else {
|
||||
if fsmResponse.State != dpf.StateDkgInitial {
|
||||
t.Fatalf("expected state {%s} got {%s}", dpf.StateDkgInitial, fsmResponse.State)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(fsmResponse.State, err)
|
||||
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
package signature_proposal_fsm
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
|
@ -12,19 +11,19 @@ import (
|
|||
|
||||
// init -> awaitingConfirmations
|
||||
// args: payload, signing id, participants list
|
||||
func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
func (m *SignatureProposalFSM) actionInitProposal(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("participants list required")
|
||||
err = errors.New("{arg0} required {SignatureProposalParticipantsListRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.SignatureProposalParticipantsListRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast participants list")
|
||||
err = errors.New("cannot cast {arg0} to type {SignatureProposalParticipantsListRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -34,21 +33,35 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
|
||||
m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum)
|
||||
|
||||
for participantIntId, participant := range request {
|
||||
participantId := createFingerprint(&participant.PublicKey)
|
||||
for index, participant := range request.Participants {
|
||||
participantId := createFingerprint(&participant.PubKey)
|
||||
secret, err := generateRandomString(32)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot generateRandomString")
|
||||
return inEvent, nil, errors.New("cannot generate source for {InvitationSecret}")
|
||||
}
|
||||
|
||||
parsedPubKey, err := x509.ParsePKCS1PublicKey(participant.PubKey)
|
||||
|
||||
if err != nil {
|
||||
return inEvent, nil, errors.New("cannot parse {PubKey}")
|
||||
}
|
||||
|
||||
m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{
|
||||
ParticipantId: participantIntId,
|
||||
ParticipantId: index,
|
||||
Title: participant.Title,
|
||||
PublicKey: participant.PublicKey,
|
||||
PublicKey: parsedPubKey,
|
||||
InvitationSecret: secret,
|
||||
UpdatedAt: nil,
|
||||
Status: internal.SignatureAwaitConfirmation,
|
||||
UpdatedAt: request.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Checking fo quorum length
|
||||
if len(m.payload.ConfirmationProposalPayload) != len(request.Participants) {
|
||||
err = errors.New("error with creating {SignatureProposalQuorum}")
|
||||
return
|
||||
}
|
||||
|
||||
// Make response
|
||||
|
||||
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
|
||||
|
@ -56,9 +69,10 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
for pubKeyFingerprint, proposal := range m.payload.ConfirmationProposalPayload {
|
||||
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot encryptWithPubKey")
|
||||
return inEvent, nil, errors.New("cannot encryptWithPubKey")
|
||||
}
|
||||
responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
|
||||
ParticipantId: proposal.ParticipantId,
|
||||
Title: proposal.Title,
|
||||
PubKeyFingerprint: pubKeyFingerprint,
|
||||
EncryptedInvitation: encryptedInvitationSecret,
|
||||
|
@ -67,22 +81,66 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
}
|
||||
|
||||
// Change state
|
||||
|
||||
return responseData, nil
|
||||
return inEvent, responseData, nil
|
||||
}
|
||||
|
||||
//
|
||||
func (m *SignatureProposalFSM) actionConfirmProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionConfirmProposalByParticipant")
|
||||
// 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()
|
||||
|
||||
if len(args) != 1 {
|
||||
err = errors.New("{arg0} required {SignatureProposalParticipantRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.SignatureProposalParticipantRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {SignatureProposalParticipantRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant, ok := m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint]
|
||||
|
||||
if !ok {
|
||||
err = errors.New("{PubKeyFingerprint} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
if signatureProposalParticipant.InvitationSecret != request.DecryptedInvitation {
|
||||
err = errors.New("{InvitationSecret} not match {DecryptedInvitation}")
|
||||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant.Status = internal.SignatureConfirmed
|
||||
signatureProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
|
||||
m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant
|
||||
|
||||
outEvent, response, err = m.actionValidateProposal(eventValidateProposalInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SignatureProposalFSM) actionDeclineProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDeclineProposalByParticipant")
|
||||
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) actionValidateProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionValidateProposal")
|
||||
unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload)
|
||||
for _, participant := range m.payload.ConfirmationProposalPayload {
|
||||
if participant.Status == internal.SignatureConfirmed {
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
outEvent = eventSetProposalValidatedInternal
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package signature_proposal_fsm
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum
|
|||
// Common functions
|
||||
|
||||
func createFingerprint(data *[]byte) string {
|
||||
hash := sha256.Sum256(*data)
|
||||
hash := sha1.Sum(*data)
|
||||
return base64.StdEncoding.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,13 @@ func generateRandomString(s int) (string, error) {
|
|||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
||||
|
||||
func encryptWithPubKey(key []byte, value string) (string, error) {
|
||||
return value, nil
|
||||
func encryptWithPubKey(pubKey *rsa.PublicKey, value string) (string, error) {
|
||||
r := rand.Reader
|
||||
encryptedData, err := rsa.EncryptPKCS1v15(r, pubKey, []byte(value))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(encryptedData), nil
|
||||
}
|
||||
|
|
|
@ -2,28 +2,32 @@ package signature_proposal_fsm
|
|||
|
||||
import (
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
fsmName = "signature_proposal_fsm"
|
||||
FsmName = "signature_proposal_fsm"
|
||||
signingIdLen = 32
|
||||
|
||||
StateAwaitParticipantsConfirmations = fsm.State("state_validation_await_participants_confirmations") // waiting participants
|
||||
StateParticipantsConfirmationsInit = fsm.StateGlobalIdle
|
||||
|
||||
StateValidationCanceledByParticipant = fsm.State("state_validation_canceled_by_participant")
|
||||
StateValidationCanceledByTimeout = fsm.State("state_validation_canceled_by_timeout")
|
||||
StateAwaitParticipantsConfirmations = fsm.State("state_sig_proposal_await_participants_confirmations") // waiting participants
|
||||
|
||||
StateValidationCompleted = fsm.State("state_validation_completed")
|
||||
StateValidationCanceledByParticipant = fsm.State("state_sig_proposal_canceled_by_participant")
|
||||
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
|
||||
|
||||
EventInitProposal = fsm.Event("event_proposal_init")
|
||||
EventConfirmProposal = fsm.Event("event_proposal_confirm_by_participant")
|
||||
EventDeclineProposal = fsm.Event("event_proposal_decline_by_participant")
|
||||
EventValidateProposal = fsm.Event("event_proposal_validate")
|
||||
EventSetProposalValidated = fsm.Event("event_proposal_set_validated")
|
||||
// Out state
|
||||
StateValidationCompleted = fsm.State("state_sig_proposal_completed")
|
||||
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("proposal_canceled_timeout")
|
||||
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")
|
||||
eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated")
|
||||
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
|
||||
|
||||
// Switch to next fsm
|
||||
|
||||
|
@ -39,13 +43,13 @@ func New() internal.DumpedMachineProvider {
|
|||
machine := &SignatureProposalFSM{}
|
||||
|
||||
machine.FSM = fsm.MustNewFSM(
|
||||
fsmName,
|
||||
FsmName,
|
||||
fsm.StateGlobalIdle,
|
||||
[]fsm.EventDesc{
|
||||
// {Name: "", SrcState: []string{""}, DstState: ""},
|
||||
|
||||
// Init
|
||||
{Name: EventInitProposal, SrcState: []fsm.State{fsm.StateGlobalIdle}, DstState: StateAwaitParticipantsConfirmations},
|
||||
{Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations},
|
||||
|
||||
// Validate by participants
|
||||
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
||||
|
@ -53,20 +57,20 @@ func New() internal.DumpedMachineProvider {
|
|||
// Now set for external emitting.
|
||||
{Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant},
|
||||
|
||||
{Name: EventValidateProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
||||
{Name: eventValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true},
|
||||
|
||||
// eventProposalValidate internal or from client?
|
||||
// yay
|
||||
// Exit point
|
||||
{Name: EventSetProposalValidated, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: fsm.State("state_dkg_pub_keys_sending_required"), IsInternal: true},
|
||||
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: dkg_proposal_fsm.StateDkgPubKeysAwaitConfirmations, IsInternal: true},
|
||||
// nan
|
||||
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
|
||||
},
|
||||
fsm.Callbacks{
|
||||
EventInitProposal: machine.actionInitProposal,
|
||||
EventConfirmProposal: machine.actionConfirmProposalByParticipant,
|
||||
EventDeclineProposal: machine.actionDeclineProposalByParticipant,
|
||||
EventValidateProposal: machine.actionValidateProposal,
|
||||
EventConfirmProposal: machine.actionProposalResponseByParticipant,
|
||||
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
||||
eventValidateProposalInternal: machine.actionValidateProposal,
|
||||
},
|
||||
)
|
||||
return machine
|
||||
|
|
|
@ -1,16 +1,49 @@
|
|||
package requests
|
||||
|
||||
// Event: "event_dkg_pub_keys_sent"
|
||||
type DKGProposalPubKeysSendingRequest struct {
|
||||
PubKeys map[int][]byte
|
||||
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
|
||||
}
|
||||
|
||||
// Event: "event_dkg_commits_sent"
|
||||
type DKGProposalCommitsSendingRequest struct {
|
||||
Commits map[int][]byte
|
||||
// States: "state_dkg_commits_sending_await_confirmations"
|
||||
// Events: "event_dkg_commit_confirm_received"
|
||||
type DKGProposalCommitConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Commit []byte
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
||||
// Event: "event_dkg_deals_sent"
|
||||
type DKGProposalDealsSendingRequest struct {
|
||||
Deals map[int][]byte
|
||||
// States: "state_dkg_deals_await_confirmations"
|
||||
// Events: "event_dkg_deal_confirm_received"
|
||||
type DKGProposalDealConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Deal []byte
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_responses_await_confirmations"
|
||||
// Events: "event_dkg_response_confirm_received"
|
||||
type DKGProposalResponseConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Response []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"
|
||||
//
|
||||
// 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"
|
||||
type DKGProposalConfirmationErrorRequest struct {
|
||||
ParticipantId int
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
|
|
@ -1 +1,79 @@
|
|||
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 == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DKGProposalCommitConfirmationRequest) Validate() error {
|
||||
if r.ParticipantId < 0 {
|
||||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if len(r.Commit) == 0 {
|
||||
return errors.New("{Commit} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DKGProposalDealConfirmationRequest) Validate() error {
|
||||
if r.ParticipantId < 0 {
|
||||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if len(r.Deal) == 0 {
|
||||
return errors.New("{Deal} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DKGProposalResponseConfirmationRequest) Validate() error {
|
||||
if r.ParticipantId < 0 {
|
||||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if len(r.Response) == 0 {
|
||||
return errors.New("{Response} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DKGProposalConfirmationErrorRequest) Validate() error {
|
||||
if r.ParticipantId < 0 {
|
||||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,17 +1,28 @@
|
|||
package requests
|
||||
|
||||
import "time"
|
||||
|
||||
// Requests
|
||||
|
||||
type SignatureProposalParticipantsListRequest []SignatureProposalParticipantsEntry
|
||||
// States: "__idle"
|
||||
// Events: "event_sig_proposal_init"
|
||||
type SignatureProposalParticipantsListRequest struct {
|
||||
Participants []*SignatureProposalParticipantsEntry
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
||||
type SignatureProposalParticipantsEntry struct {
|
||||
// Public title for address, such as name, nickname, organization
|
||||
Title string
|
||||
PublicKey []byte
|
||||
PubKey []byte
|
||||
}
|
||||
|
||||
// States: "__idle"
|
||||
// Events: "event_sig_proposal_confirm_by_participant"
|
||||
// "event_sig_proposal_decline_by_participant"
|
||||
type SignatureProposalParticipantRequest struct {
|
||||
// Key for link invitations to participants
|
||||
PubKeyFingerprint string
|
||||
EncryptedInvitation string
|
||||
DecryptedInvitation string
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
|
|
@ -2,31 +2,47 @@ package requests
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/config"
|
||||
)
|
||||
|
||||
func (r *SignatureProposalParticipantsListRequest) Validate() error {
|
||||
if len(*r) < config.ParticipantsMinCount {
|
||||
return errors.New("too few participants")
|
||||
if len(r.Participants) < config.ParticipantsMinCount {
|
||||
return errors.New(fmt.Sprintf("too few participants, minimum is {%d}", config.ParticipantsMinCount))
|
||||
}
|
||||
|
||||
for _, participant := range *r {
|
||||
for _, participant := range r.Participants {
|
||||
if len(participant.Title) < 3 {
|
||||
return errors.New("title too short")
|
||||
return errors.New("{Title} minimum length is {3}")
|
||||
}
|
||||
|
||||
if len(participant.Title) > 150 {
|
||||
return errors.New("title too long")
|
||||
return errors.New("{Title} maximum length is {150}")
|
||||
}
|
||||
|
||||
if len(participant.PublicKey) < 10 {
|
||||
return errors.New("pub key too short")
|
||||
if len(participant.PubKey) < 10 {
|
||||
return errors.New("{PubKey} too short")
|
||||
}
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SignatureProposalParticipantRequest) Validate() error {
|
||||
if len(r.PubKeyFingerprint) == 0 {
|
||||
return errors.New("{PubKeyFingerprint} cannot zero length")
|
||||
}
|
||||
|
||||
if len(r.DecryptedInvitation) == 0 {
|
||||
return errors.New("{DecryptedInvitation} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package responses
|
||||
|
||||
// Responses
|
||||
// Response
|
||||
|
||||
const (
|
||||
ProposalConfirmationStatusIdle = iota
|
||||
|
|
Loading…
Reference in New Issue