mirror of https://github.com/certusone/dc4bc.git
Merge branch 'master' into feat/client-fsm
This commit is contained in:
commit
e5c0b082a9
|
@ -1,82 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/state_machines"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tm := time.Now()
|
||||
fsmMachine, err := state_machines.Create("d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257")
|
||||
log.Println(fsmMachine, err)
|
||||
resp, dump, err := fsmMachine.Do(
|
||||
signature_proposal_fsm.EventInitProposal,
|
||||
requests.SignatureProposalParticipantsListRequest{
|
||||
Participants: []*requests.SignatureProposalParticipantsEntry{
|
||||
{
|
||||
"John Doe",
|
||||
[]byte("pubkey123123"),
|
||||
},
|
||||
{
|
||||
"Crypto Billy",
|
||||
[]byte("pubkey456456"),
|
||||
},
|
||||
{
|
||||
"Matt",
|
||||
[]byte("pubkey789789"),
|
||||
},
|
||||
},
|
||||
CreatedAt: &tm,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Println("Err", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Dump", string(dump))
|
||||
|
||||
processResponse(resp)
|
||||
|
||||
}
|
||||
|
||||
func processResponse(resp *fsm.Response) {
|
||||
switch resp.State {
|
||||
// Await proposals
|
||||
case fsm.State("state_validation_await_participants_confirmations"):
|
||||
data, ok := resp.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||
if !ok {
|
||||
log.Printf("undefined response type for state \"%s\"\n", resp.State)
|
||||
return
|
||||
}
|
||||
sendInvitations(data)
|
||||
|
||||
case fsm.State("validation_canceled_by_participant"):
|
||||
updateDashboardWithCanceled("Participant")
|
||||
case fsm.State("validation_canceled_by_timeout"):
|
||||
updateDashboardWithCanceled("Timeout")
|
||||
default:
|
||||
log.Printf("undefined response type for state \"%s\"\n", resp.State)
|
||||
}
|
||||
}
|
||||
|
||||
func sendInvitations(invitations responses.SignatureProposalParticipantInvitationsResponse) {
|
||||
for _, invitation := range invitations {
|
||||
log.Printf(
|
||||
"Dear %s, please encrypt value \"%s\" with your key, fingerprint: %s\n",
|
||||
invitation.Title,
|
||||
invitation.EncryptedInvitation,
|
||||
invitation.PubKeyFingerprint,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func updateDashboardWithCanceled(msg string) {
|
||||
log.Printf("Breaking news! Proposal canceled with reason: %s\n", msg)
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// TODO: Move to machine level configs?
|
||||
ParticipantsMinCount = 3
|
||||
ParticipantsMinCount = 3
|
||||
SignatureProposalConfirmationDeadline = time.Hour * 24
|
||||
DkgConfirmationDeadline = time.Hour * 24
|
||||
)
|
||||
|
|
135
fsm/fsm/fsm.go
135
fsm/fsm/fsm.go
|
@ -23,6 +23,12 @@ const (
|
|||
StateGlobalDone = State("__done")
|
||||
)
|
||||
|
||||
const (
|
||||
EventRunDefault EventRunMode = iota
|
||||
EventRunBefore
|
||||
EventRunAfter
|
||||
)
|
||||
|
||||
type State string
|
||||
|
||||
func (s *State) String() string {
|
||||
|
@ -39,6 +45,8 @@ 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
|
||||
|
@ -55,6 +63,8 @@ type FSM struct {
|
|||
// May be mapping must require pair source + event?
|
||||
transitions map[trKey]*trEvent
|
||||
|
||||
autoTransitions map[State]*trEvent
|
||||
|
||||
callbacks Callbacks
|
||||
|
||||
initialEvent Event
|
||||
|
@ -77,10 +87,11 @@ type trKey struct {
|
|||
|
||||
// Transition lightweight event description
|
||||
type trEvent struct {
|
||||
event Event
|
||||
dstState State
|
||||
isInternal bool
|
||||
isStateBefore bool
|
||||
event Event
|
||||
dstState State
|
||||
isInternal bool
|
||||
isAuto bool
|
||||
runMode EventRunMode
|
||||
}
|
||||
|
||||
type EventDesc struct {
|
||||
|
@ -94,8 +105,10 @@ 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{}) (Event, interface{}, error)
|
||||
|
@ -121,12 +134,13 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
|||
}
|
||||
|
||||
f := &FSM{
|
||||
name: machineName,
|
||||
currentState: initialState,
|
||||
initialState: initialState,
|
||||
transitions: make(map[trKey]*trEvent),
|
||||
finStates: make(map[State]bool),
|
||||
callbacks: make(map[Event]Callback),
|
||||
name: machineName,
|
||||
currentState: initialState,
|
||||
initialState: initialState,
|
||||
transitions: make(map[trKey]*trEvent),
|
||||
autoTransitions: make(map[State]*trEvent),
|
||||
finStates: make(map[State]bool),
|
||||
callbacks: make(map[Event]Callback),
|
||||
}
|
||||
|
||||
allEvents := make(map[Event]bool)
|
||||
|
@ -177,19 +191,40 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
|||
panic("duplicate dst for pair `source + event`")
|
||||
}
|
||||
|
||||
f.transitions[tKey] = &trEvent{
|
||||
if event.IsAuto && event.AutoRunMode == EventRunDefault {
|
||||
event.AutoRunMode = EventRunAfter
|
||||
}
|
||||
|
||||
trEvent := &trEvent{
|
||||
tKey.event,
|
||||
event.DstState,
|
||||
event.IsInternal,
|
||||
event.IsDstInit,
|
||||
event.IsAuto,
|
||||
event.AutoRunMode,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
|
@ -212,7 +247,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
|||
}
|
||||
|
||||
if _, ok := allEvents[event]; !ok {
|
||||
panic("callback has no event")
|
||||
panic("callback has empty event")
|
||||
}
|
||||
|
||||
f.callbacks[event] = callback
|
||||
|
@ -260,14 +295,28 @@ func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err err
|
|||
// f.eventMu.Lock()
|
||||
// defer f.eventMu.Unlock()
|
||||
|
||||
if trEvent.isStateBefore {
|
||||
err = f.SetState(trEvent.event)
|
||||
if err != nil {
|
||||
resp = &Response{
|
||||
State: f.State(),
|
||||
}
|
||||
return resp, err
|
||||
// Process auto event
|
||||
if autoEvent, ok := f.autoTransitions[f.State()]; ok {
|
||||
autoEventResp := &Response{
|
||||
State: f.State(),
|
||||
}
|
||||
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{
|
||||
|
@ -282,13 +331,35 @@ func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err err
|
|||
}
|
||||
}
|
||||
|
||||
if !trEvent.isStateBefore {
|
||||
if outEvent.IsEmpty() || trEvent.event == outEvent {
|
||||
err = f.SetState(trEvent.event)
|
||||
} else {
|
||||
err = f.SetState(outEvent)
|
||||
}
|
||||
// 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()
|
||||
|
@ -311,7 +382,7 @@ func (f *FSM) SetState(event Event) error {
|
|||
|
||||
trEvent, ok := f.transitions[trKey{f.currentState, event}]
|
||||
if !ok {
|
||||
return errors.New("cannot change state")
|
||||
return errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState))
|
||||
}
|
||||
|
||||
f.currentState = trEvent.dstState
|
||||
|
|
|
@ -12,9 +12,9 @@ const (
|
|||
stateStage1 = State("state_stage1")
|
||||
// Process data
|
||||
stateStage2 = State("state_stage2")
|
||||
// Cancelled with internal event
|
||||
// Canceled with internal event
|
||||
stateCanceledByInternal = State("state_canceled")
|
||||
// Cancelled with external event
|
||||
// Canceled with external event
|
||||
stateCanceled2 = State("state_canceled2")
|
||||
// Out endpoint to switch
|
||||
stateOutToFSM2 = State("state_out_to_fsm2")
|
||||
|
@ -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
|
||||
|
@ -39,9 +40,10 @@ type FSMStatesMapper map[fsm.State]string
|
|||
type FSMPool struct {
|
||||
fsmInitialEvent fsm.Event
|
||||
// Pool mapper by names
|
||||
mapper FSMMapper
|
||||
events FSMEventsMapper
|
||||
states FSMStatesMapper
|
||||
mapper FSMMapper
|
||||
events FSMEventsMapper
|
||||
states FSMStatesMapper
|
||||
entryEvents map[fsm.State]fsm.Event
|
||||
}
|
||||
|
||||
func Init(machines ...MachineProvider) *FSMPool {
|
||||
|
@ -49,9 +51,10 @@ func Init(machines ...MachineProvider) *FSMPool {
|
|||
panic("cannot initialize empty pool")
|
||||
}
|
||||
p := &FSMPool{
|
||||
mapper: make(FSMMapper),
|
||||
events: make(FSMEventsMapper),
|
||||
states: make(FSMStatesMapper),
|
||||
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
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -23,9 +23,9 @@ const (
|
|||
fsm1StateStage1 = fsm.State("state_fsm1_stage1")
|
||||
// Process data
|
||||
fsm1StateStage2 = fsm.State("state_fsm1_stage2")
|
||||
// Cancelled with internal event
|
||||
// Canceled with internal event
|
||||
fsm1StateCanceledByInternal = fsm.State("state_fsm1_canceled")
|
||||
// Cancelled with external event
|
||||
// Canceled with external event
|
||||
fsm1StateCanceled2 = fsm.State("state_fsm1_canceled2")
|
||||
// Out endpoint to switch
|
||||
fsm1StateOutToFSM2 = fsm.State("state_fsm1_out_to_fsm2")
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -105,7 +108,7 @@ const (
|
|||
// Process data
|
||||
fsm2StateStage1 = fsm.State("state_fsm2_stage1")
|
||||
fsm2StateStage2 = fsm.State("state_fsm2_stage2")
|
||||
// Cancelled with internal event
|
||||
// Canceled with internal event
|
||||
fsm2StateCanceledByInternal = fsm.State("state_fsm2_canceled")
|
||||
// Out endpoint to switch
|
||||
fsm2StateOutToFSM3 = fsm.State("state_fsm2_out_to_fsm3")
|
||||
|
|
|
@ -3,17 +3,16 @@ package dkg_proposal_fsm
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/config"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pub keys
|
||||
|
||||
func (m *DKGProposalFSM) actionPubKeyPrepareConfirmations(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
@ -34,18 +33,72 @@ func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, arg
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.PublicKey, request.PubKey)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
if dkgProposalParticipant.Status != internal.PubKeyAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot confirm pubkey with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.PubKey, request.PubKey)
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionValidateDkgProposalPubKeys(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsError, isContainsExpired bool
|
||||
)
|
||||
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
if participant.Status == internal.PubKeyAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.PubKeyConfirmationError {
|
||||
isContainsError = true
|
||||
} else if participant.Status == internal.PubKeyConfirmed {
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsError {
|
||||
outEvent = eventDKGSetPubKeysConfirmationCanceledByErrorInternal
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal
|
||||
return
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventDKGSetPubKeysConfirmedInternal
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.CommitAwaitConfirmation
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -72,18 +125,72 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
if dkgProposalParticipant.Status != internal.CommitAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Commit, request.Commit)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.CommitConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsError, isContainsExpired bool
|
||||
)
|
||||
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
if participant.Status == internal.CommitAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.CommitConfirmationError {
|
||||
isContainsError = true
|
||||
} else if participant.Status == internal.CommitConfirmed {
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsError {
|
||||
outEvent = eventDKGCommitsConfirmationCancelByTimeoutInternal
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventDKGCommitsConfirmationCancelByErrorInternal
|
||||
return
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventDKGCommitsConfirmedInternal
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.DealAwaitConfirmation
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -110,18 +217,72 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
if dkgProposalParticipant.Status != internal.DealAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot confirm deal with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Deal, request.Deal)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.DealConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsError, isContainsExpired bool
|
||||
)
|
||||
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
if participant.Status == internal.DealAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.DealConfirmationError {
|
||||
isContainsError = true
|
||||
} else if participant.Status == internal.DealConfirmed {
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsError {
|
||||
outEvent = eventDKGDealsConfirmationCancelByErrorInternal
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventDKGDealsConfirmationCancelByTimeoutInternal
|
||||
return
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventDKGDealsConfirmedInternal
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.ResponseAwaitConfirmation
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -148,18 +309,188 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
if dkgProposalParticipant.Status != internal.ResponseAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.Response, request.Response)
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsError, isContainsExpired bool
|
||||
)
|
||||
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
if participant.Status == internal.ResponseAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.ResponseConfirmationError {
|
||||
isContainsError = true
|
||||
} else if participant.Status == internal.ResponseConfirmed {
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsError {
|
||||
outEvent = eventDKGResponseConfirmationCancelByErrorInternal
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventDKGResponseConfirmationCancelByTimeoutInternal
|
||||
return
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventDKGResponsesConfirmedInternal
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.MasterKeyAwaitConfirmation
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Master key
|
||||
|
||||
func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
if len(args) != 1 {
|
||||
err = errors.New("{arg0} required {DKGProposalMasterKeyConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
request, ok := args[0].(requests.DKGProposalMasterKeyConfirmationRequest)
|
||||
|
||||
if !ok {
|
||||
err = errors.New("cannot cast {arg0} to type {DKGProposalMasterKeyConfirmationRequest}")
|
||||
return
|
||||
}
|
||||
|
||||
if err = request.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
if dkgProposalParticipant.Status != internal.MasterKeyAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
copy(dkgProposalParticipant.MasterKey, request.MasterKey)
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
dkgProposalParticipant.Status = internal.MasterKeyConfirmed
|
||||
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitMasterKey(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsError, isContainsExpired bool
|
||||
masterKeys [][]byte
|
||||
)
|
||||
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
if participant.Status == internal.MasterKeyAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.MasterKeyConfirmationError {
|
||||
isContainsError = true
|
||||
} else if participant.Status == internal.MasterKeyConfirmed {
|
||||
masterKeys = append(masterKeys, participant.MasterKey)
|
||||
unconfirmedParticipants--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsError {
|
||||
outEvent = eventDKGMasterKeyConfirmationCancelByErrorInternal
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventDKGMasterKeyConfirmationCancelByTimeoutInternal
|
||||
return
|
||||
}
|
||||
|
||||
// Temporary simplest match master keys
|
||||
if len(masterKeys) > 1 {
|
||||
for i, masterKey := range masterKeys {
|
||||
for j := range masterKeys {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(masterKey, masterKeys[i]) {
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.MasterKeyConfirmationError
|
||||
participant.Error = errors.New("master key is mismatched")
|
||||
}
|
||||
|
||||
outEvent = eventDKGMasterKeyConfirmationCancelByErrorInternal
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventDKGMasterKeyConfirmedInternal
|
||||
|
||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||
participant.Status = internal.MasterKeyConfirmed
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -185,18 +516,18 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.DKGQuorumExists(request.ParticipantId) {
|
||||
err = errors.New("{ParticipantId} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
|
||||
|
||||
// TODO: Move to methods
|
||||
switch inEvent {
|
||||
case EventDKGPubKeyConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.PubKeyConAwaitConfirmation:
|
||||
case internal.PubKeyAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||
case internal.PubKeyConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
|
@ -221,13 +552,13 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
|||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
internal.CommitConfirmationError,
|
||||
))
|
||||
}
|
||||
case EventDKGDealConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.DealAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||
dkgProposalParticipant.Status = internal.DealConfirmationError
|
||||
case internal.DealConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.DealConfirmationError:
|
||||
|
@ -236,13 +567,13 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
|||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
internal.DealConfirmationError,
|
||||
))
|
||||
}
|
||||
case EventDKGResponseConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.ResponseAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||
dkgProposalParticipant.Status = internal.ResponseConfirmationError
|
||||
case internal.ResponseConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.ResponseConfirmationError:
|
||||
|
@ -251,7 +582,22 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
|||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.PubKeyConfirmationError,
|
||||
internal.ResponseConfirmationError,
|
||||
))
|
||||
}
|
||||
case EventDKGMasterKeyConfirmationError:
|
||||
switch dkgProposalParticipant.Status {
|
||||
case internal.MasterKeyAwaitConfirmation:
|
||||
dkgProposalParticipant.Status = internal.MasterKeyConfirmationError
|
||||
case internal.MasterKeyConfirmed:
|
||||
err = errors.New("{Status} already confirmed")
|
||||
case internal.MasterKeyConfirmationError:
|
||||
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.MasterKeyConfirmationError))
|
||||
default:
|
||||
err = errors.New(fmt.Sprintf(
|
||||
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
|
||||
dkgProposalParticipant.Status,
|
||||
internal.MasterKeyConfirmationError,
|
||||
))
|
||||
}
|
||||
default:
|
||||
|
@ -262,9 +608,10 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
|||
return
|
||||
}
|
||||
|
||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
dkgProposalParticipant.Error = request.Error
|
||||
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
|
||||
|
||||
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
|
||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||
|
||||
// TODO: Add outEvent
|
||||
|
||||
|
|
|
@ -3,71 +3,88 @@ 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"
|
||||
|
||||
StateDkgInitial = signature_proposal_fsm.StateValidationCompleted
|
||||
StateDkgInitial = StateDkgPubKeysAwaitConfirmations
|
||||
|
||||
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgPubKeysAwaitCancelled = fsm.State("state_dkg_pub_keys_await_cancelled")
|
||||
StateDkgPubKeysAwaitCancelledByTimeout = fsm.State("state_dkg_pub_keys_await_cancelled_by_timeout")
|
||||
// Canceled
|
||||
StateDkgPubKeysAwaitCanceled = fsm.State("state_dkg_pub_keys_await_canceled")
|
||||
StateDkgPubKeysAwaitCanceledByTimeout = fsm.State("state_dkg_pub_keys_await_canceled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
|
||||
// StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
|
||||
|
||||
// Sending dkg commits
|
||||
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgCommitsAwaitCancelled = fsm.State("state_dkg_commits_await_cancelled")
|
||||
StateDkgCommitsAwaitCancelledByTimeout = fsm.State("state_dkg_commits_await_cancelled_by_timeout")
|
||||
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations")
|
||||
// Canceled
|
||||
StateDkgCommitsAwaitCanceled = fsm.State("state_dkg_commits_await_canceled")
|
||||
StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
|
||||
|
||||
// Sending dkg deals
|
||||
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgDealsAwaitCancelled = fsm.State("state_dkg_deals_await_cancelled")
|
||||
StateDkgDealsAwaitCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
|
||||
// Canceled
|
||||
StateDkgDealsAwaitCanceled = fsm.State("state_dkg_deals_await_canceled")
|
||||
StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_await_canceled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_confirmed")
|
||||
//StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_confirmed")
|
||||
|
||||
StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations")
|
||||
// Cancelled
|
||||
StateDkgResponsesAwaitCancelled = fsm.State("state_dkg_responses_await_cancelled")
|
||||
StateDkgResponsesAwaitCancelledByTimeout = fsm.State("state_dkg_responses_sending_cancelled_by_timeout")
|
||||
// Canceled
|
||||
StateDkgResponsesAwaitCanceled = fsm.State("state_dkg_responses_await_canceled")
|
||||
StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout")
|
||||
// Confirmed
|
||||
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed")
|
||||
|
||||
StateDkgMasterKeyAwaitConfirmations = fsm.State("state_dkg_master_key_await_confirmations")
|
||||
StateDkgMasterKeyAwaitCanceled = fsm.State("state_dkg_master_key_await_canceled")
|
||||
StateDkgMasterKeyAwaitCanceledByTimeout = fsm.State("state_dkg_master_key_await_canceled_by_timeout")
|
||||
|
||||
// Events
|
||||
EventDKGPubKeysSendingRequiredInternal = fsm.Event("event_dkg_pub_key_sending_required_internal")
|
||||
|
||||
EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received")
|
||||
EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error")
|
||||
EventDKGPubKeysConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal")
|
||||
EventDKGPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal")
|
||||
eventAutoDKGInitialInternal = fsm.Event("event_dkg_init_internal")
|
||||
|
||||
EventDKGCommitsSendingRequiredInternal = fsm.Event("event_dkg_commits_sending_required_internal")
|
||||
EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received")
|
||||
EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error")
|
||||
|
||||
eventAutoDKGValidatePubKeysConfirmationInternal = fsm.Event("event_dkg_pub_keys_validate_internal")
|
||||
|
||||
eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal")
|
||||
eventDKGSetPubKeysConfirmationCanceledByErrorInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_error_internal")
|
||||
eventDKGSetPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal")
|
||||
|
||||
EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received")
|
||||
EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error")
|
||||
EventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal")
|
||||
EventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal")
|
||||
|
||||
EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal")
|
||||
eventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal")
|
||||
eventDKGCommitsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_error_internal")
|
||||
eventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal")
|
||||
eventAutoDKGValidateConfirmationCommitsInternal = fsm.Event("event_dkg_commits_validate_internal")
|
||||
|
||||
EventDKGDealConfirmationReceived = fsm.Event("event_dkg_deal_confirm_received")
|
||||
EventDKGDealConfirmationError = fsm.Event("event_dkg_deal_confirm_canceled_by_error")
|
||||
EventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal")
|
||||
|
||||
EventDKGResponsesSendingRequiredInternal = fsm.Event("event_dkg_responses_sending_required_internal")
|
||||
eventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal")
|
||||
eventDKGDealsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_error_internal")
|
||||
eventDKGDealsConfirmedInternal = fsm.Event("event_dkg_deals_confirmed_internal")
|
||||
eventAutoDKGValidateConfirmationDealsInternal = fsm.Event("event_dkg_deals_validate_internal")
|
||||
|
||||
EventDKGResponseConfirmationReceived = fsm.Event("event_dkg_response_confirm_received")
|
||||
EventDKGResponseConfirmationError = fsm.Event("event_dkg_response_confirm_canceled_by_error")
|
||||
EventDKGResponseConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_response_confirm_canceled_by_timeout_internal")
|
||||
eventDKGResponseConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_response_confirm_canceled_by_timeout_internal")
|
||||
eventDKGResponseConfirmationCancelByErrorInternal = fsm.Event("event_dkg_response_confirm_canceled_by_error_internal")
|
||||
eventDKGResponsesConfirmedInternal = fsm.Event("event_dkg_responses_confirmed_internal")
|
||||
eventAutoDKGValidateResponsesConfirmationInternal = fsm.Event("event_dkg_responses_validate_internal")
|
||||
|
||||
EventDKGMasterKeyConfirmationReceived = fsm.Event("event_dkg_master_key_confirm_received")
|
||||
EventDKGMasterKeyConfirmationError = fsm.Event("event_dkg_master_key_confirm_canceled_by_error")
|
||||
eventDKGMasterKeyConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_master_key_confirm_canceled_by_timeout_internal")
|
||||
eventDKGMasterKeyConfirmationCancelByErrorInternal = fsm.Event("event_dkg_master_key_confirm_canceled_by_error_internal")
|
||||
eventDKGMasterKeyConfirmedInternal = fsm.Event("event_dkg_master_key_confirmed_internal")
|
||||
eventAutoDKGValidateMasterKeyConfirmationInternal = fsm.Event("event_dkg_master_key_validate_internal")
|
||||
|
||||
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
|
||||
)
|
||||
|
@ -88,60 +105,87 @@ func New() internal.DumpedMachineProvider {
|
|||
|
||||
// Init
|
||||
// Switch to pub keys required
|
||||
{Name: EventDKGPubKeysSendingRequiredInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true},
|
||||
// {Name: eventDKGPubKeysSendingRequiredAuto, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunAfter},
|
||||
|
||||
// {Name: eventAutoDKGInitialInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore},
|
||||
|
||||
// Pub keys sending
|
||||
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelled},
|
||||
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Canceled
|
||||
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceled},
|
||||
|
||||
{Name: eventAutoDKGValidatePubKeysConfirmationInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
{Name: eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceledByTimeout, IsInternal: true},
|
||||
// Confirmed
|
||||
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmed, IsInternal: true},
|
||||
{Name: eventDKGSetPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Switch to commits required
|
||||
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Commits
|
||||
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelled},
|
||||
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Confirmed
|
||||
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmed, IsInternal: true},
|
||||
// Canceled
|
||||
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceled},
|
||||
{Name: eventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByTimeout, IsInternal: true},
|
||||
|
||||
// Switch to deals required
|
||||
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||
{Name: eventAutoDKGValidateConfirmationCommitsInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
// Confirmed
|
||||
{Name: eventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Deals
|
||||
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelled},
|
||||
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Canceled
|
||||
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceled},
|
||||
{Name: eventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||
{Name: eventAutoDKGValidateConfirmationDealsInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
// Switch to responses required
|
||||
{Name: EventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
|
||||
{Name: eventDKGDealsConfirmedInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Deals
|
||||
// Responses
|
||||
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
|
||||
// Cancelled
|
||||
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelled},
|
||||
{Name: EventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelledByTimeout, IsInternal: true},
|
||||
// Canceled
|
||||
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceled},
|
||||
{Name: eventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByTimeout, IsInternal: true},
|
||||
|
||||
{Name: eventAutoDKGValidateResponsesConfirmationInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
{Name: eventDKGResponsesConfirmedInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true},
|
||||
|
||||
// Master key
|
||||
|
||||
{Name: EventDKGMasterKeyConfirmationReceived, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations},
|
||||
{Name: EventDKGMasterKeyConfirmationError, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceled},
|
||||
{Name: eventDKGMasterKeyConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceledByTimeout, IsInternal: true},
|
||||
|
||||
{Name: eventAutoDKGValidateMasterKeyConfirmationInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
{Name: eventDKGMasterKeyConfirmedInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
|
||||
|
||||
// Done
|
||||
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
|
||||
// {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
|
||||
},
|
||||
fsm.Callbacks{
|
||||
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
|
||||
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
|
||||
|
||||
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
|
||||
EventDKGCommitConfirmationError: machine.actionConfirmationError,
|
||||
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
|
||||
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
|
||||
eventAutoDKGValidatePubKeysConfirmationInternal: machine.actionValidateDkgProposalPubKeys,
|
||||
|
||||
EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived,
|
||||
EventDKGDealConfirmationError: machine.actionConfirmationError,
|
||||
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
|
||||
EventDKGCommitConfirmationError: machine.actionConfirmationError,
|
||||
eventAutoDKGValidateConfirmationCommitsInternal: machine.actionValidateDkgProposalAwaitCommits,
|
||||
|
||||
EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived,
|
||||
EventDKGResponseConfirmationError: machine.actionConfirmationError,
|
||||
EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived,
|
||||
EventDKGDealConfirmationError: machine.actionConfirmationError,
|
||||
eventAutoDKGValidateConfirmationDealsInternal: machine.actionValidateDkgProposalAwaitDeals,
|
||||
|
||||
EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived,
|
||||
EventDKGResponseConfirmationError: machine.actionConfirmationError,
|
||||
eventAutoDKGValidateResponsesConfirmationInternal: machine.actionValidateDkgProposalAwaitResponses,
|
||||
|
||||
EventDKGMasterKeyConfirmationReceived: machine.actionMasterKeyConfirmationReceived,
|
||||
EventDKGMasterKeyConfirmationError: machine.actionConfirmationError,
|
||||
eventAutoDKGValidateMasterKeyConfirmationInternal: machine.actionValidateDkgProposalAwaitMasterKey,
|
||||
},
|
||||
)
|
||||
return machine
|
||||
|
|
|
@ -2,13 +2,79 @@ package internal
|
|||
|
||||
import "github.com/depools/dc4bc/fsm/fsm_pool"
|
||||
|
||||
type DumpedMachineStatePayload struct {
|
||||
TransactionId string
|
||||
ConfirmationProposalPayload SignatureProposalQuorum
|
||||
DKGProposalPayload DKGProposalQuorum
|
||||
}
|
||||
|
||||
type DumpedMachineProvider interface {
|
||||
fsm_pool.MachineProvider
|
||||
SetUpPayload(payload *DumpedMachineStatePayload)
|
||||
}
|
||||
|
||||
// DKG and other stages quorums are separated,
|
||||
// because unnecessary data may be unset
|
||||
type DumpedMachineStatePayload struct {
|
||||
TransactionId string
|
||||
SignatureProposalPayload *SignatureConfirmation
|
||||
DKGProposalPayload *DKGConfirmation
|
||||
}
|
||||
|
||||
// Signature quorum
|
||||
|
||||
func (p *DumpedMachineStatePayload) SigQuorumCount() int {
|
||||
var count int
|
||||
if p.SignatureProposalPayload.Quorum != nil {
|
||||
count = len(p.SignatureProposalPayload.Quorum)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) SigQuorumExists(id string) bool {
|
||||
var exists bool
|
||||
if p.SignatureProposalPayload.Quorum != nil {
|
||||
_, exists = p.SignatureProposalPayload.Quorum[id]
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) SigQuorumGet(id string) (participant *SignatureProposalParticipant) {
|
||||
if p.SignatureProposalPayload.Quorum != nil {
|
||||
participant, _ = p.SignatureProposalPayload.Quorum[id]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) SigQuorumUpdate(id string, participant *SignatureProposalParticipant) {
|
||||
if p.SignatureProposalPayload.Quorum != nil {
|
||||
p.SignatureProposalPayload.Quorum[id] = participant
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DKG quorum
|
||||
|
||||
func (p *DumpedMachineStatePayload) DKGQuorumCount() int {
|
||||
var count int
|
||||
if p.DKGProposalPayload.Quorum != nil {
|
||||
count = len(p.DKGProposalPayload.Quorum)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) DKGQuorumExists(id int) bool {
|
||||
var exists bool
|
||||
if p.DKGProposalPayload.Quorum != nil {
|
||||
_, exists = p.DKGProposalPayload.Quorum[id]
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) DKGQuorumGet(id int) (participant *DKGProposalParticipant) {
|
||||
if p.DKGProposalPayload.Quorum != nil {
|
||||
participant, _ = p.DKGProposalPayload.Quorum[id]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *DumpedMachineStatePayload) DKGQuorumUpdate(id int, participant *DKGProposalParticipant) {
|
||||
if p.DKGProposalPayload.Quorum != nil {
|
||||
p.DKGProposalPayload.Quorum[id] = participant
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -5,12 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota
|
||||
SignatureConfirmed
|
||||
)
|
||||
|
||||
type ConfirmationProposal struct {
|
||||
type SignatureConfirmation struct {
|
||||
Quorum SignatureProposalQuorum
|
||||
CreatedAt *time.Time
|
||||
ExpiresAt *time.Time
|
||||
|
@ -23,22 +18,22 @@ type SignatureProposalParticipant struct {
|
|||
PublicKey *rsa.PublicKey
|
||||
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
|
||||
InvitationSecret string
|
||||
Status SignatureProposalParticipantStatus
|
||||
Status ParticipantStatus
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
// Unique alias for map iteration - Public Key Fingerprint
|
||||
// Excludes array merge and rotate operations
|
||||
type SignatureProposalQuorum map[string]SignatureProposalParticipant
|
||||
type SignatureProposalQuorum map[string]*SignatureProposalParticipant
|
||||
|
||||
type SignatureProposalParticipantStatus uint8
|
||||
type ParticipantStatus uint8
|
||||
|
||||
const (
|
||||
SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota
|
||||
SignatureConfirmationAwaitConfirmation ParticipantStatus = iota
|
||||
SignatureConfirmationConfirmed
|
||||
SignatureConfirmationDeclined
|
||||
SignatureConfirmationError
|
||||
PubKeyConAwaitConfirmation
|
||||
PubKeyAwaitConfirmation
|
||||
PubKeyConfirmed
|
||||
PubKeyConfirmationError
|
||||
CommitAwaitConfirmation
|
||||
|
@ -50,29 +45,34 @@ const (
|
|||
ResponseAwaitConfirmation
|
||||
ResponseConfirmed
|
||||
ResponseConfirmationError
|
||||
MasterKeyAwaitConfirmation
|
||||
MasterKeyConfirmed
|
||||
MasterKeyConfirmationError
|
||||
)
|
||||
|
||||
type DKGProposal struct {
|
||||
Quorum map[int]DKGProposalParticipant
|
||||
type DKGProposalParticipant struct {
|
||||
Title string
|
||||
PubKey []byte
|
||||
Commit []byte
|
||||
Deal []byte
|
||||
Response []byte
|
||||
MasterKey []byte
|
||||
Status ParticipantStatus
|
||||
Error error
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
type DKGProposalQuorum map[int]*DKGProposalParticipant
|
||||
|
||||
type DKGConfirmation struct {
|
||||
Quorum DKGProposalQuorum
|
||||
CreatedAt *time.Time
|
||||
ExpiresAt *time.Time
|
||||
}
|
||||
|
||||
type DKGProposalParticipant struct {
|
||||
Title string
|
||||
PublicKey []byte
|
||||
Commit []byte
|
||||
Deal []byte
|
||||
Response []byte
|
||||
Status DKGProposalParticipantStatus
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
type DKGProposalQuorum map[int]DKGProposalParticipant
|
||||
|
||||
type DKGProposalParticipantStatus uint8
|
||||
|
||||
func (s DKGProposalParticipantStatus) String() string {
|
||||
func (s ParticipantStatus) String() string {
|
||||
var str = "undefined"
|
||||
switch s {
|
||||
case SignatureConfirmationAwaitConfirmation:
|
||||
|
@ -83,8 +83,8 @@ func (s DKGProposalParticipantStatus) String() string {
|
|||
str = "SignatureConfirmationDeclined"
|
||||
case SignatureConfirmationError:
|
||||
str = "SignatureConfirmationError"
|
||||
case PubKeyConAwaitConfirmation:
|
||||
str = "PubKeyConAwaitConfirmation"
|
||||
case PubKeyAwaitConfirmation:
|
||||
str = "PubKeyAwaitConfirmation"
|
||||
case PubKeyConfirmed:
|
||||
str = "PubKeyConfirmed"
|
||||
case PubKeyConfirmationError:
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package state_machines
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||
|
@ -12,6 +14,10 @@ import (
|
|||
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
)
|
||||
|
||||
const (
|
||||
dkgTransactionIdLength = 128
|
||||
)
|
||||
|
||||
// Is machine state scope dump will be locked?
|
||||
type FSMDump struct {
|
||||
TransactionId string
|
||||
|
@ -37,9 +43,17 @@ func init() {
|
|||
|
||||
// Create new fsm with unique id
|
||||
// transactionId required for unique identify dump
|
||||
func Create(transactionId string) (*FSMInstance, error) {
|
||||
var err error
|
||||
i := &FSMInstance{}
|
||||
func Create() (*FSMInstance, error) {
|
||||
var (
|
||||
err error
|
||||
i = &FSMInstance{}
|
||||
)
|
||||
transactionId, err := generateDkgTransactionId()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = i.InitDump(transactionId)
|
||||
|
||||
if err != nil {
|
||||
|
@ -52,7 +66,7 @@ func Create(transactionId string) (*FSMInstance, error) {
|
|||
return i, err
|
||||
}
|
||||
|
||||
// Get fsm from dump
|
||||
// DKGQuorumGet fsm from dump
|
||||
func FromDump(data []byte) (*FSMInstance, error) {
|
||||
var err error
|
||||
|
||||
|
@ -79,6 +93,10 @@ func FromDump(data []byte) (*FSMInstance, error) {
|
|||
func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Response, dump []byte, err error) {
|
||||
var dumpErr error
|
||||
|
||||
if i.machine == nil {
|
||||
return nil, []byte{}, errors.New("machine is not initialized")
|
||||
}
|
||||
|
||||
result, err = i.machine.Do(event, args...)
|
||||
|
||||
// On route errors result will be nil
|
||||
|
@ -106,16 +124,38 @@ func (i *FSMInstance) InitDump(transactionId string) error {
|
|||
}
|
||||
|
||||
i.dump = &FSMDump{
|
||||
State: fsm.StateGlobalIdle,
|
||||
TransactionId: transactionId,
|
||||
State: fsm.StateGlobalIdle,
|
||||
Payload: &internal.DumpedMachineStatePayload{
|
||||
TransactionId: transactionId,
|
||||
ConfirmationProposalPayload: nil,
|
||||
DKGProposalPayload: nil,
|
||||
TransactionId: transactionId,
|
||||
SignatureProposalPayload: nil,
|
||||
DKGProposalPayload: nil,
|
||||
},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *FSMInstance) State() (fsm.State, error) {
|
||||
if i.machine == nil {
|
||||
return "", errors.New("machine is not initialized")
|
||||
}
|
||||
return i.machine.State(), nil
|
||||
}
|
||||
|
||||
func (i *FSMInstance) Id() string {
|
||||
if i.dump != nil {
|
||||
return i.dump.TransactionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *FSMInstance) Dump() ([]byte, error) {
|
||||
if i.dump == nil {
|
||||
return []byte{}, errors.New("dump is not initialized")
|
||||
}
|
||||
return i.dump.Marshal()
|
||||
}
|
||||
|
||||
// TODO: Add encryption
|
||||
func (d *FSMDump) Marshal() ([]byte, error) {
|
||||
return json.Marshal(d)
|
||||
|
@ -124,8 +164,18 @@ func (d *FSMDump) Marshal() ([]byte, error) {
|
|||
// TODO: Add decryption
|
||||
func (d *FSMDump) Unmarshal(data []byte) error {
|
||||
if d == nil {
|
||||
return errors.New("dump struct is not initialized")
|
||||
return errors.New("dump is not initialized")
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, d)
|
||||
}
|
||||
|
||||
func generateDkgTransactionId() (string, error) {
|
||||
b := make([]byte, dkgTransactionIdLength)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"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"
|
||||
|
@ -34,6 +36,8 @@ var (
|
|||
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
||||
CreatedAt: &tm,
|
||||
}
|
||||
|
||||
testFSMDump []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -76,7 +80,7 @@ func init() {
|
|||
}
|
||||
|
||||
func TestCreate_Positive(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
testFSMInstance, err := Create()
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
|
@ -86,48 +90,80 @@ func TestCreate_Positive(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
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 Test_Workflow(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
|
||||
if testFSMInstance == nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Workflow
|
||||
|
||||
func Test_SignatureProposal_Init(t *testing.T) {
|
||||
testFSMInstance, err := Create()
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
transactionId := testFSMInstance.Id()
|
||||
|
||||
if transactionId == "" {
|
||||
t.Fatalf("expected {transactionId for dump}")
|
||||
}
|
||||
|
||||
if testFSMInstance.machine.Name() != spf.FsmName {
|
||||
t.Fatalf("expected machine name {%s}", spf.FsmName)
|
||||
}
|
||||
|
||||
if testFSMInstance.machine.State() != spf.StateParticipantsConfirmationsInit {
|
||||
t.Fatalf("expected inital state {%s}", spf.StateParticipantsConfirmationsInit)
|
||||
}
|
||||
compareState(t, spf.StateParticipantsConfirmationsInit, testFSMInstance.machine.State())
|
||||
|
||||
testFSMDump, err = testFSMInstance.Dump()
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, testFSMDump)
|
||||
}
|
||||
|
||||
func Test_SignatureProposal_Positive(t *testing.T) {
|
||||
testFSMInstance, err := FromDump(testFSMDump)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
compareErrNil(t, err)
|
||||
|
||||
if len(dump) == 0 {
|
||||
t.Fatalf("expected non zero dump, when executed without error")
|
||||
}
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
if fsmResponse == nil {
|
||||
t.Fatalf("expected {*fsm.FSMResponse}")
|
||||
}
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
if fsmResponse.State != spf.StateAwaitParticipantsConfirmations {
|
||||
t.Fatalf("expected state {%s}", spf.StateAwaitParticipantsConfirmations)
|
||||
}
|
||||
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||
|
||||
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||
|
||||
|
@ -161,19 +197,19 @@ func Test_Workflow(t *testing.T) {
|
|||
participantsMap[participant.ParticipantId] = participant
|
||||
}
|
||||
|
||||
tm = tm.Add(10 * time.Second)
|
||||
tm = tm.Add(10 * time.Hour)
|
||||
|
||||
participantsCount := len(participantsMap)
|
||||
|
||||
participantCounter := participantsCount
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
|
||||
participantCounter--
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
compareErrNil(t, err)
|
||||
|
||||
if testFSMInstance == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||
t.Fatalf("not found external user data for response fingerprint")
|
||||
|
@ -191,6 +227,202 @@ func Test_Workflow(t *testing.T) {
|
|||
DecryptedInvitation: string(encrypted),
|
||||
CreatedAt: &tm,
|
||||
})
|
||||
log.Println(fsmResponse.State, err)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
if participantCounter > 0 {
|
||||
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||
} else {
|
||||
compareState(t, dpf.StateDkgInitial, 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
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
participantCounter--
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||
t.Fatalf("not found external user data for response fingerprint")
|
||||
}
|
||||
|
||||
commitMock := make([]byte, 128)
|
||||
_, err := rand.Read(commitMock)
|
||||
if err != nil {
|
||||
compareErrNil(t, err)
|
||||
}
|
||||
|
||||
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGCommitConfirmationReceived, requests.DKGProposalCommitConfirmationRequest{
|
||||
ParticipantId: participant.ParticipantId,
|
||||
Commit: commitMock,
|
||||
CreatedAt: tm,
|
||||
})
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
}
|
||||
|
||||
compareState(t, dpf.StateDkgDealsAwaitConfirmations, fsmResponse.State)
|
||||
|
||||
// Deals
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
participantCounter--
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||
t.Fatalf("not found external user data for response fingerprint")
|
||||
}
|
||||
|
||||
dealMock := make([]byte, 128)
|
||||
_, err := rand.Read(dealMock)
|
||||
if err != nil {
|
||||
compareErrNil(t, err)
|
||||
}
|
||||
|
||||
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGDealConfirmationReceived, requests.DKGProposalDealConfirmationRequest{
|
||||
ParticipantId: participant.ParticipantId,
|
||||
Deal: dealMock,
|
||||
CreatedAt: tm,
|
||||
})
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
}
|
||||
|
||||
compareState(t, dpf.StateDkgResponsesAwaitConfirmations, fsmResponse.State)
|
||||
|
||||
// Responses
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
participantCounter--
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||
t.Fatalf("not found external user data for response fingerprint")
|
||||
}
|
||||
|
||||
responseMock := make([]byte, 128)
|
||||
_, err := rand.Read(responseMock)
|
||||
if err != nil {
|
||||
compareErrNil(t, err)
|
||||
}
|
||||
|
||||
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGResponseConfirmationReceived, requests.DKGProposalResponseConfirmationRequest{
|
||||
ParticipantId: participant.ParticipantId,
|
||||
Response: responseMock,
|
||||
CreatedAt: tm,
|
||||
})
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
}
|
||||
|
||||
compareState(t, dpf.StateDkgMasterKeyAwaitConfirmations, fsmResponse.State)
|
||||
|
||||
// Master keys
|
||||
|
||||
masterKeyMock := make([]byte, 128)
|
||||
_, err = rand.Read(masterKeyMock)
|
||||
if err != nil {
|
||||
compareErrNil(t, err)
|
||||
}
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
participantCounter--
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||
|
||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||
t.Fatalf("not found external user data for response fingerprint")
|
||||
}
|
||||
|
||||
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGMasterKeyConfirmationReceived, requests.DKGProposalMasterKeyConfirmationRequest{
|
||||
ParticipantId: participant.ParticipantId,
|
||||
MasterKey: masterKeyMock,
|
||||
CreatedAt: tm,
|
||||
})
|
||||
|
||||
compareErrNil(t, err)
|
||||
|
||||
compareDumpNotZero(t, dump)
|
||||
|
||||
compareFSMResponseNotNil(t, fsmResponse)
|
||||
|
||||
}
|
||||
|
||||
compareState(t, fsm.StateGlobalDone, fsmResponse.State)
|
||||
}
|
||||
|
||||
func Test_DKGProposal_Positive(t *testing.T) {
|
||||
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ package signature_proposal_fsm
|
|||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/config"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"time"
|
||||
)
|
||||
|
||||
// init -> awaitingConfirmations
|
||||
|
@ -31,7 +34,9 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
|||
return
|
||||
}
|
||||
|
||||
m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum)
|
||||
m.payload.SignatureProposalPayload = &internal.SignatureConfirmation{
|
||||
Quorum: make(internal.SignatureProposalQuorum),
|
||||
}
|
||||
|
||||
for index, participant := range request.Participants {
|
||||
participantId := createFingerprint(&participant.PubKey)
|
||||
|
@ -46,18 +51,18 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
|||
return inEvent, nil, errors.New("cannot parse {PubKey}")
|
||||
}
|
||||
|
||||
m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{
|
||||
m.payload.SignatureProposalPayload.Quorum[participantId] = &internal.SignatureProposalParticipant{
|
||||
ParticipantId: index,
|
||||
Title: participant.Title,
|
||||
PublicKey: parsedPubKey,
|
||||
InvitationSecret: secret,
|
||||
Status: internal.SignatureAwaitConfirmation,
|
||||
Status: internal.SignatureConfirmationAwaitConfirmation,
|
||||
UpdatedAt: request.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Checking fo quorum length
|
||||
if len(m.payload.ConfirmationProposalPayload) != len(request.Participants) {
|
||||
if m.payload.SigQuorumCount() != len(request.Participants) {
|
||||
err = errors.New("error with creating {SignatureProposalQuorum}")
|
||||
return
|
||||
}
|
||||
|
@ -66,7 +71,7 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
|||
|
||||
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
|
||||
|
||||
for pubKeyFingerprint, proposal := range m.payload.ConfirmationProposalPayload {
|
||||
for pubKeyFingerprint, proposal := range m.payload.SignatureProposalPayload.Quorum {
|
||||
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret)
|
||||
if err != nil {
|
||||
return inEvent, nil, errors.New("cannot encryptWithPubKey")
|
||||
|
@ -86,8 +91,8 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
|||
|
||||
// TODO: Add timeout checking
|
||||
func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
// m.payloadMu.Lock()
|
||||
// defer m.payloadMu.Unlock()
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
if len(args) != 1 {
|
||||
err = errors.New("{arg0} required {SignatureProposalParticipantRequest}")
|
||||
|
@ -105,42 +110,134 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
|
|||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant, ok := m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint]
|
||||
|
||||
if !ok {
|
||||
if !m.payload.SigQuorumExists(request.PubKeyFingerprint) {
|
||||
err = errors.New("{PubKeyFingerprint} not exist in quorum")
|
||||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant := m.payload.SigQuorumGet(request.PubKeyFingerprint)
|
||||
|
||||
if signatureProposalParticipant.InvitationSecret != request.DecryptedInvitation {
|
||||
err = errors.New("{InvitationSecret} not match {DecryptedInvitation}")
|
||||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant.Status = internal.SignatureConfirmed
|
||||
if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(*request.CreatedAt) {
|
||||
outEvent = eventSetValidationCanceledByTimeout
|
||||
return
|
||||
}
|
||||
|
||||
if signatureProposalParticipant.Status != internal.SignatureConfirmationAwaitConfirmation {
|
||||
err = errors.New(fmt.Sprintf("cannot apply reply participant with {Status} = {\"%s\"}", signatureProposalParticipant.Status))
|
||||
return
|
||||
}
|
||||
|
||||
switch inEvent {
|
||||
case EventConfirmProposal:
|
||||
signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed
|
||||
case EventDeclineProposal:
|
||||
signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined
|
||||
default:
|
||||
err = errors.New("undefined {Event} for action")
|
||||
return
|
||||
}
|
||||
|
||||
signatureProposalParticipant.UpdatedAt = request.CreatedAt
|
||||
|
||||
m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant
|
||||
|
||||
outEvent, response, err = m.actionValidateProposal(eventValidateProposalInternal)
|
||||
m.payload.SigQuorumUpdate(request.PubKeyFingerprint, signatureProposalParticipant)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SignatureProposalFSM) actionValidateProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
// m.payloadMu.Lock()
|
||||
// defer m.payloadMu.Unlock()
|
||||
func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
var (
|
||||
isContainsDeclined, isContainsExpired bool
|
||||
)
|
||||
|
||||
unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload)
|
||||
for _, participant := range m.payload.ConfirmationProposalPayload {
|
||||
if participant.Status == internal.SignatureConfirmed {
|
||||
unconfirmedParticipants--
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
tm := time.Now()
|
||||
|
||||
unconfirmedParticipants := m.payload.SigQuorumCount()
|
||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||
if participant.Status == internal.SignatureConfirmationAwaitConfirmation {
|
||||
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(tm) {
|
||||
isContainsExpired = true
|
||||
}
|
||||
} else {
|
||||
if participant.Status == internal.SignatureConfirmationConfirmed {
|
||||
unconfirmedParticipants--
|
||||
} else if participant.Status == internal.SignatureConfirmationDeclined {
|
||||
isContainsDeclined = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isContainsDeclined {
|
||||
outEvent = eventSetValidationCanceledByParticipant
|
||||
return
|
||||
}
|
||||
|
||||
if isContainsExpired {
|
||||
outEvent = eventSetValidationCanceledByTimeout
|
||||
return
|
||||
}
|
||||
|
||||
// The are no declined and timed out participants, check for all confirmations
|
||||
if unconfirmedParticipants > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outEvent = eventSetProposalValidatedInternal
|
||||
|
||||
m.actionSetValidatedSignatureProposal(outEvent)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SignatureProposalFSM) actionSetValidatedSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
// m.payloadMu.Lock()
|
||||
// defer m.payloadMu.Unlock()
|
||||
|
||||
// TODO: Run once after validation
|
||||
if m.payload.DKGProposalPayload != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.payload.DKGProposalPayload = &internal.DKGConfirmation{
|
||||
Quorum: make(internal.DKGProposalQuorum),
|
||||
}
|
||||
|
||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||
m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{
|
||||
Title: participant.Title,
|
||||
Status: internal.PubKeyAwaitConfirmation,
|
||||
UpdatedAt: participant.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove m.payload.SignatureProposalPayload?
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||
m.payloadMu.Lock()
|
||||
defer m.payloadMu.Unlock()
|
||||
|
||||
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
|
||||
|
||||
for pubKeyFingerprint, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
||||
ParticipantId: participant.ParticipantId,
|
||||
Title: participant.Title,
|
||||
PubKeyFingerprint: pubKeyFingerprint,
|
||||
Status: uint8(participant.Status),
|
||||
}
|
||||
responseData = append(responseData, responseEntry)
|
||||
}
|
||||
|
||||
return inEvent, responseData, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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"
|
||||
)
|
||||
|
@ -17,15 +18,16 @@ const (
|
|||
StateValidationCanceledByParticipant = fsm.State("state_sig_proposal_canceled_by_participant")
|
||||
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
|
||||
|
||||
StateValidationCompleted = fsm.State("state_sig_proposal_completed")
|
||||
// Out state
|
||||
|
||||
EventInitProposal = fsm.Event("event_sig_proposal_init")
|
||||
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
|
||||
EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant")
|
||||
eventValidateProposalInternal = fsm.Event("event_sig_proposal_validate")
|
||||
eventAutoValidateProposalInternal = fsm.Event("event_sig_proposal_validate")
|
||||
eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated")
|
||||
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
|
||||
eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_declined_timeout")
|
||||
|
||||
// Switch to next fsm
|
||||
|
||||
|
@ -53,22 +55,24 @@ func New() internal.DumpedMachineProvider {
|
|||
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
||||
// Is decline event should auto change state to default, or it process will initiated by client (external emit)?
|
||||
// Now set for external emitting.
|
||||
{Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant},
|
||||
{Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
||||
{Name: eventSetValidationCanceledByParticipant, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant, IsInternal: true},
|
||||
|
||||
{Name: eventValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true},
|
||||
{Name: eventAutoValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true, IsAuto: true},
|
||||
|
||||
// eventProposalValidate internal or from client?
|
||||
// yay
|
||||
// Exit point
|
||||
{Name: eventSetProposalValidatedInternal, 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.actionProposalResponseByParticipant,
|
||||
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
||||
eventValidateProposalInternal: machine.actionValidateProposal,
|
||||
EventInitProposal: machine.actionInitProposal,
|
||||
EventConfirmProposal: machine.actionProposalResponseByParticipant,
|
||||
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
||||
eventAutoValidateProposalInternal: machine.actionValidateSignatureProposal,
|
||||
eventSetProposalValidatedInternal: machine.actionSetValidatedSignatureProposal,
|
||||
},
|
||||
)
|
||||
return machine
|
||||
|
|
|
@ -7,7 +7,7 @@ import "time"
|
|||
type DKGProposalPubKeyConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
PubKey []byte
|
||||
CreatedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_commits_sending_await_confirmations"
|
||||
|
@ -15,7 +15,7 @@ type DKGProposalPubKeyConfirmationRequest struct {
|
|||
type DKGProposalCommitConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Commit []byte
|
||||
CreatedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_deals_await_confirmations"
|
||||
|
@ -23,7 +23,7 @@ type DKGProposalCommitConfirmationRequest struct {
|
|||
type DKGProposalDealConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Deal []byte
|
||||
CreatedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_responses_await_confirmations"
|
||||
|
@ -31,19 +31,30 @@ type DKGProposalDealConfirmationRequest struct {
|
|||
type DKGProposalResponseConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
Response []byte
|
||||
CreatedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_master_key_await_confirmations"
|
||||
// Events: "event_dkg_master_key_confirm_received"
|
||||
type DKGProposalMasterKeyConfirmationRequest struct {
|
||||
ParticipantId int
|
||||
MasterKey []byte
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
// States: "state_dkg_pub_keys_await_confirmations"
|
||||
// "state_dkg_commits_sending_await_confirmations"
|
||||
// "state_dkg_deals_await_confirmations"
|
||||
// "state_dkg_responses_await_confirmations"
|
||||
// "state_dkg_master_key_await_confirmations"
|
||||
//
|
||||
// Events: "event_dkg_pub_key_confirm_canceled_by_error",
|
||||
// "event_dkg_commit_confirm_canceled_by_error"
|
||||
// "event_dkg_deal_confirm_canceled_by_error"
|
||||
// "event_dkg_response_confirm_canceled_by_error"
|
||||
// "event_dkg_master_key_confirm_canceled_by_error"
|
||||
type DKGProposalConfirmationErrorRequest struct {
|
||||
ParticipantId int
|
||||
CreatedAt *time.Time
|
||||
Error error
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ func (r *DKGProposalPubKeyConfirmationRequest) Validate() error {
|
|||
return errors.New("{PubKey} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -27,8 +27,8 @@ func (r *DKGProposalCommitConfirmationRequest) Validate() error {
|
|||
return errors.New("{Commit} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -43,8 +43,8 @@ func (r *DKGProposalDealConfirmationRequest) Validate() error {
|
|||
return errors.New("{Deal} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -59,8 +59,24 @@ func (r *DKGProposalResponseConfirmationRequest) Validate() error {
|
|||
return errors.New("{Response} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DKGProposalMasterKeyConfirmationRequest) Validate() error {
|
||||
if r.ParticipantId < 0 {
|
||||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if len(r.MasterKey) == 0 {
|
||||
return errors.New("{MasterKey} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -71,8 +87,12 @@ func (r *DKGProposalConfirmationErrorRequest) Validate() error {
|
|||
return errors.New("{ParticipantId} cannot be a negative number")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
if r.Error == nil {
|
||||
return errors.New("{Error} cannot be a nil")
|
||||
}
|
||||
|
||||
if r.CreatedAt.IsZero() {
|
||||
return errors.New("{CreatedAt} is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -2,14 +2,8 @@ package responses
|
|||
|
||||
// Response
|
||||
|
||||
const (
|
||||
ProposalConfirmationStatusIdle = iota
|
||||
ProposalConfirmationStatusAccepted
|
||||
ProposalConfirmationStatusCanceled
|
||||
ProposalConfirmationStatusTimeout
|
||||
)
|
||||
|
||||
// States: "validate_proposal"
|
||||
// Event: "event_sig_proposal_init"
|
||||
// States: "__idle"
|
||||
|
||||
type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry
|
||||
|
||||
|
@ -31,5 +25,5 @@ type SignatureProposalParticipantStatusEntry struct {
|
|||
ParticipantId int
|
||||
Title string
|
||||
PubKeyFingerprint string
|
||||
Status int
|
||||
Status uint8
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue