This commit is contained in:
x88 2020-08-18 14:27:30 +03:00
parent 19640adcf0
commit 3b4e09f6cb
15 changed files with 485 additions and 40 deletions

View File

@ -7,4 +7,5 @@ const (
ParticipantsMinCount = 3 ParticipantsMinCount = 3
SignatureProposalConfirmationDeadline = time.Hour * 24 SignatureProposalConfirmationDeadline = time.Hour * 24
DkgConfirmationDeadline = time.Hour * 24 DkgConfirmationDeadline = time.Hour * 24
SigningConfirmationDeadline = time.Hour * 24
) )

View File

@ -0,0 +1,4 @@
Events

View File

@ -15,7 +15,7 @@ const (
// Sending dkg commits // Sending dkg commits
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations") StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations")
// Canceled // Canceled
StateDkgCommitsAwaitCanceled = fsm.State("state_dkg_commits_await_canceled") StateDkgCommitsAwaitCanceledByError = fsm.State("state_dkg_commits_await_canceled_by_error")
StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout") StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout")
// Confirmed // Confirmed
StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected") StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected")
@ -23,20 +23,20 @@ const (
// Sending dkg deals // Sending dkg deals
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations") StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
// Canceled // Canceled
StateDkgDealsAwaitCanceled = fsm.State("state_dkg_deals_await_canceled") StateDkgDealsAwaitCanceledByError = fsm.State("state_dkg_deals_await_canceled_by_error")
StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_await_canceled_by_timeout") StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_await_canceled_by_timeout")
// Confirmed // Confirmed
//StateDkgDealsCollected = fsm.State("state_dkg_deals_collected") //StateDkgDealsCollected = fsm.State("state_dkg_deals_collected")
StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations") StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations")
// Canceled // Canceled
StateDkgResponsesAwaitCanceled = fsm.State("state_dkg_responses_await_canceled") StateDkgResponsesAwaitCanceledByError = fsm.State("state_dkg_responses_await_canceled_by_error")
StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout") StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout")
// Confirmed // Confirmed
StateDkgResponsesCollected = fsm.State("state_dkg_responses_collected") StateDkgResponsesCollected = fsm.State("state_dkg_responses_collected")
StateDkgMasterKeyAwaitConfirmations = fsm.State("state_dkg_master_key_await_confirmations") StateDkgMasterKeyAwaitConfirmations = fsm.State("state_dkg_master_key_await_confirmations")
StateDkgMasterKeyAwaitCanceled = fsm.State("state_dkg_master_key_await_canceled") StateDkgMasterKeyAwaitCanceledByError = fsm.State("state_dkg_master_key_await_canceled_by_error")
StateDkgMasterKeyAwaitCanceledByTimeout = fsm.State("state_dkg_master_key_await_canceled_by_timeout") StateDkgMasterKeyAwaitCanceledByTimeout = fsm.State("state_dkg_master_key_await_canceled_by_timeout")
StateDkgMasterKeyCollected = fsm.State("state_dkg_master_key_collected") StateDkgMasterKeyCollected = fsm.State("state_dkg_master_key_collected")
@ -104,7 +104,7 @@ func New() internal.DumpedMachineProvider {
// Commits // Commits
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations}, {Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
// Canceled // Canceled
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceled}, {Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByError},
{Name: eventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByTimeout, IsInternal: true}, {Name: eventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCanceledByTimeout, IsInternal: true},
{Name: eventAutoDKGValidateConfirmationCommitsInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true}, {Name: eventAutoDKGValidateConfirmationCommitsInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -115,7 +115,7 @@ func New() internal.DumpedMachineProvider {
// Deals // Deals
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations}, {Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
// Canceled // Canceled
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceled}, {Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitCanceledByError},
{Name: eventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true}, {Name: eventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
{Name: eventAutoDKGValidateConfirmationDealsInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true, IsAuto: true}, {Name: eventAutoDKGValidateConfirmationDealsInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -124,7 +124,7 @@ func New() internal.DumpedMachineProvider {
// Responses // Responses
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations}, {Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
// Canceled // Canceled
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceled}, {Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByError},
{Name: eventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByTimeout, IsInternal: true}, {Name: eventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCanceledByTimeout, IsInternal: true},
{Name: eventAutoDKGValidateResponsesConfirmationInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true, IsAuto: true}, {Name: eventAutoDKGValidateResponsesConfirmationInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -134,15 +134,13 @@ func New() internal.DumpedMachineProvider {
// Master key // Master key
{Name: EventDKGMasterKeyConfirmationReceived, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations}, {Name: EventDKGMasterKeyConfirmationReceived, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations},
{Name: EventDKGMasterKeyConfirmationError, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceled}, {Name: EventDKGMasterKeyConfirmationError, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceledByError},
{Name: eventDKGMasterKeyConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceledByTimeout, IsInternal: true}, {Name: eventDKGMasterKeyConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitCanceledByTimeout, IsInternal: true},
{Name: eventAutoDKGValidateMasterKeyConfirmationInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true, IsAuto: 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 // Done
// {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true}, {Name: eventDKGMasterKeyConfirmedInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyCollected, IsInternal: true},
}, },
fsm.Callbacks{ fsm.Callbacks{
EventDKGInitProcess: machine.actionInitDKGProposal, EventDKGInitProcess: machine.actionInitDKGProposal,

View File

@ -13,6 +13,7 @@ type DumpedMachineStatePayload struct {
TransactionId string TransactionId string
SignatureProposalPayload *SignatureConfirmation SignatureProposalPayload *SignatureConfirmation
DKGProposalPayload *DKGConfirmation DKGProposalPayload *DKGConfirmation
SigningProposalPayload *SigningConfirmation
} }
// Signature quorum // Signature quorum
@ -78,3 +79,35 @@ func (p *DumpedMachineStatePayload) DKGQuorumUpdate(id int, participant *DKGProp
} }
return return
} }
// Signing quorum
func (p *DumpedMachineStatePayload) SigningQuorumCount() int {
var count int
if p.SigningProposalPayload.Quorum != nil {
count = len(p.SigningProposalPayload.Quorum)
}
return count
}
func (p *DumpedMachineStatePayload) SigningQuorumExists(id int) bool {
var exists bool
if p.SigningProposalPayload.Quorum != nil {
_, exists = p.SigningProposalPayload.Quorum[id]
}
return exists
}
func (p *DumpedMachineStatePayload) SigningQuorumGet(id int) (participant *SigningProposalParticipant) {
if p.SigningProposalPayload.Quorum != nil {
participant, _ = p.SigningProposalPayload.Quorum[id]
}
return
}
func (p *DumpedMachineStatePayload) SigningQuorumUpdate(id int, participant *SigningProposalParticipant) {
if p.SigningProposalPayload.Quorum != nil {
p.SigningProposalPayload.Quorum[id] = participant
}
return
}

View File

@ -5,6 +5,30 @@ import (
"time" "time"
) )
type ConfirmationParticipantStatus uint8
const (
SigConfirmationAwaitConfirmation ConfirmationParticipantStatus = iota
SigConfirmationConfirmed
SigConfirmationDeclined
SigConfirmationError
)
func (s ConfirmationParticipantStatus) String() string {
var str = "undefined"
switch s {
case SigConfirmationAwaitConfirmation:
str = "SigConfirmationAwaitConfirmation"
case SigConfirmationConfirmed:
str = "SigConfirmationConfirmed"
case SigConfirmationDeclined:
str = "SigConfirmationDeclined"
case SigConfirmationError:
str = "SigConfirmationError"
}
return str
}
type SignatureConfirmation struct { type SignatureConfirmation struct {
Quorum SignatureProposalQuorum Quorum SignatureProposalQuorum
CreatedAt time.Time CreatedAt time.Time
@ -19,7 +43,7 @@ type SignatureProposalParticipant struct {
DkgPubKey []byte DkgPubKey []byte
// For validation user confirmation: sign(InvitationSecret, PubKey) => user // For validation user confirmation: sign(InvitationSecret, PubKey) => user
InvitationSecret string InvitationSecret string
Status ParticipantStatus Status ConfirmationParticipantStatus
UpdatedAt time.Time UpdatedAt time.Time
} }
@ -27,14 +51,12 @@ type SignatureProposalParticipant struct {
// Excludes array merge and rotate operations // Excludes array merge and rotate operations
type SignatureProposalQuorum map[string]*SignatureProposalParticipant type SignatureProposalQuorum map[string]*SignatureProposalParticipant
type ParticipantStatus uint8 // DKG proposal
type DKGParticipantStatus uint8
const ( const (
SignatureConfirmationAwaitConfirmation ParticipantStatus = iota CommitAwaitConfirmation DKGParticipantStatus = iota
SignatureConfirmationConfirmed
SignatureConfirmationDeclined
SignatureConfirmationError
CommitAwaitConfirmation
CommitConfirmed CommitConfirmed
CommitConfirmationError CommitConfirmationError
DealAwaitConfirmation DealAwaitConfirmation
@ -55,7 +77,7 @@ type DKGProposalParticipant struct {
Deal []byte Deal []byte
Response []byte Response []byte
MasterKey []byte MasterKey []byte
Status ParticipantStatus Status DKGParticipantStatus
Error error Error error
UpdatedAt time.Time UpdatedAt time.Time
} }
@ -70,17 +92,9 @@ type DKGConfirmation struct {
type DKGProposalParticipantStatus uint8 type DKGProposalParticipantStatus uint8
func (s ParticipantStatus) String() string { func (s DKGParticipantStatus) String() string {
var str = "undefined" var str = "undefined"
switch s { switch s {
case SignatureConfirmationAwaitConfirmation:
str = "SignatureConfirmationAwaitConfirmation"
case SignatureConfirmationConfirmed:
str = "SignatureConfirmationConfirmed"
case SignatureConfirmationDeclined:
str = "SignatureConfirmationDeclined"
case SignatureConfirmationError:
str = "SignatureConfirmationError"
case CommitAwaitConfirmation: case CommitAwaitConfirmation:
str = "CommitAwaitConfirmation" str = "CommitAwaitConfirmation"
case CommitConfirmed: case CommitConfirmed:
@ -99,6 +113,67 @@ func (s ParticipantStatus) String() string {
str = "ResponseConfirmed" str = "ResponseConfirmed"
case ResponseConfirmationError: case ResponseConfirmationError:
str = "ResponseConfirmationError" str = "ResponseConfirmationError"
case MasterKeyAwaitConfirmation:
str = "MasterKeyAwaitConfirmation"
case MasterKeyConfirmed:
str = "MasterKeyConfirmed"
case MasterKeyConfirmationError:
str = "MasterKeyConfirmationError"
} }
return str return str
} }
// Signing proposal
type SigningConfirmation struct {
Quorum SigningProposalQuorum
RecoveredKey []byte
SrcPayload []byte
EncryptedPayload []byte
CreatedAt time.Time
ExpiresAt time.Time
}
type SigningProposalQuorum map[int]*SigningProposalParticipant
type SigningParticipantStatus uint8
const (
SigningIdle SigningParticipantStatus = iota
SigningAwaitConfirmation
SigningConfirmed
SigningDeclined
SigningAwaitPartialKeys
SigningPartialKeysConfirmed
SigningError
SigningProcess
)
func (s SigningParticipantStatus) String() string {
var str = "undefined"
switch s {
case SigningIdle:
str = "SigningIdle"
case SigningAwaitConfirmation:
str = "SigningAwaitConfirmation"
case SigningConfirmed:
str = "SigningConfirmed"
case SigningAwaitPartialKeys:
str = "SigningAwaitPartialKeys"
case SigningPartialKeysConfirmed:
str = "SigningPartialKeysConfirmed"
case SigningError:
str = "SigningError"
case SigningProcess:
str = "SigningProcess"
}
return str
}
type SigningProposalParticipant struct {
Title string
Status SigningParticipantStatus
PartialKey []byte
Error error
UpdatedAt time.Time
}

View File

@ -80,6 +80,7 @@ func init() {
}) })
} }
testParticipantsListRequest.Participants = participantsForRequest testParticipantsListRequest.Participants = participantsForRequest
testParticipantsListRequest.SigningThreshold = len(participantsForRequest)
} }
func TestCreate_Positive(t *testing.T) { func TestCreate_Positive(t *testing.T) {
@ -399,7 +400,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
} }
compareState(t, fsm.StateGlobalDone, fsmResponse.State) compareState(t, dpf.StateDkgMasterKeyCollected, fsmResponse.State)
} }
func Test_DKGProposal_Positive(t *testing.T) { func Test_DKGProposal_Positive(t *testing.T) {

View File

@ -57,7 +57,7 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar
PubKey: parsedPubKey, PubKey: parsedPubKey,
DkgPubKey: participant.DkgPubKey, DkgPubKey: participant.DkgPubKey,
InvitationSecret: secret, InvitationSecret: secret,
Status: internal.SignatureConfirmationAwaitConfirmation, Status: internal.SigConfirmationAwaitConfirmation,
UpdatedAt: request.CreatedAt, UpdatedAt: request.CreatedAt,
} }
} }
@ -128,16 +128,16 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
return return
} }
if signatureProposalParticipant.Status != internal.SignatureConfirmationAwaitConfirmation { if signatureProposalParticipant.Status != internal.SigConfirmationAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot apply reply participant with {Status} = {\"%s\"}", signatureProposalParticipant.Status)) err = errors.New(fmt.Sprintf("cannot apply reply participant with {Status} = {\"%s\"}", signatureProposalParticipant.Status))
return return
} }
switch inEvent { switch inEvent {
case EventConfirmSignatureProposal: case EventConfirmSignatureProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed signatureProposalParticipant.Status = internal.SigConfirmationConfirmed
case EventDeclineProposal: case EventDeclineProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined signatureProposalParticipant.Status = internal.SigConfirmationDeclined
default: default:
err = errors.New("undefined {Event} for action") err = errors.New("undefined {Event} for action")
return return
@ -162,14 +162,14 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event
unconfirmedParticipants := m.payload.SigQuorumCount() unconfirmedParticipants := m.payload.SigQuorumCount()
for _, participant := range m.payload.SignatureProposalPayload.Quorum { for _, participant := range m.payload.SignatureProposalPayload.Quorum {
if participant.Status == internal.SignatureConfirmationAwaitConfirmation { if participant.Status == internal.SigConfirmationAwaitConfirmation {
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(tm) { if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(tm) {
isContainsExpired = true isContainsExpired = true
} }
} else { } else {
if participant.Status == internal.SignatureConfirmationConfirmed { if participant.Status == internal.SigConfirmationConfirmed {
unconfirmedParticipants-- unconfirmedParticipants--
} else if participant.Status == internal.SignatureConfirmationDeclined { } else if participant.Status == internal.SigConfirmationDeclined {
isContainsDeclined = true isContainsDeclined = true
} }
} }

View File

@ -28,7 +28,7 @@ const (
eventDoneInternal = fsm.Event("event_sig_proposal_done") eventDoneInternal = fsm.Event("event_sig_proposal_done")
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout") eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_declined_timeout") eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_canceled_participant")
StateSignatureProposalCollected = fsm.State("state_sig_proposal_collected") StateSignatureProposalCollected = fsm.State("state_sig_proposal_collected")
@ -49,7 +49,7 @@ func New() internal.DumpedMachineProvider {
FsmName, FsmName,
fsm.StateGlobalIdle, fsm.StateGlobalIdle,
[]fsm.EventDesc{ []fsm.EventDesc{
// {Name: "", SrcState: []string{""}, DstState: ""}, // {Name: "", SrcState: []fsm.State{""}, DstState: ""},
// Init // Init
{Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations}, {Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations},

View File

@ -0,0 +1,143 @@
package signing_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"
"time"
)
func (m *SigningProposalFSM) actionInitSigningProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
m.payload.SigningProposalPayload = &internal.SigningConfirmation{
Quorum: make(internal.SigningProposalQuorum),
}
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
m.payload.SigningProposalPayload.Quorum[participant.ParticipantId] = &internal.SigningProposalParticipant{
Title: participant.Title,
Status: internal.SigningIdle,
UpdatedAt: participant.UpdatedAt,
}
}
return
}
func (m *SigningProposalFSM) actionStartSigningProposal(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 {SigningProposalStartRequest}")
return
}
request, ok := args[0].(requests.SigningProposalStartRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {SigningProposalStartRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
return
}
func (m *SigningProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {SigningProposalParticipantRequest}")
return
}
request, ok := args[0].(requests.SigningProposalParticipantRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {SigningProposalParticipantRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
if !m.payload.SigningQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
signingProposalParticipant := m.payload.SigningQuorumGet(request.ParticipantId)
if signingProposalParticipant.Status != internal.SigningAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", signingProposalParticipant.Status))
return
}
// copy(signingProposalParticipant.Commit, request.Commit)
signingProposalParticipant.UpdatedAt = request.CreatedAt
signingProposalParticipant.Status = internal.SigningConfirmed
m.payload.SigningQuorumUpdate(request.ParticipantId, signingProposalParticipant)
return
}
func (m *SigningProposalFSM) actionValidateSigningProposalConfirmations(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.SigningQuorumCount()
for _, participant := range m.payload.SigningProposalPayload.Quorum {
if participant.Status == internal.SigningAwaitConfirmation {
if participant.UpdatedAt.Add(config.SigningConfirmationDeadline).Before(tm) {
isContainsExpired = true
}
} else {
if participant.Status == internal.SigningDeclined {
isContainsError = true
} else if participant.Status == internal.SigningConfirmed {
unconfirmedParticipants--
}
}
}
if isContainsError {
outEvent = eventSetSigningConfirmCanceledByTimeoutInternal
return
}
if isContainsExpired {
outEvent = eventSetSigningConfirmCanceledByParticipantInternal
return
}
// The are no declined and timed out participants, check for all confirmations
if unconfirmedParticipants > 0 {
return
}
outEvent = eventSetProposalValidatedInternal
for _, participant := range m.payload.SigningProposalPayload.Quorum {
participant.Status = internal.SigningAwaitPartialKeys
}
return
}

View File

@ -0,0 +1,106 @@
package signing_proposal_fsm
import (
"github.com/depools/dc4bc/fsm/fsm"
dkp "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"sync"
)
const (
FsmName = "signing_proposal_fsm"
StateSigningInitial = dkp.StateDkgMasterKeyCollected
StateSigningIdle = fsm.State("stage_signing_idle")
// Starting
StateSigningAwaitConfirmations = fsm.State("state_signing_await_confirmations")
// Cancelled
StateSigningConfirmationsAwaitCancelledByTimeout = fsm.State("state_signing_confirmations_await_cancelled_by_timeout")
StateSigningConfirmationsAwaitCancelledByParticipant = fsm.State("state_signing_confirmations_await_cancelled_by_participant")
StateSigningAwaitPartialKeys = fsm.State("state_signing_await_partial_keys")
// Cancelled
StateSigningPartialKeysAwaitCancelledByTimeout = fsm.State("state_signing_partial_keys_await_cancelled_by_timeout")
StateSigningPartialKeysAwaitCancelledByParticipant = fsm.State("state_signing_partial_keys_await_cancelled_by_participant")
StateSigningPartialKeysCollected = fsm.State("state_signing_partial_keys_collected")
// Events
EventSigningInit = fsm.Event("event_signing_init")
EventSigningStart = fsm.Event("event_signing_start")
EventConfirmSigningConfirmation = fsm.Event("event_signing_proposal_confirm_by_participant")
EventDeclineSigningConfirmation = fsm.Event("event_signing_proposal_decline_by_participant")
eventSetSigningConfirmCanceledByParticipantInternal = fsm.Event("event_signing_proposal_canceled_by_participant")
eventSetSigningConfirmCanceledByTimeoutInternal = fsm.Event("event_signing_proposal_canceled_by_timeout")
eventAutoValidateProposalInternal = fsm.Event("event_signing_proposal_validate")
eventSetProposalValidatedInternal = fsm.Event("event_signing_proposal_set_validated")
EventSigningPartialKeyReceived = fsm.Event("event_signing_partial_key_received")
EventSigningPartialKeyError = fsm.Event("event_signing_partial_key_error_received")
eventSigningPartialKeyCancelByTimeoutInternal = fsm.Event("event_signing_partial_key_canceled_by_timeout_internal")
eventSigningPartialKeyCancelByErrorInternal = fsm.Event("event_signing_partial_key_canceled_by_error_internal")
eventSigningPartialKeysConfirmedInternal = fsm.Event("event_signing_partial_keys_confirmed_internal")
EventSigningFinish = fsm.Event("event_signing_finish")
)
type SigningProposalFSM struct {
*fsm.FSM
payload *internal.DumpedMachineStatePayload
payloadMu sync.RWMutex
}
func New() internal.DumpedMachineProvider {
machine := &SigningProposalFSM{}
machine.FSM = fsm.MustNewFSM(
FsmName,
StateSigningInitial,
[]fsm.EventDesc{
// Init
{Name: EventSigningInit, SrcState: []fsm.State{StateSigningInitial}, DstState: StateSigningIdle},
// Start
{Name: EventSigningStart, SrcState: []fsm.State{StateSigningIdle}, DstState: StateSigningAwaitConfirmations},
// Validate by participants
{Name: EventConfirmSigningConfirmation, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations},
{Name: EventDeclineSigningConfirmation, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations},
// Canceled
{Name: eventSetSigningConfirmCanceledByParticipantInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningConfirmationsAwaitCancelledByParticipant, IsInternal: true},
{Name: eventSetSigningConfirmCanceledByTimeoutInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningConfirmationsAwaitCancelledByTimeout, IsInternal: true},
// Validate
{Name: eventAutoValidateProposalInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations, IsInternal: true, IsAuto: true},
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations, IsInternal: true, IsAuto: true},
// Canceled
{Name: EventSigningPartialKeyReceived, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningAwaitPartialKeys},
{Name: EventSigningPartialKeyError, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByParticipant},
{Name: eventSigningPartialKeyCancelByTimeoutInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByTimeout, IsInternal: true},
{Name: eventSigningPartialKeyCancelByErrorInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByParticipant, IsInternal: true, IsAuto: true},
{Name: eventSigningPartialKeysConfirmedInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysCollected, IsInternal: true},
{Name: EventSigningFinish, SrcState: []fsm.State{StateSigningPartialKeysCollected}, DstState: StateSigningIdle, IsInternal: true},
},
fsm.Callbacks{
EventSigningInit: machine.actionInitSigningProposal,
},
)
return machine
}
func (m *SigningProposalFSM) SetUpPayload(payload *internal.DumpedMachineStatePayload) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
m.payload = payload
}

View File

@ -7,8 +7,9 @@ import "time"
// States: "__idle" // States: "__idle"
// Events: "event_sig_proposal_init" // Events: "event_sig_proposal_init"
type SignatureProposalParticipantsListRequest struct { type SignatureProposalParticipantsListRequest struct {
Participants []*SignatureProposalParticipantsEntry Participants []*SignatureProposalParticipantsEntry
CreatedAt time.Time SigningThreshold int
CreatedAt time.Time
} }
type SignatureProposalParticipantsEntry struct { type SignatureProposalParticipantsEntry struct {

View File

@ -11,6 +11,14 @@ func (r *SignatureProposalParticipantsListRequest) Validate() error {
return errors.New(fmt.Sprintf("too few participants, minimum is {%d}", config.ParticipantsMinCount)) return errors.New(fmt.Sprintf("too few participants, minimum is {%d}", config.ParticipantsMinCount))
} }
if r.SigningThreshold < 2 {
return errors.New("{SigningThreshold} minimum count is {2}")
}
if r.SigningThreshold > len(r.Participants) {
return errors.New("{SigningThreshold} cannot be higher than {ParticipantsCount}")
}
for _, participant := range r.Participants { for _, participant := range r.Participants {
if len(participant.Title) < 3 { if len(participant.Title) < 3 {
return errors.New("{Title} minimum length is {3}") return errors.New("{Title} minimum length is {3}")

View File

@ -0,0 +1,27 @@
package requests
import "time"
// States: "stage_signing_idle"
// Events: "event_signing_start"
type SigningProposalStartRequest struct {
ParticipantId int
SrcPayload []byte
CreatedAt time.Time
}
// States: "state_signing_await_confirmations"
// Events: "event_signing_proposal_confirm_by_participant"
// "event_signing_proposal_decline_by_participant"
type SigningProposalParticipantRequest struct {
ParticipantId int
CreatedAt time.Time
}
// States: "state_signing_await_partial_keys"
// Events: "event_signing_partial_key_received"
type SigningProposalPartialKeyRequest struct {
ParticipantId int
PartialKey []byte
CreatedAt time.Time
}

View File

@ -0,0 +1,47 @@
package requests
import "errors"
func (r *SigningProposalStartRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.SrcPayload) == 0 {
return errors.New("{SrcPayload} cannot zero length")
}
if r.CreatedAt.IsZero() {
return errors.New("{CreatedAt} is not set")
}
return nil
}
func (r *SigningProposalParticipantRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if r.CreatedAt.IsZero() {
return errors.New("{CreatedAt} is not set")
}
return nil
}
func (r *SigningProposalPartialKeyRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.PartialKey) == 0 {
return errors.New("{PartialKey} cannot zero length")
}
if r.CreatedAt.IsZero() {
return errors.New("{CreatedAt} is not set")
}
return nil
}

View File

@ -0,0 +1 @@
package responses