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
SignatureProposalConfirmationDeadline = 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
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations")
// 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")
// Confirmed
StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected")
@ -23,20 +23,20 @@ const (
// Sending dkg deals
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
// 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")
// Confirmed
//StateDkgDealsCollected = fsm.State("state_dkg_deals_collected")
StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations")
// 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")
// Confirmed
StateDkgResponsesCollected = fsm.State("state_dkg_responses_collected")
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")
StateDkgMasterKeyCollected = fsm.State("state_dkg_master_key_collected")
@ -104,7 +104,7 @@ func New() internal.DumpedMachineProvider {
// Commits
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
// 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: eventAutoDKGValidateConfirmationCommitsInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -115,7 +115,7 @@ func New() internal.DumpedMachineProvider {
// Deals
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
// 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: eventAutoDKGValidateConfirmationDealsInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -124,7 +124,7 @@ func New() internal.DumpedMachineProvider {
// Responses
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
// 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: eventAutoDKGValidateResponsesConfirmationInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true, IsAuto: true},
@ -134,15 +134,13 @@ func New() internal.DumpedMachineProvider {
// Master key
{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: eventAutoDKGValidateMasterKeyConfirmationInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyAwaitConfirmations, IsInternal: true, IsAuto: true},
{Name: eventDKGMasterKeyConfirmedInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
// Done
// {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
{Name: eventDKGMasterKeyConfirmedInternal, SrcState: []fsm.State{StateDkgMasterKeyAwaitConfirmations}, DstState: StateDkgMasterKeyCollected, IsInternal: true},
},
fsm.Callbacks{
EventDKGInitProcess: machine.actionInitDKGProposal,

View File

@ -13,6 +13,7 @@ type DumpedMachineStatePayload struct {
TransactionId string
SignatureProposalPayload *SignatureConfirmation
DKGProposalPayload *DKGConfirmation
SigningProposalPayload *SigningConfirmation
}
// Signature quorum
@ -78,3 +79,35 @@ func (p *DumpedMachineStatePayload) DKGQuorumUpdate(id int, participant *DKGProp
}
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"
)
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 {
Quorum SignatureProposalQuorum
CreatedAt time.Time
@ -19,7 +43,7 @@ type SignatureProposalParticipant struct {
DkgPubKey []byte
// For validation user confirmation: sign(InvitationSecret, PubKey) => user
InvitationSecret string
Status ParticipantStatus
Status ConfirmationParticipantStatus
UpdatedAt time.Time
}
@ -27,14 +51,12 @@ type SignatureProposalParticipant struct {
// Excludes array merge and rotate operations
type SignatureProposalQuorum map[string]*SignatureProposalParticipant
type ParticipantStatus uint8
// DKG proposal
type DKGParticipantStatus uint8
const (
SignatureConfirmationAwaitConfirmation ParticipantStatus = iota
SignatureConfirmationConfirmed
SignatureConfirmationDeclined
SignatureConfirmationError
CommitAwaitConfirmation
CommitAwaitConfirmation DKGParticipantStatus = iota
CommitConfirmed
CommitConfirmationError
DealAwaitConfirmation
@ -55,7 +77,7 @@ type DKGProposalParticipant struct {
Deal []byte
Response []byte
MasterKey []byte
Status ParticipantStatus
Status DKGParticipantStatus
Error error
UpdatedAt time.Time
}
@ -70,17 +92,9 @@ type DKGConfirmation struct {
type DKGProposalParticipantStatus uint8
func (s ParticipantStatus) String() string {
func (s DKGParticipantStatus) String() string {
var str = "undefined"
switch s {
case SignatureConfirmationAwaitConfirmation:
str = "SignatureConfirmationAwaitConfirmation"
case SignatureConfirmationConfirmed:
str = "SignatureConfirmationConfirmed"
case SignatureConfirmationDeclined:
str = "SignatureConfirmationDeclined"
case SignatureConfirmationError:
str = "SignatureConfirmationError"
case CommitAwaitConfirmation:
str = "CommitAwaitConfirmation"
case CommitConfirmed:
@ -99,6 +113,67 @@ func (s ParticipantStatus) String() string {
str = "ResponseConfirmed"
case ResponseConfirmationError:
str = "ResponseConfirmationError"
case MasterKeyAwaitConfirmation:
str = "MasterKeyAwaitConfirmation"
case MasterKeyConfirmed:
str = "MasterKeyConfirmed"
case MasterKeyConfirmationError:
str = "MasterKeyConfirmationError"
}
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.SigningThreshold = len(participantsForRequest)
}
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) {

View File

@ -57,7 +57,7 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar
PubKey: parsedPubKey,
DkgPubKey: participant.DkgPubKey,
InvitationSecret: secret,
Status: internal.SignatureConfirmationAwaitConfirmation,
Status: internal.SigConfirmationAwaitConfirmation,
UpdatedAt: request.CreatedAt,
}
}
@ -128,16 +128,16 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
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))
return
}
switch inEvent {
case EventConfirmSignatureProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed
signatureProposalParticipant.Status = internal.SigConfirmationConfirmed
case EventDeclineProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined
signatureProposalParticipant.Status = internal.SigConfirmationDeclined
default:
err = errors.New("undefined {Event} for action")
return
@ -162,14 +162,14 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event
unconfirmedParticipants := m.payload.SigQuorumCount()
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) {
isContainsExpired = true
}
} else {
if participant.Status == internal.SignatureConfirmationConfirmed {
if participant.Status == internal.SigConfirmationConfirmed {
unconfirmedParticipants--
} else if participant.Status == internal.SignatureConfirmationDeclined {
} else if participant.Status == internal.SigConfirmationDeclined {
isContainsDeclined = true
}
}

View File

@ -28,7 +28,7 @@ const (
eventDoneInternal = fsm.Event("event_sig_proposal_done")
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")
@ -49,7 +49,7 @@ func New() internal.DumpedMachineProvider {
FsmName,
fsm.StateGlobalIdle,
[]fsm.EventDesc{
// {Name: "", SrcState: []string{""}, DstState: ""},
// {Name: "", SrcState: []fsm.State{""}, DstState: ""},
// Init
{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"
// Events: "event_sig_proposal_init"
type SignatureProposalParticipantsListRequest struct {
Participants []*SignatureProposalParticipantsEntry
CreatedAt time.Time
Participants []*SignatureProposalParticipantsEntry
SigningThreshold int
CreatedAt time.Time
}
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))
}
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 {
if len(participant.Title) < 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