dc4bc/fsm/state_machines/dkg_proposal_fsm/actions.go

591 lines
16 KiB
Go
Raw Normal View History

2020-08-06 17:20:13 -07:00
package dkg_proposal_fsm
import (
"errors"
"fmt"
2020-08-11 09:46:18 -07:00
"github.com/depools/dc4bc/fsm/config"
2020-08-06 17:20:13 -07:00
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"github.com/depools/dc4bc/fsm/types/requests"
2020-08-11 09:46:18 -07:00
"time"
2020-08-06 17:20:13 -07:00
)
// Pub keys
2020-08-08 14:44:52 -07:00
func (m *DKGProposalFSM) actionPubKeyConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {DKGProposalPubKeyConfirmationRequest}")
return
}
request, ok := args[0].(requests.DKGProposalPubKeyConfirmationRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalPubKeyConfirmationRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
2020-08-12 06:40:03 -07:00
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
2020-08-11 09:46:18 -07:00
if dkgProposalParticipant.Status != internal.PubKeyAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm pubkey with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
return
}
copy(dkgProposalParticipant.PubKey, request.PubKey)
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.Status = internal.PubKeyConfirmed
2020-08-12 06:40:03 -07:00
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
2020-08-06 17:20:13 -07:00
return
}
2020-08-11 09:46:18 -07:00
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()
2020-08-12 06:40:03 -07:00
unconfirmedParticipants := m.payload.DKGQuorumCount()
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
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
2020-08-12 06:40:03 -07:00
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
participant.Status = internal.CommitAwaitConfirmation
}
return
}
2020-08-06 17:20:13 -07:00
// Commits
2020-08-08 14:44:52 -07:00
func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {DKGProposalCommitConfirmationRequest}")
return
}
request, ok := args[0].(requests.DKGProposalCommitConfirmationRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalCommitConfirmationRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
2020-08-12 06:40:03 -07:00
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
2020-08-11 09:46:18 -07:00
if dkgProposalParticipant.Status != internal.CommitAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
return
}
2020-08-08 14:44:52 -07:00
copy(dkgProposalParticipant.Commit, request.Commit)
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.Status = internal.CommitConfirmed
2020-08-12 06:40:03 -07:00
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
2020-08-06 17:20:13 -07:00
return
}
2020-08-11 09:46:18 -07:00
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()
2020-08-12 06:40:03 -07:00
unconfirmedParticipants := m.payload.DKGQuorumCount()
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
if participant.Status == internal.CommitAwaitConfirmation {
2020-08-12 06:40:03 -07:00
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
2020-08-11 09:46:18 -07:00
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
2020-08-12 06:40:03 -07:00
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
participant.Status = internal.DealAwaitConfirmation
}
return
}
2020-08-06 17:20:13 -07:00
// Deals
2020-08-08 14:44:52 -07:00
func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {DKGProposalDealConfirmationRequest}")
return
}
request, ok := args[0].(requests.DKGProposalDealConfirmationRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalDealConfirmationRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
2020-08-12 06:40:03 -07:00
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
2020-08-11 09:46:18 -07:00
if dkgProposalParticipant.Status != internal.DealAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm deal with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
return
}
2020-08-08 14:44:52 -07:00
copy(dkgProposalParticipant.Deal, request.Deal)
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.Status = internal.DealConfirmed
2020-08-12 06:40:03 -07:00
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
2020-08-06 17:20:13 -07:00
return
}
2020-08-11 09:46:18 -07:00
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()
2020-08-12 06:40:03 -07:00
unconfirmedParticipants := m.payload.DKGQuorumCount()
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
if participant.Status == internal.DealAwaitConfirmation {
2020-08-12 06:40:03 -07:00
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
2020-08-11 09:46:18 -07:00
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
2020-08-12 06:40:03 -07:00
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
participant.Status = internal.ResponseAwaitConfirmation
}
return
}
// Responses
2020-08-08 14:44:52 -07:00
func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {DKGProposalResponseConfirmationRequest}")
return
}
request, ok := args[0].(requests.DKGProposalResponseConfirmationRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalResponseConfirmationRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
2020-08-12 06:40:03 -07:00
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
2020-08-11 09:46:18 -07:00
if dkgProposalParticipant.Status != internal.ResponseAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm response with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
return
}
2020-08-08 14:44:52 -07:00
copy(dkgProposalParticipant.Response, request.Response)
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.Status = internal.ResponseConfirmed
2020-08-12 06:40:03 -07:00
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
2020-08-06 17:20:13 -07:00
return
}
2020-08-11 09:46:18 -07:00
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()
2020-08-12 06:40:03 -07:00
unconfirmedParticipants := m.payload.DKGQuorumCount()
for _, participant := range m.payload.DKGProposalPayload.Quorum {
2020-08-11 09:46:18 -07:00
if participant.Status == internal.ResponseAwaitConfirmation {
2020-08-12 06:40:03 -07:00
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
2020-08-11 09:46:18 -07:00
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
2020-08-12 06:40:03 -07:00
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
)
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 {
unconfirmedParticipants--
}
}
}
if isContainsError {
outEvent = eventDKGMasterKeyConfirmationCancelByErrorInternal
return
}
if isContainsExpired {
outEvent = eventDKGMasterKeyConfirmationCancelByTimeoutInternal
return
}
// The are no declined and timed out participants, check for all confirmations
if unconfirmedParticipants > 0 {
return
}
outEvent = eventDKGMasterKeyConfirmedInternal
2020-08-11 09:46:18 -07:00
return
}
// Errors
2020-08-08 14:44:52 -07:00
func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {DKGProposalConfirmationErrorRequest}")
return
}
request, ok := args[0].(requests.DKGProposalConfirmationErrorRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalConfirmationErrorRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
2020-08-12 06:40:03 -07:00
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
// TODO: Move to methods
2020-08-08 14:44:52 -07:00
switch inEvent {
case EventDKGPubKeyConfirmationError:
switch dkgProposalParticipant.Status {
2020-08-11 09:46:18 -07:00
case internal.PubKeyAwaitConfirmation:
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
case internal.PubKeyConfirmed:
err = errors.New("{Status} already confirmed")
case internal.PubKeyConfirmationError:
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.PubKeyConfirmationError))
default:
err = errors.New(fmt.Sprintf(
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
dkgProposalParticipant.Status,
internal.PubKeyConfirmationError,
))
}
case EventDKGCommitConfirmationError:
switch dkgProposalParticipant.Status {
case internal.CommitAwaitConfirmation:
dkgProposalParticipant.Status = internal.CommitConfirmationError
case internal.CommitConfirmed:
err = errors.New("{Status} already confirmed")
case internal.CommitConfirmationError:
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.CommitConfirmationError))
default:
err = errors.New(fmt.Sprintf(
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
dkgProposalParticipant.Status,
2020-08-12 06:40:03 -07:00
internal.CommitConfirmationError,
))
}
case EventDKGDealConfirmationError:
switch dkgProposalParticipant.Status {
case internal.DealAwaitConfirmation:
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.Status = internal.DealConfirmationError
case internal.DealConfirmed:
err = errors.New("{Status} already confirmed")
case internal.DealConfirmationError:
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.DealConfirmationError))
default:
err = errors.New(fmt.Sprintf(
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
dkgProposalParticipant.Status,
2020-08-12 06:40:03 -07:00
internal.DealConfirmationError,
))
}
case EventDKGResponseConfirmationError:
switch dkgProposalParticipant.Status {
case internal.ResponseAwaitConfirmation:
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.Status = internal.ResponseConfirmationError
case internal.ResponseConfirmed:
err = errors.New("{Status} already confirmed")
case internal.ResponseConfirmationError:
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.ResponseConfirmationError))
default:
err = errors.New(fmt.Sprintf(
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
dkgProposalParticipant.Status,
2020-08-12 06:40:03 -07:00
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:
2020-08-08 14:44:52 -07:00
err = errors.New(fmt.Sprintf("{%s} event cannot be used for action {actionConfirmationError}", inEvent))
}
if err != nil {
return
}
2020-08-12 06:40:03 -07:00
dkgProposalParticipant.Error = request.Error
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
2020-08-12 06:40:03 -07:00
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
2020-08-08 14:44:52 -07:00
// TODO: Add outEvent
2020-08-06 17:20:13 -07:00
return
}