mirror of https://github.com/certusone/dc4bc.git
feat: fixed flow, added tests
This commit is contained in:
parent
1e8af819bb
commit
10c343ac90
|
@ -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
|
package config
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TODO: Move to machine level configs?
|
// TODO: Move to machine level configs?
|
||||||
ParticipantsMinCount = 3
|
ParticipantsMinCount = 3
|
||||||
|
SignatureProposalConfirmationDeadline = time.Hour * 24
|
||||||
|
DkgConfirmationDeadline = time.Hour * 24
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
const (
|
const (
|
||||||
StateGlobalIdle = State("__idle")
|
StateGlobalIdle = State("__idle")
|
||||||
StateGlobalDone = State("__done")
|
StateGlobalDone = State("__done")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
EventRunDefault EventRunMode = iota
|
EventRunDefault EventRunMode = iota
|
||||||
EventRunBefore
|
EventRunBefore
|
||||||
EventRunAfter
|
EventRunAfter
|
||||||
|
@ -189,6 +191,10 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
||||||
panic("duplicate dst for pair `source + event`")
|
panic("duplicate dst for pair `source + event`")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if event.IsAuto && event.AutoRunMode == EventRunDefault {
|
||||||
|
event.AutoRunMode = EventRunAfter
|
||||||
|
}
|
||||||
|
|
||||||
trEvent := &trEvent{
|
trEvent := &trEvent{
|
||||||
tKey.event,
|
tKey.event,
|
||||||
event.DstState,
|
event.DstState,
|
||||||
|
@ -197,10 +203,6 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
||||||
event.AutoRunMode,
|
event.AutoRunMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if trEvent.isAuto && trEvent.runMode == EventRunDefault {
|
|
||||||
trEvent.runMode = EventRunAfter
|
|
||||||
}
|
|
||||||
|
|
||||||
f.transitions[tKey] = trEvent
|
f.transitions[tKey] = trEvent
|
||||||
|
|
||||||
// For using provider, event must use with IsGlobal = true
|
// For using provider, event must use with IsGlobal = true
|
||||||
|
@ -245,7 +247,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := allEvents[event]; !ok {
|
if _, ok := allEvents[event]; !ok {
|
||||||
panic("callback has no event")
|
panic("callback has empty event")
|
||||||
}
|
}
|
||||||
|
|
||||||
f.callbacks[event] = callback
|
f.callbacks[event] = callback
|
||||||
|
|
|
@ -12,9 +12,9 @@ const (
|
||||||
stateStage1 = State("state_stage1")
|
stateStage1 = State("state_stage1")
|
||||||
// Process data
|
// Process data
|
||||||
stateStage2 = State("state_stage2")
|
stateStage2 = State("state_stage2")
|
||||||
// Cancelled with internal event
|
// Canceled with internal event
|
||||||
stateCanceledByInternal = State("state_canceled")
|
stateCanceledByInternal = State("state_canceled")
|
||||||
// Cancelled with external event
|
// Canceled with external event
|
||||||
stateCanceled2 = State("state_canceled2")
|
stateCanceled2 = State("state_canceled2")
|
||||||
// Out endpoint to switch
|
// Out endpoint to switch
|
||||||
stateOutToFSM2 = State("state_out_to_fsm2")
|
stateOutToFSM2 = State("state_out_to_fsm2")
|
||||||
|
|
|
@ -23,9 +23,9 @@ const (
|
||||||
fsm1StateStage1 = fsm.State("state_fsm1_stage1")
|
fsm1StateStage1 = fsm.State("state_fsm1_stage1")
|
||||||
// Process data
|
// Process data
|
||||||
fsm1StateStage2 = fsm.State("state_fsm1_stage2")
|
fsm1StateStage2 = fsm.State("state_fsm1_stage2")
|
||||||
// Cancelled with internal event
|
// Canceled with internal event
|
||||||
fsm1StateCanceledByInternal = fsm.State("state_fsm1_canceled")
|
fsm1StateCanceledByInternal = fsm.State("state_fsm1_canceled")
|
||||||
// Cancelled with external event
|
// Canceled with external event
|
||||||
fsm1StateCanceled2 = fsm.State("state_fsm1_canceled2")
|
fsm1StateCanceled2 = fsm.State("state_fsm1_canceled2")
|
||||||
// Out endpoint to switch
|
// Out endpoint to switch
|
||||||
fsm1StateOutToFSM2 = fsm.State("state_fsm1_out_to_fsm2")
|
fsm1StateOutToFSM2 = fsm.State("state_fsm1_out_to_fsm2")
|
||||||
|
@ -108,7 +108,7 @@ const (
|
||||||
// Process data
|
// Process data
|
||||||
fsm2StateStage1 = fsm.State("state_fsm2_stage1")
|
fsm2StateStage1 = fsm.State("state_fsm2_stage1")
|
||||||
fsm2StateStage2 = fsm.State("state_fsm2_stage2")
|
fsm2StateStage2 = fsm.State("state_fsm2_stage2")
|
||||||
// Cancelled with internal event
|
// Canceled with internal event
|
||||||
fsm2StateCanceledByInternal = fsm.State("state_fsm2_canceled")
|
fsm2StateCanceledByInternal = fsm.State("state_fsm2_canceled")
|
||||||
// Out endpoint to switch
|
// Out endpoint to switch
|
||||||
fsm2StateOutToFSM3 = fsm.State("state_fsm2_out_to_fsm3")
|
fsm2StateOutToFSM3 = fsm.State("state_fsm2_out_to_fsm3")
|
||||||
|
|
|
@ -3,17 +3,15 @@ package dkg_proposal_fsm
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/depools/dc4bc/fsm/config"
|
||||||
"github.com/depools/dc4bc/fsm/fsm"
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pub keys
|
// 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) {
|
func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
@ -41,7 +39,12 @@ func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, arg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.PublicKey, request.PubKey)
|
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.UpdatedAt = request.CreatedAt
|
||||||
dkgProposalParticipant.Status = internal.PubKeyConfirmed
|
dkgProposalParticipant.Status = internal.PubKeyConfirmed
|
||||||
|
|
||||||
|
@ -50,6 +53,55 @@ func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, arg
|
||||||
return
|
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 := len(m.payload.DKGProposalPayload)
|
||||||
|
for _, participant := range m.payload.DKGProposalPayload {
|
||||||
|
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 {
|
||||||
|
participant.Status = internal.CommitAwaitConfirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Commits
|
// Commits
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
|
@ -79,6 +131,11 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dkgProposalParticipant.Status != internal.CommitAwaitConfirmation {
|
||||||
|
err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Commit, request.Commit)
|
copy(dkgProposalParticipant.Commit, request.Commit)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
dkgProposalParticipant.Status = internal.CommitConfirmed
|
dkgProposalParticipant.Status = internal.CommitConfirmed
|
||||||
|
@ -88,6 +145,55 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
|
||||||
return
|
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 := len(m.payload.DKGProposalPayload)
|
||||||
|
for _, participant := range m.payload.DKGProposalPayload {
|
||||||
|
if participant.Status == internal.CommitAwaitConfirmation {
|
||||||
|
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).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 {
|
||||||
|
participant.Status = internal.DealAwaitConfirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Deals
|
// Deals
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
|
@ -117,6 +223,11 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dkgProposalParticipant.Status != internal.DealAwaitConfirmation {
|
||||||
|
err = errors.New(fmt.Sprintf("cannot confirm deal with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Deal, request.Deal)
|
copy(dkgProposalParticipant.Deal, request.Deal)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
dkgProposalParticipant.Status = internal.DealConfirmed
|
dkgProposalParticipant.Status = internal.DealConfirmed
|
||||||
|
@ -126,6 +237,55 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
|
||||||
return
|
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 := len(m.payload.DKGProposalPayload)
|
||||||
|
for _, participant := range m.payload.DKGProposalPayload {
|
||||||
|
if participant.Status == internal.DealAwaitConfirmation {
|
||||||
|
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).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 {
|
||||||
|
participant.Status = internal.ResponseAwaitConfirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Responses
|
// Responses
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
|
@ -155,6 +315,11 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dkgProposalParticipant.Status != internal.ResponseAwaitConfirmation {
|
||||||
|
err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Response, request.Response)
|
copy(dkgProposalParticipant.Response, request.Response)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
||||||
|
@ -164,6 +329,51 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
|
||||||
return
|
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 := len(m.payload.DKGProposalPayload)
|
||||||
|
for _, participant := range m.payload.DKGProposalPayload {
|
||||||
|
if participant.Status == internal.ResponseAwaitConfirmation {
|
||||||
|
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).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
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
|
@ -196,7 +406,7 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
||||||
switch inEvent {
|
switch inEvent {
|
||||||
case EventDKGPubKeyConfirmationError:
|
case EventDKGPubKeyConfirmationError:
|
||||||
switch dkgProposalParticipant.Status {
|
switch dkgProposalParticipant.Status {
|
||||||
case internal.PubKeyConAwaitConfirmation:
|
case internal.PubKeyAwaitConfirmation:
|
||||||
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
|
||||||
case internal.PubKeyConfirmed:
|
case internal.PubKeyConfirmed:
|
||||||
err = errors.New("{Status} already confirmed")
|
err = errors.New("{Status} already confirmed")
|
||||||
|
|
|
@ -12,61 +12,67 @@ const (
|
||||||
StateDkgInitial = StateDkgPubKeysAwaitConfirmations
|
StateDkgInitial = StateDkgPubKeysAwaitConfirmations
|
||||||
|
|
||||||
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
|
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
|
||||||
// Cancelled
|
// Canceled
|
||||||
StateDkgPubKeysAwaitCancelled = fsm.State("state_dkg_pub_keys_await_cancelled")
|
StateDkgPubKeysAwaitCanceled = fsm.State("state_dkg_pub_keys_await_canceled")
|
||||||
StateDkgPubKeysAwaitCancelledByTimeout = fsm.State("state_dkg_pub_keys_await_cancelled_by_timeout")
|
StateDkgPubKeysAwaitCanceledByTimeout = fsm.State("state_dkg_pub_keys_await_canceled_by_timeout")
|
||||||
// Confirmed
|
// Confirmed
|
||||||
StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
|
// StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
|
||||||
|
|
||||||
// Sending dkg commits
|
// Sending dkg commits
|
||||||
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
|
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations")
|
||||||
// Cancelled
|
// Canceled
|
||||||
StateDkgCommitsAwaitCancelled = fsm.State("state_dkg_commits_await_cancelled")
|
StateDkgCommitsAwaitCanceled = fsm.State("state_dkg_commits_await_canceled")
|
||||||
StateDkgCommitsAwaitCancelledByTimeout = fsm.State("state_dkg_commits_await_cancelled_by_timeout")
|
StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout")
|
||||||
// Confirmed
|
// Confirmed
|
||||||
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
|
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
|
||||||
|
|
||||||
// Sending dkg deals
|
// Sending dkg deals
|
||||||
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
|
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
|
||||||
// Cancelled
|
// Canceled
|
||||||
StateDkgDealsAwaitCancelled = fsm.State("state_dkg_deals_await_cancelled")
|
StateDkgDealsAwaitCanceled = fsm.State("state_dkg_deals_await_canceled")
|
||||||
StateDkgDealsAwaitCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
|
StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_sending_canceled_by_timeout")
|
||||||
// Confirmed
|
// 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")
|
StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations")
|
||||||
// Cancelled
|
// Canceled
|
||||||
StateDkgResponsesAwaitCancelled = fsm.State("state_dkg_responses_await_cancelled")
|
StateDkgResponsesAwaitCanceled = fsm.State("state_dkg_responses_await_canceled")
|
||||||
StateDkgResponsesAwaitCancelledByTimeout = fsm.State("state_dkg_responses_sending_cancelled_by_timeout")
|
StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout")
|
||||||
// Confirmed
|
// Confirmed
|
||||||
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed")
|
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed")
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
eventDKGPubKeysSendingRequiredAuto = fsm.Event("event_dkg_pub_key_sending_required_internal")
|
|
||||||
|
|
||||||
EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received")
|
eventAutoDKGInitialInternal = fsm.Event("event_dkg_init_internal")
|
||||||
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")
|
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
|
eventAutoValidatePubKeysInternal = 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")
|
EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received")
|
||||||
EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error")
|
EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error")
|
||||||
EventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal")
|
eventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal")
|
||||||
EventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal")
|
eventDKGCommitsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_error_internal")
|
||||||
|
eventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal")
|
||||||
|
|
||||||
EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal")
|
// EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal")
|
||||||
|
|
||||||
EventDKGDealConfirmationReceived = fsm.Event("event_dkg_deal_confirm_received")
|
EventDKGDealConfirmationReceived = fsm.Event("event_dkg_deal_confirm_received")
|
||||||
EventDKGDealConfirmationError = fsm.Event("event_dkg_deal_confirm_canceled_by_error")
|
EventDKGDealConfirmationError = fsm.Event("event_dkg_deal_confirm_canceled_by_error")
|
||||||
EventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal")
|
eventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal")
|
||||||
|
eventDKGDealsConfirmationCancelByErrorInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_error_internal")
|
||||||
EventDKGResponsesSendingRequiredInternal = fsm.Event("event_dkg_responses_sending_required_internal")
|
eventDKGDealsConfirmedInternal = fsm.Event("event_dkg_deals_confirmed_internal")
|
||||||
|
|
||||||
EventDKGResponseConfirmationReceived = fsm.Event("event_dkg_response_confirm_received")
|
EventDKGResponseConfirmationReceived = fsm.Event("event_dkg_response_confirm_received")
|
||||||
EventDKGResponseConfirmationError = fsm.Event("event_dkg_response_confirm_canceled_by_error")
|
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")
|
||||||
|
|
||||||
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
|
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
|
||||||
)
|
)
|
||||||
|
@ -89,49 +95,57 @@ func New() internal.DumpedMachineProvider {
|
||||||
// Switch to pub keys required
|
// Switch to pub keys required
|
||||||
// {Name: eventDKGPubKeysSendingRequiredAuto, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunAfter},
|
// {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
|
// Pub keys sending
|
||||||
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
|
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
|
||||||
// Cancelled
|
// Canceled
|
||||||
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelled},
|
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceled},
|
||||||
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelledByTimeout, IsInternal: true},
|
|
||||||
|
{Name: eventAutoValidatePubKeysInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true},
|
||||||
|
|
||||||
|
{Name: eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceledByTimeout, IsInternal: true},
|
||||||
// Confirmed
|
// 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
|
// Switch to commits required
|
||||||
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
|
//{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
|
||||||
|
|
||||||
// Commits
|
// Commits
|
||||||
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
|
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
|
||||||
// Cancelled
|
// Canceled
|
||||||
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelled},
|
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceled},
|
||||||
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelledByTimeout, IsInternal: true},
|
{Name: eventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByTimeout, IsInternal: true},
|
||||||
// Confirmed
|
// Confirmed
|
||||||
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmed, IsInternal: true},
|
{Name: eventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||||
|
|
||||||
// Switch to deals required
|
// Switch to deals required
|
||||||
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
// {Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
|
||||||
|
|
||||||
// Deals
|
// Deals
|
||||||
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
|
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
|
||||||
// Cancelled
|
// Canceled
|
||||||
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelled},
|
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceled},
|
||||||
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelledByTimeout, IsInternal: true},
|
{Name: eventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceledByTimeout, IsInternal: true},
|
||||||
|
|
||||||
// Switch to responses required
|
// Switch to responses required
|
||||||
{Name: EventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
|
// {Name: eventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
|
||||||
|
|
||||||
// Deals
|
// Deals
|
||||||
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
|
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
|
||||||
// Cancelled
|
// Canceled
|
||||||
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelled},
|
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceled},
|
||||||
{Name: EventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelledByTimeout, IsInternal: true},
|
{Name: eventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByTimeout, IsInternal: true},
|
||||||
|
|
||||||
// Done
|
// 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{
|
fsm.Callbacks{
|
||||||
|
|
||||||
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
|
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
|
||||||
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
|
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
|
||||||
|
// actionValidateDkgProposalPubKeys
|
||||||
|
eventAutoValidatePubKeysInternal: machine.actionValidateDkgProposalPubKeys,
|
||||||
|
|
||||||
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
|
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
|
||||||
EventDKGCommitConfirmationError: machine.actionConfirmationError,
|
EventDKGCommitConfirmationError: machine.actionConfirmationError,
|
||||||
|
|
|
@ -5,11 +5,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota
|
|
||||||
SignatureConfirmed
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfirmationProposal struct {
|
type ConfirmationProposal struct {
|
||||||
Quorum SignatureProposalQuorum
|
Quorum SignatureProposalQuorum
|
||||||
CreatedAt *time.Time
|
CreatedAt *time.Time
|
||||||
|
@ -23,22 +18,22 @@ type SignatureProposalParticipant struct {
|
||||||
PublicKey *rsa.PublicKey
|
PublicKey *rsa.PublicKey
|
||||||
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
|
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
|
||||||
InvitationSecret string
|
InvitationSecret string
|
||||||
Status SignatureProposalParticipantStatus
|
Status ParticipantStatus
|
||||||
UpdatedAt *time.Time
|
UpdatedAt *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unique alias for map iteration - Public Key Fingerprint
|
// Unique alias for map iteration - Public Key Fingerprint
|
||||||
// Excludes array merge and rotate operations
|
// Excludes array merge and rotate operations
|
||||||
type SignatureProposalQuorum map[string]SignatureProposalParticipant
|
type SignatureProposalQuorum map[string]*SignatureProposalParticipant
|
||||||
|
|
||||||
type SignatureProposalParticipantStatus uint8
|
type ParticipantStatus uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota
|
SignatureConfirmationAwaitConfirmation ParticipantStatus = iota
|
||||||
SignatureConfirmationConfirmed
|
SignatureConfirmationConfirmed
|
||||||
SignatureConfirmationDeclined
|
SignatureConfirmationDeclined
|
||||||
SignatureConfirmationError
|
SignatureConfirmationError
|
||||||
PubKeyConAwaitConfirmation
|
PubKeyAwaitConfirmation
|
||||||
PubKeyConfirmed
|
PubKeyConfirmed
|
||||||
PubKeyConfirmationError
|
PubKeyConfirmationError
|
||||||
CommitAwaitConfirmation
|
CommitAwaitConfirmation
|
||||||
|
@ -52,27 +47,21 @@ const (
|
||||||
ResponseConfirmationError
|
ResponseConfirmationError
|
||||||
)
|
)
|
||||||
|
|
||||||
type DKGProposal struct {
|
|
||||||
Quorum map[int]DKGProposalParticipant
|
|
||||||
CreatedAt *time.Time
|
|
||||||
ExpiresAt *time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type DKGProposalParticipant struct {
|
type DKGProposalParticipant struct {
|
||||||
Title string
|
Title string
|
||||||
PublicKey []byte
|
PubKey []byte
|
||||||
Commit []byte
|
Commit []byte
|
||||||
Deal []byte
|
Deal []byte
|
||||||
Response []byte
|
Response []byte
|
||||||
Status DKGProposalParticipantStatus
|
Status ParticipantStatus
|
||||||
UpdatedAt *time.Time
|
UpdatedAt *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type DKGProposalQuorum map[int]DKGProposalParticipant
|
type DKGProposalQuorum map[int]*DKGProposalParticipant
|
||||||
|
|
||||||
type DKGProposalParticipantStatus uint8
|
type DKGProposalParticipantStatus uint8
|
||||||
|
|
||||||
func (s DKGProposalParticipantStatus) String() string {
|
func (s ParticipantStatus) String() string {
|
||||||
var str = "undefined"
|
var str = "undefined"
|
||||||
switch s {
|
switch s {
|
||||||
case SignatureConfirmationAwaitConfirmation:
|
case SignatureConfirmationAwaitConfirmation:
|
||||||
|
@ -83,8 +72,8 @@ func (s DKGProposalParticipantStatus) String() string {
|
||||||
str = "SignatureConfirmationDeclined"
|
str = "SignatureConfirmationDeclined"
|
||||||
case SignatureConfirmationError:
|
case SignatureConfirmationError:
|
||||||
str = "SignatureConfirmationError"
|
str = "SignatureConfirmationError"
|
||||||
case PubKeyConAwaitConfirmation:
|
case PubKeyAwaitConfirmation:
|
||||||
str = "PubKeyConAwaitConfirmation"
|
str = "PubKeyAwaitConfirmation"
|
||||||
case PubKeyConfirmed:
|
case PubKeyConfirmed:
|
||||||
str = "PubKeyConfirmed"
|
str = "PubKeyConfirmed"
|
||||||
case PubKeyConfirmationError:
|
case PubKeyConfirmationError:
|
||||||
|
|
|
@ -149,6 +149,13 @@ func (i *FSMInstance) Id() string {
|
||||||
return ""
|
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
|
// TODO: Add encryption
|
||||||
func (d *FSMDump) Marshal() ([]byte, error) {
|
func (d *FSMDump) Marshal() ([]byte, error) {
|
||||||
return json.Marshal(d)
|
return json.Marshal(d)
|
||||||
|
@ -157,7 +164,7 @@ func (d *FSMDump) Marshal() ([]byte, error) {
|
||||||
// TODO: Add decryption
|
// TODO: Add decryption
|
||||||
func (d *FSMDump) Unmarshal(data []byte) error {
|
func (d *FSMDump) Unmarshal(data []byte) error {
|
||||||
if d == nil {
|
if d == nil {
|
||||||
return errors.New("dump struct is not initialized")
|
return errors.New("dump is not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Unmarshal(data, d)
|
return json.Unmarshal(data, d)
|
||||||
|
|
|
@ -36,6 +36,8 @@ var (
|
||||||
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
||||||
CreatedAt: &tm,
|
CreatedAt: &tm,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testFSMDump []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -118,21 +120,41 @@ func compareState(t *testing.T, expected fsm.State, got fsm.State) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Workflow(t *testing.T) {
|
// Test Workflow
|
||||||
testFSMInstance, err := Create()
|
|
||||||
|
|
||||||
log.Println(testFSMInstance.Id())
|
func Test_SignatureProposal_Init(t *testing.T) {
|
||||||
|
testFSMInstance, err := Create()
|
||||||
|
|
||||||
compareErrNil(t, err)
|
compareErrNil(t, err)
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
|
transactionId := testFSMInstance.Id()
|
||||||
|
|
||||||
|
if transactionId == "" {
|
||||||
|
t.Fatalf("expected {transactionId for dump}")
|
||||||
|
}
|
||||||
|
|
||||||
if testFSMInstance.machine.Name() != spf.FsmName {
|
if testFSMInstance.machine.Name() != spf.FsmName {
|
||||||
t.Fatalf("expected machine name {%s}", spf.FsmName)
|
t.Fatalf("expected machine name {%s}", spf.FsmName)
|
||||||
}
|
}
|
||||||
|
|
||||||
compareState(t, spf.StateParticipantsConfirmationsInit, testFSMInstance.machine.State())
|
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)
|
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
|
||||||
|
|
||||||
compareErrNil(t, err)
|
compareErrNil(t, err)
|
||||||
|
@ -143,10 +165,251 @@ func Test_Workflow(t *testing.T) {
|
||||||
|
|
||||||
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||||
|
|
||||||
if testFSMInstance.Id() != testTransactionId {
|
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||||
t.Fatalf("expected {testTransactionId}")
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected response {SignatureProposalParticipantInvitationsResponse}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(testParticipantsListResponse) != len(testParticipantsListRequest.Participants) {
|
||||||
|
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(testParticipantsListResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
participantsMap := map[int]*responses.SignatureProposalParticipantInvitationEntry{}
|
||||||
|
|
||||||
|
for _, participant := range testParticipantsListResponse {
|
||||||
|
if _, ok := participantsMap[participant.ParticipantId]; ok {
|
||||||
|
t.Fatalf("expected unique {ParticipantId}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if participant.Title == "" {
|
||||||
|
t.Fatalf("expected not empty {Title}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if participant.EncryptedInvitation == "" {
|
||||||
|
t.Fatalf("expected not empty {DecryptedInvitation}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if participant.PubKeyFingerprint == "" {
|
||||||
|
t.Fatalf("expected not empty {PubKeyFingerprint}")
|
||||||
|
}
|
||||||
|
|
||||||
|
participantsMap[participant.ParticipantId] = participant
|
||||||
|
}
|
||||||
|
|
||||||
|
tm = tm.Add(10 * time.Hour)
|
||||||
|
|
||||||
|
participantsCount := len(participantsMap)
|
||||||
|
|
||||||
|
participantCounter := participantsCount
|
||||||
|
|
||||||
|
for _, participant := range participantsMap {
|
||||||
|
participantCounter--
|
||||||
|
testFSMInstance, err = FromDump(dump)
|
||||||
|
|
||||||
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
|
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
||||||
|
t.Fatalf("not found external user data for response fingerprint")
|
||||||
|
}
|
||||||
|
|
||||||
|
r := rand.Reader
|
||||||
|
encrypted, err := rsa.DecryptPKCS1v15(r, testParticipants[participant.PubKeyFingerprint].PrivKey, []byte(participant.EncryptedInvitation))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot encrypt {DecryptedInvitation} with private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{
|
||||||
|
PubKeyFingerprint: participant.PubKeyFingerprint,
|
||||||
|
DecryptedInvitation: string(encrypted),
|
||||||
|
CreatedAt: &tm,
|
||||||
|
})
|
||||||
|
|
||||||
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
compareDumpNotZero(t, dump)
|
||||||
|
|
||||||
|
compareFSMResponseNotNil(t, fsmResponse)
|
||||||
|
|
||||||
|
if participantCounter > 0 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
/*if participantCounter > 0 {
|
||||||
|
compareState(t, dpf.StateDkgPubKeysAwaitConfirmations, fsmResponse.State)
|
||||||
|
} else {
|
||||||
|
compareState(t, dpf.StateDkgCommitsAwaitConfirmations, fsmResponse.State)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
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.EventDKGDealConfirmationReceived, requests.DKGProposalDealConfirmationRequest{
|
||||||
|
ParticipantId: participant.ParticipantId,
|
||||||
|
Deal: responseMock,
|
||||||
|
CreatedAt: &tm,
|
||||||
|
})
|
||||||
|
|
||||||
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
compareDumpNotZero(t, dump)
|
||||||
|
|
||||||
|
compareFSMResponseNotNil(t, fsmResponse)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
compareState(t, fsm.StateGlobalDone, fsmResponse.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func Test_SignatureProposal_Negative_By_Decline(t *testing.T) {
|
||||||
|
testFSMInstance, err := FromDump(testFSMDump)
|
||||||
|
|
||||||
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
|
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
|
||||||
|
|
||||||
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
compareDumpNotZero(t, dump)
|
||||||
|
|
||||||
|
compareFSMResponseNotNil(t, fsmResponse)
|
||||||
|
|
||||||
|
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
||||||
|
|
||||||
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -204,7 +467,7 @@ func Test_Workflow(t *testing.T) {
|
||||||
t.Fatalf("cannot encrypt {DecryptedInvitation} with private key")
|
t.Fatalf("cannot encrypt {DecryptedInvitation} with private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{
|
fsmResponse, dump, err = testFSMInstance.Do(spf.EventDeclineProposal, requests.SignatureProposalParticipantRequest{
|
||||||
PubKeyFingerprint: participant.PubKeyFingerprint,
|
PubKeyFingerprint: participant.PubKeyFingerprint,
|
||||||
DecryptedInvitation: string(encrypted),
|
DecryptedInvitation: string(encrypted),
|
||||||
CreatedAt: &tm,
|
CreatedAt: &tm,
|
||||||
|
@ -216,19 +479,9 @@ func Test_Workflow(t *testing.T) {
|
||||||
|
|
||||||
compareFSMResponseNotNil(t, fsmResponse)
|
compareFSMResponseNotNil(t, fsmResponse)
|
||||||
|
|
||||||
if participantCounter > 0 {
|
compareState(t, spf.StateValidationCanceledByParticipant, fsmResponse.State)
|
||||||
if fsmResponse.State != spf.StateAwaitParticipantsConfirmations {
|
|
||||||
t.Fatalf("expected state {%s} got {%s}", spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if fsmResponse.State != dpf.StateDkgInitial {
|
|
||||||
t.Fatalf("expected state {%s} got {%s}", dpf.StateDkgInitial, fsmResponse.State)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state, err := testFSMInstance.State()
|
|
||||||
|
|
||||||
log.Println(err, state)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -3,10 +3,13 @@ package signature_proposal_fsm
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/depools/dc4bc/fsm/config"
|
||||||
"github.com/depools/dc4bc/fsm/fsm"
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"github.com/depools/dc4bc/fsm/types/responses"
|
"github.com/depools/dc4bc/fsm/types/responses"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// init -> awaitingConfirmations
|
// init -> awaitingConfirmations
|
||||||
|
@ -46,12 +49,12 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
||||||
return inEvent, nil, errors.New("cannot parse {PubKey}")
|
return inEvent, nil, errors.New("cannot parse {PubKey}")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{
|
m.payload.ConfirmationProposalPayload[participantId] = &internal.SignatureProposalParticipant{
|
||||||
ParticipantId: index,
|
ParticipantId: index,
|
||||||
Title: participant.Title,
|
Title: participant.Title,
|
||||||
PublicKey: parsedPubKey,
|
PublicKey: parsedPubKey,
|
||||||
InvitationSecret: secret,
|
InvitationSecret: secret,
|
||||||
Status: internal.SignatureAwaitConfirmation,
|
Status: internal.SignatureConfirmationAwaitConfirmation,
|
||||||
UpdatedAt: request.CreatedAt,
|
UpdatedAt: request.CreatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +89,8 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
|
||||||
|
|
||||||
// TODO: Add timeout checking
|
// TODO: Add timeout checking
|
||||||
func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
// m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
// defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
err = errors.New("{arg0} required {SignatureProposalParticipantRequest}")
|
err = errors.New("{arg0} required {SignatureProposalParticipantRequest}")
|
||||||
|
@ -117,30 +120,120 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
|
||||||
return
|
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
|
signatureProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant
|
m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant
|
||||||
|
|
||||||
outEvent, response, err = m.actionValidateProposal(eventValidateProposalInternal)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SignatureProposalFSM) actionValidateProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
// m.payloadMu.Lock()
|
var (
|
||||||
// defer m.payloadMu.Unlock()
|
isContainsDeclined, isContainsExpired bool
|
||||||
|
)
|
||||||
|
|
||||||
|
m.payloadMu.Lock()
|
||||||
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
|
tm := time.Now()
|
||||||
|
|
||||||
unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload)
|
unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload)
|
||||||
for _, participant := range m.payload.ConfirmationProposalPayload {
|
for _, participant := range m.payload.ConfirmationProposalPayload {
|
||||||
if participant.Status == internal.SignatureConfirmed {
|
if participant.Status == internal.SignatureConfirmationAwaitConfirmation {
|
||||||
unconfirmedParticipants--
|
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 {
|
if unconfirmedParticipants > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
outEvent = eventSetProposalValidatedInternal
|
outEvent = eventSetProposalValidatedInternal
|
||||||
|
|
||||||
|
m.actionSetValidatedSignatureProposal(outEvent)
|
||||||
|
|
||||||
return
|
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 = make(internal.DKGProposalQuorum)
|
||||||
|
|
||||||
|
for _, participant := range m.payload.ConfirmationProposalPayload {
|
||||||
|
m.payload.DKGProposalPayload[participant.ParticipantId] = &internal.DKGProposalParticipant{
|
||||||
|
Title: participant.Title,
|
||||||
|
Status: internal.PubKeyAwaitConfirmation,
|
||||||
|
UpdatedAt: participant.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove m.payload.ConfirmationProposalPayload?
|
||||||
|
|
||||||
|
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.ConfirmationProposalPayload {
|
||||||
|
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
||||||
|
ParticipantId: participant.ParticipantId,
|
||||||
|
Title: participant.Title,
|
||||||
|
PubKeyFingerprint: pubKeyFingerprint,
|
||||||
|
Status: uint8(participant.Status),
|
||||||
|
}
|
||||||
|
responseData = append(responseData, responseEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inEvent, responseData, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -19,15 +19,15 @@ const (
|
||||||
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
|
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
|
||||||
|
|
||||||
// Out state
|
// Out state
|
||||||
StateValidationCompleted = fsm.State("state_sig_proposal_completed")
|
|
||||||
|
|
||||||
EventInitProposal = fsm.Event("event_sig_proposal_init")
|
EventInitProposal = fsm.Event("event_sig_proposal_init")
|
||||||
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
|
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
|
||||||
EventDeclineProposal = fsm.Event("event_sig_proposal_decline_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")
|
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
|
// Switch to next fsm
|
||||||
|
|
||||||
|
@ -55,9 +55,10 @@ func New() internal.DumpedMachineProvider {
|
||||||
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
{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)?
|
// Is decline event should auto change state to default, or it process will initiated by client (external emit)?
|
||||||
// Now set for external emitting.
|
// 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?
|
// eventProposalValidate internal or from client?
|
||||||
// yay
|
// yay
|
||||||
|
@ -67,10 +68,11 @@ func New() internal.DumpedMachineProvider {
|
||||||
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
|
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
|
||||||
},
|
},
|
||||||
fsm.Callbacks{
|
fsm.Callbacks{
|
||||||
EventInitProposal: machine.actionInitProposal,
|
EventInitProposal: machine.actionInitProposal,
|
||||||
EventConfirmProposal: machine.actionProposalResponseByParticipant,
|
EventConfirmProposal: machine.actionProposalResponseByParticipant,
|
||||||
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
||||||
eventValidateProposalInternal: machine.actionValidateProposal,
|
eventAutoValidateProposalInternal: machine.actionValidateSignatureProposal,
|
||||||
|
eventSetProposalValidatedInternal: machine.actionSetValidatedSignatureProposal,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return machine
|
return machine
|
||||||
|
|
|
@ -2,14 +2,8 @@ package responses
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
const (
|
// Event: "event_sig_proposal_init"
|
||||||
ProposalConfirmationStatusIdle = iota
|
// States: "__idle"
|
||||||
ProposalConfirmationStatusAccepted
|
|
||||||
ProposalConfirmationStatusCanceled
|
|
||||||
ProposalConfirmationStatusTimeout
|
|
||||||
)
|
|
||||||
|
|
||||||
// States: "validate_proposal"
|
|
||||||
|
|
||||||
type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry
|
type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry
|
||||||
|
|
||||||
|
@ -31,5 +25,5 @@ type SignatureProposalParticipantStatusEntry struct {
|
||||||
ParticipantId int
|
ParticipantId int
|
||||||
Title string
|
Title string
|
||||||
PubKeyFingerprint string
|
PubKeyFingerprint string
|
||||||
Status int
|
Status uint8
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue