Merge branch 'fsm-draft' into feat/client-fsm

This commit is contained in:
programmer10110 2020-08-09 11:41:45 +03:00
commit 0c374216df
16 changed files with 893 additions and 175 deletions

View File

@ -2,34 +2,44 @@ 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(
"event_proposal_init",
signature_proposal_fsm.EventInitProposal,
requests.SignatureProposalParticipantsListRequest{
{
"John Doe",
[]byte("pubkey123123"),
},
{
"Crypto Billy",
[]byte("pubkey456456"),
},
{
"Matt",
[]byte("pubkey789789"),
Participants: []*requests.SignatureProposalParticipantsEntry{
{
"John Doe",
[]byte("pubkey123123"),
},
{
"Crypto Billy",
[]byte("pubkey456456"),
},
{
"Matt",
[]byte("pubkey789789"),
},
},
CreatedAt: &tm,
},
)
log.Println("Err", err)
if err != nil {
log.Println("Err", err)
return
}
log.Println("Dump", string(dump))
processResponse(resp)

View File

@ -35,6 +35,10 @@ func (e *Event) String() string {
return string(*e)
}
func (e *Event) IsEmpty() bool {
return e.String() == ""
}
// Response returns result for processing with clientMocks events
type Response struct {
// Returns machine execution result state
@ -73,10 +77,10 @@ type trKey struct {
// Transition lightweight event description
type trEvent struct {
event Event
dstState State
isInternal bool
isDstInit bool
event Event
dstState State
isInternal bool
isStateBefore bool
}
type EventDesc struct {
@ -94,7 +98,7 @@ type EventDesc struct {
IsDstInit bool
}
type Callback func(event Event, args ...interface{}) (interface{}, error)
type Callback func(event Event, args ...interface{}) (Event, interface{}, error)
type Callbacks map[Event]Callback
@ -252,10 +256,11 @@ func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) {
return f.do(trEvent, args...)
}
func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) {
var outEvent Event
// f.eventMu.Lock()
// defer f.eventMu.Unlock()
if trEvent.isDstInit {
if trEvent.isStateBefore {
err = f.SetState(trEvent.event)
if err != nil {
resp = &Response{
@ -270,15 +275,20 @@ func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err err
}
if callback, ok := f.callbacks[trEvent.event]; ok {
resp.Data, err = callback(trEvent.event, args...)
outEvent, resp.Data, err = callback(trEvent.event, args...)
// Do not try change state on error
if err != nil {
return resp, err
}
}
if !trEvent.isDstInit {
err = f.SetState(trEvent.event)
if !trEvent.isStateBefore {
if outEvent.IsEmpty() || trEvent.event == outEvent {
err = f.SetState(trEvent.event)
} else {
err = f.SetState(outEvent)
}
}
resp.State = f.State()

View File

@ -1,54 +1,272 @@
package dkg_proposal_fsm
import (
"errors"
"fmt"
"github.com/depools/dc4bc/fsm/fsm"
"log"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"github.com/depools/dc4bc/fsm/types/requests"
)
// Pub keys
func (m *DKGProposalFSM) actionDKGPubKeysSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeysSent")
func (m *DKGProposalFSM) actionPubKeyPrepareConfirmations(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
return
}
func (m *DKGProposalFSM) actionDKGPubKeyConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeyConfirmationReceived")
return
}
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
}
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
if !ok {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
copy(dkgProposalParticipant.PublicKey, request.PubKey)
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.PubKeyConfirmed
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
func (m *DKGProposalFSM) actionDKGPubKeyConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeyConfirmationError")
return
}
// Commits
func (m *DKGProposalFSM) actionDKGCommitsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitsSent")
return
}
func (m *DKGProposalFSM) actionDKGCommitConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitConfirmationReceived")
return
}
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
}
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
if !ok {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
copy(dkgProposalParticipant.Commit, request.Commit)
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.CommitConfirmed
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
func (m *DKGProposalFSM) actionDKGCommitConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitConfirmationError")
return
}
// Deals
func (m *DKGProposalFSM) actionDKGDealsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealsSent")
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
}
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
if !ok {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
copy(dkgProposalParticipant.Deal, request.Deal)
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.DealConfirmed
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
return
}
func (m *DKGProposalFSM) actionDKGDealConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealConfirmationReceived")
// Responses
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
}
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
if !ok {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
copy(dkgProposalParticipant.Response, request.Response)
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.ResponseConfirmed
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
return
}
func (m *DKGProposalFSM) actionDKGDealConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealConfirmationError")
// Errors
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
}
dkgProposalParticipant, ok := m.payload.DKGProposalPayload[request.ParticipantId]
if !ok {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
// TODO: Move to methods
switch inEvent {
case EventDKGPubKeyConfirmationError:
switch dkgProposalParticipant.Status {
case internal.PubKeyConAwaitConfirmation:
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,
internal.PubKeyConfirmationError,
))
}
case EventDKGDealConfirmationError:
switch dkgProposalParticipant.Status {
case internal.DealAwaitConfirmation:
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
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,
internal.PubKeyConfirmationError,
))
}
case EventDKGResponseConfirmationError:
switch dkgProposalParticipant.Status {
case internal.ResponseAwaitConfirmation:
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
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,
internal.PubKeyConfirmationError,
))
}
default:
err = errors.New(fmt.Sprintf("{%s} event cannot be used for action {actionConfirmationError}", inEvent))
}
if err != nil {
return
}
dkgProposalParticipant.UpdatedAt = request.CreatedAt
m.payload.DKGProposalPayload[request.ParticipantId] = dkgProposalParticipant
// TODO: Add outEvent
return
}

View File

@ -0,0 +1 @@
package dkg_proposal_fsm

View File

@ -8,41 +8,43 @@ import (
)
const (
fsmName = "dkg_proposal_fsm"
FsmName = "dkg_proposal_fsm"
StateDkgInitial = signature_proposal_fsm.StateValidationCompleted
// Sending dkg pub keys
StateDkgPubKeysSendingRequired = fsm.State("state_dkg_pub_keys_sending_required")
StateDkgPubKeysSendingAwaitConfirmations = fsm.State("state_dkg_pub_keys_sending_await_confirmations")
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
// Cancelled
StateDkgPubKeysSendingCancelled = fsm.State("state_dkg_pub_keys_sending_cancelled")
StateDkgPubKeysSendingCancelledByTimeout = fsm.State("state_dkg_pub_keys_sending_cancelled_by_timeout")
StateDkgPubKeysAwaitCancelled = fsm.State("state_dkg_pub_keys_await_cancelled")
StateDkgPubKeysAwaitCancelledByTimeout = fsm.State("state_dkg_pub_keys_await_cancelled_by_timeout")
// Confirmed
StateDkgPubKeysSendingConfirmed = fsm.State("state_dkg_pub_keys_sending_confirmed")
StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
// Sending dkg commits
StateDkgCommitsSendingRequired = fsm.State("state_dkg_commits_sending_required")
StateDkgCommitsSendingAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_sending_await_confirmations")
// Cancelled
StateDkgCommitsSendingCancelled = fsm.State("state_dkg_commits_sending_cancelled")
StateDkgCommitsSendingCancelledByTimeout = fsm.State("state_dkg_commits_sending_cancelled_by_timeout")
StateDkgCommitsAwaitCancelled = fsm.State("state_dkg_commits_await_cancelled")
StateDkgCommitsAwaitCancelledByTimeout = fsm.State("state_dkg_commits_await_cancelled_by_timeout")
// Confirmed
StateDkgCommitsSendingConfirmed = fsm.State("state_dkg_commits_sending_confirmed")
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
// Sending dkg deals
StateDkgDealsSendingRequired = fsm.State("state_dkg_deals_sending_required")
StateDkgDealsSendingAwaitConfirmations = fsm.State("state_dkg_deals_sending_await_confirmations")
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
// Cancelled
StateDkgDealsSendingCancelled = fsm.State("state_dkg_deals_sending_cancelled")
StateDkgDealsSendingCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
StateDkgDealsAwaitCancelled = fsm.State("state_dkg_deals_await_cancelled")
StateDkgDealsAwaitCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
// Confirmed
StateDkgDealsSendingConfirmed = fsm.State("state_dkg_deals_sending_confirmed")
StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_confirmed")
StateDkgResponsesAwaitConfirmations = fsm.State("state_dkg_responses_await_confirmations")
// Cancelled
StateDkgResponsesAwaitCancelled = fsm.State("state_dkg_responses_await_cancelled")
StateDkgResponsesAwaitCancelledByTimeout = fsm.State("state_dkg_responses_sending_cancelled_by_timeout")
// Confirmed
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_confirmed")
// Events
EventDKGPubKeysSendingRequiredInternal = fsm.Event("event_dkg_pub_key_sending_required_internal")
EventDKGPubKeysSent = fsm.Event("event_dkg_pub_keys_sent")
EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received")
EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error")
EventDKGPubKeysConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal")
@ -50,7 +52,6 @@ const (
EventDKGCommitsSendingRequiredInternal = fsm.Event("event_dkg_commits_sending_required_internal")
EventDKGCommitsSent = fsm.Event("event_dkg_commits_sent")
EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received")
EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error")
EventDKGCommitsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_commits_confirm_canceled_by_timeout_internal")
@ -58,11 +59,16 @@ const (
EventDKGDealsSendingRequiredInternal = fsm.Event("event_dkg_deals_sending_required_internal")
EventDKGDealsSent = fsm.Event("event_dkg_deals_sent")
EventDKGDealConfirmationReceived = fsm.Event("event_dkg_deal_confirm_received")
EventDKGDealConfirmationError = fsm.Event("event_dkg_deal_confirm_canceled_by_error")
EventDKGDealsConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_deals_confirm_canceled_by_timeout_internal")
EventDKGResponsesSendingRequiredInternal = fsm.Event("event_dkg_responses_sending_required_internal")
EventDKGResponseConfirmationReceived = fsm.Event("event_dkg_response_confirm_received")
EventDKGResponseConfirmationError = fsm.Event("event_dkg_response_confirm_canceled_by_error")
EventDKGResponseConfirmationCancelByTimeoutInternal = fsm.Event("event_dkg_response_confirm_canceled_by_timeout_internal")
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
)
@ -76,60 +82,66 @@ func New() internal.DumpedMachineProvider {
machine := &DKGProposalFSM{}
machine.FSM = fsm.MustNewFSM(
fsmName,
FsmName,
StateDkgInitial,
[]fsm.EventDesc{
// Init
// Switch to pub keys required
{Name: EventDKGPubKeysSendingRequiredInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysSendingRequired, IsInternal: true},
{Name: EventDKGPubKeysSendingRequiredInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true},
// Pub keys sending
{Name: EventDKGPubKeysSent, SrcState: []fsm.State{StateDkgPubKeysSendingRequired}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
// Cancelled
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelled},
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelledByTimeout, IsInternal: true},
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelled},
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCancelledByTimeout, IsInternal: true},
// Confirmed
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingConfirmed, IsInternal: true},
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmed, IsInternal: true},
// Switch to commits required
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysSendingConfirmed}, DstState: StateDkgCommitsSendingRequired, IsInternal: true},
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmed}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
// Commits
{Name: EventDKGCommitsSent, SrcState: []fsm.State{StateDkgCommitsSendingRequired}, DstState: StateDkgCommitsSendingAwaitConfirmations},
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingAwaitConfirmations},
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
// Cancelled
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelled},
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelledByTimeout, IsInternal: true},
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelled},
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitCancelledByTimeout, IsInternal: true},
// Confirmed
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingConfirmed, IsInternal: true},
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmed, IsInternal: true},
// Switch to deals required
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsSendingConfirmed}, DstState: StateDkgDealsSendingRequired, IsInternal: true},
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsAwaitConfirmed}, DstState: StateDkgDealsAwaitConfirmations, IsInternal: true},
// Deals
{Name: EventDKGDealsSent, SrcState: []fsm.State{StateDkgDealsSendingRequired}, DstState: StateDkgDealsSendingAwaitConfirmations},
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingAwaitConfirmations},
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsAwaitConfirmations}, DstState: StateDkgDealsAwaitConfirmations},
// Cancelled
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelled},
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelledByTimeout, IsInternal: true},
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelled},
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgDealsAwaitCancelledByTimeout, IsInternal: true},
// Switch to responses required
{Name: EventDKGResponsesSendingRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmed}, DstState: StateDkgResponsesAwaitConfirmations, IsInternal: true},
// Deals
{Name: EventDKGResponseConfirmationReceived, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitConfirmations},
// Cancelled
{Name: EventDKGResponseConfirmationError, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelled},
{Name: EventDKGResponseConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: StateDkgResponsesAwaitCancelledByTimeout, IsInternal: true},
// Done
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
},
fsm.Callbacks{
EventDKGPubKeysSent: machine.actionDKGPubKeysSent,
EventDKGPubKeyConfirmationReceived: machine.actionDKGPubKeyConfirmationReceived,
EventDKGPubKeyConfirmationError: machine.actionDKGPubKeyConfirmationError,
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
EventDKGCommitsSent: machine.actionDKGCommitsSent,
EventDKGCommitConfirmationReceived: machine.actionDKGCommitConfirmationReceived,
EventDKGCommitConfirmationError: machine.actionDKGCommitConfirmationError,
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
EventDKGCommitConfirmationError: machine.actionConfirmationError,
EventDKGDealsSent: machine.actionDKGDealsSent,
EventDKGDealConfirmationReceived: machine.actionDKGDealConfirmationReceived,
EventDKGDealConfirmationError: machine.actionDKGDealConfirmationError,
EventDKGDealConfirmationReceived: machine.actionDealConfirmationReceived,
EventDKGDealConfirmationError: machine.actionConfirmationError,
EventDKGResponseConfirmationReceived: machine.actionResponseConfirmationReceived,
EventDKGResponseConfirmationError: machine.actionConfirmationError,
},
)
return machine

View File

@ -1,6 +1,9 @@
package internal
import "time"
import (
"crypto/rsa"
"time"
)
const (
SignatureAwaitConfirmation SignatureProposalParticipantStatus = iota
@ -17,7 +20,7 @@ type SignatureProposalParticipant struct {
// Public title for address, such as name, nickname, organization
ParticipantId int
Title string
PublicKey []byte
PublicKey *rsa.PublicKey
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
InvitationSecret string
Status SignatureProposalParticipantStatus
@ -31,12 +34,22 @@ type SignatureProposalQuorum map[string]SignatureProposalParticipant
type SignatureProposalParticipantStatus uint8
const (
PubKeyConAwaitConfirmation DKGProposalParticipantStatus = iota
SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota
SignatureConfirmationConfirmed
SignatureConfirmationDeclined
SignatureConfirmationError
PubKeyConAwaitConfirmation
PubKeyConfirmed
PubKeyConfirmationError
CommitAwaitConfirmation
CommitConfirmed
CommitConfirmationError
DealAwaitConfirmation
DealConfirmed
DealConfirmationError
ResponseAwaitConfirmation
ResponseConfirmed
ResponseConfirmationError
)
type DKGProposal struct {
@ -50,6 +63,7 @@ type DKGProposalParticipant struct {
PublicKey []byte
Commit []byte
Deal []byte
Response []byte
Status DKGProposalParticipantStatus
UpdatedAt *time.Time
}
@ -57,3 +71,42 @@ type DKGProposalParticipant struct {
type DKGProposalQuorum map[int]DKGProposalParticipant
type DKGProposalParticipantStatus uint8
func (s DKGProposalParticipantStatus) String() string {
var str = "undefined"
switch s {
case SignatureConfirmationAwaitConfirmation:
str = "SignatureConfirmationAwaitConfirmation"
case SignatureConfirmationConfirmed:
str = "SignatureConfirmationConfirmed"
case SignatureConfirmationDeclined:
str = "SignatureConfirmationDeclined"
case SignatureConfirmationError:
str = "SignatureConfirmationError"
case PubKeyConAwaitConfirmation:
str = "PubKeyConAwaitConfirmation"
case PubKeyConfirmed:
str = "PubKeyConfirmed"
case PubKeyConfirmationError:
str = "PubKeyConfirmationError"
case CommitAwaitConfirmation:
str = "CommitAwaitConfirmation"
case CommitConfirmed:
str = "CommitConfirmed"
case CommitConfirmationError:
str = "CommitConfirmationError"
case DealAwaitConfirmation:
str = "DealAwaitConfirmation"
case DealConfirmed:
str = "DealConfirmed"
case DealConfirmationError:
str = "DealConfirmationError"
case ResponseAwaitConfirmation:
str = "ResponseAwaitConfirmation"
case ResponseConfirmed:
str = "ResponseConfirmed"
case ResponseConfirmationError:
str = "ResponseConfirmationError"
}
return str
}

View File

@ -35,11 +35,12 @@ func init() {
)
}
// Transaction id required for unique identify dump
func Create(tid string) (*FSMInstance, error) {
// Create new fsm with unique id
// transactionId required for unique identify dump
func Create(transactionId string) (*FSMInstance, error) {
var err error
i := &FSMInstance{}
err = i.InitDump(tid)
err = i.InitDump(transactionId)
if err != nil {
return nil, err
@ -51,12 +52,20 @@ func Create(tid string) (*FSMInstance, error) {
return i, err
}
// Get fsm from dump
func FromDump(data []byte) (*FSMInstance, error) {
var err error
i := &FSMInstance{}
if len(data) < 2 {
return nil, errors.New("machine dump is empty")
}
i := &FSMInstance{
dump: &FSMDump{},
}
err = i.dump.Unmarshal(data)
// TODO: Add logger
if err != nil {
return nil, errors.New("cannot read machine dump")
}
@ -85,21 +94,21 @@ func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Resp
return result, dump, err
}
func (i *FSMInstance) InitDump(tid string) error {
func (i *FSMInstance) InitDump(transactionId string) error {
if i.dump != nil {
return errors.New("dump already initialized")
}
tid = strings.TrimSpace(tid)
transactionId = strings.TrimSpace(transactionId)
if tid == "" {
if transactionId == "" {
return errors.New("empty transaction id")
}
i.dump = &FSMDump{
State: fsm.StateGlobalIdle,
Payload: &internal.DumpedMachineStatePayload{
TransactionId: tid,
TransactionId: transactionId,
ConfirmationProposalPayload: nil,
DKGProposalPayload: nil,
},
@ -114,5 +123,9 @@ func (d *FSMDump) Marshal() ([]byte, error) {
// TODO: Add decryption
func (d *FSMDump) Unmarshal(data []byte) error {
if d == nil {
return errors.New("dump struct is not initialized")
}
return json.Unmarshal(data, d)
}

View File

@ -0,0 +1,196 @@
package state_machines
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"fmt"
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
"github.com/depools/dc4bc/fsm/types/requests"
"github.com/depools/dc4bc/fsm/types/responses"
"log"
"testing"
"time"
)
const (
testTransactionId = "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257"
)
type testExternalParticipants struct {
Title string
PrivKey *rsa.PrivateKey
PubKey *rsa.PublicKey
}
var (
tm = time.Now()
testParticipants = map[string]*testExternalParticipants{}
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
Participants: []*requests.SignatureProposalParticipantsEntry{},
CreatedAt: &tm,
}
)
func init() {
r := rand.Reader
for i := 0; i < 3; i++ {
key, err := rsa.GenerateKey(r, 2048)
if err != nil {
log.Fatal("Cannot generate key for user:", err)
return
}
key.Precompute()
marshaledPubKey := x509.MarshalPKCS1PublicKey(&key.PublicKey)
hash := sha1.Sum(marshaledPubKey)
fingerprint := base64.StdEncoding.EncodeToString(hash[:])
participant := &testExternalParticipants{
Title: fmt.Sprintf("User %d", i),
PrivKey: key,
PubKey: &key.PublicKey,
}
testParticipants[fingerprint] = participant
}
participantsForRequest := make([]*requests.SignatureProposalParticipantsEntry, 0)
for _, participant := range testParticipants {
participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{
Title: participant.Title,
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
})
}
testParticipantsListRequest.Participants = participantsForRequest
}
func TestCreate_Positive(t *testing.T) {
testFSMInstance, err := Create(testTransactionId)
if err != nil {
t.Fatalf("expected nil error, got {%s}", err)
}
if testFSMInstance == nil {
t.Fatalf("expected {*FSMInstance}")
}
}
func TestCreate_Negative(t *testing.T) {
_, err := Create("")
if err == nil {
t.Fatalf("expected error for empty {transactionId}")
}
}
func Test_Workflow(t *testing.T) {
testFSMInstance, err := Create(testTransactionId)
if err != nil {
t.Fatalf("expected nil error, got {%s}", err)
}
if testFSMInstance == nil {
t.Fatalf("expected {*FSMInstance}")
}
if testFSMInstance.machine.Name() != spf.FsmName {
t.Fatalf("expected machine name {%s}", spf.FsmName)
}
if testFSMInstance.machine.State() != spf.StateParticipantsConfirmationsInit {
t.Fatalf("expected inital state {%s}", spf.StateParticipantsConfirmationsInit)
}
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
if err != nil {
t.Fatalf("expected nil error, got {%s}", err)
}
if len(dump) == 0 {
t.Fatalf("expected non zero dump, when executed without error")
}
if fsmResponse == nil {
t.Fatalf("expected {*fsm.FSMResponse}")
}
if fsmResponse.State != spf.StateAwaitParticipantsConfirmations {
t.Fatalf("expected state {%s}", spf.StateAwaitParticipantsConfirmations)
}
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
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.Second)
for _, participant := range participantsMap {
testFSMInstance, err = FromDump(dump)
if err != nil {
t.Fatalf("expected nil error, got {%s}", err)
}
if testFSMInstance == nil {
t.Fatalf("expected {*FSMInstance}")
}
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,
})
log.Println(fsmResponse.State, err)
}
}

View File

@ -1,9 +1,8 @@
package signature_proposal_fsm
import (
"crypto/x509"
"errors"
"log"
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"github.com/depools/dc4bc/fsm/types/requests"
@ -12,19 +11,19 @@ import (
// init -> awaitingConfirmations
// args: payload, signing id, participants list
func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) {
func (m *SignatureProposalFSM) actionInitProposal(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("participants list required")
err = errors.New("{arg0} required {SignatureProposalParticipantsListRequest}")
return
}
request, ok := args[0].(requests.SignatureProposalParticipantsListRequest)
if !ok {
err = errors.New("cannot cast participants list")
err = errors.New("cannot cast {arg0} to type {SignatureProposalParticipantsListRequest}")
return
}
@ -34,21 +33,35 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum)
for participantIntId, participant := range request {
participantId := createFingerprint(&participant.PublicKey)
for index, participant := range request.Participants {
participantId := createFingerprint(&participant.PubKey)
secret, err := generateRandomString(32)
if err != nil {
return nil, errors.New("cannot generateRandomString")
return inEvent, nil, errors.New("cannot generate source for {InvitationSecret}")
}
parsedPubKey, err := x509.ParsePKCS1PublicKey(participant.PubKey)
if err != nil {
return inEvent, nil, errors.New("cannot parse {PubKey}")
}
m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{
ParticipantId: participantIntId,
ParticipantId: index,
Title: participant.Title,
PublicKey: participant.PublicKey,
PublicKey: parsedPubKey,
InvitationSecret: secret,
UpdatedAt: nil,
Status: internal.SignatureAwaitConfirmation,
UpdatedAt: request.CreatedAt,
}
}
// Checking fo quorum length
if len(m.payload.ConfirmationProposalPayload) != len(request.Participants) {
err = errors.New("error with creating {SignatureProposalQuorum}")
return
}
// Make response
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
@ -56,9 +69,10 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
for pubKeyFingerprint, proposal := range m.payload.ConfirmationProposalPayload {
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret)
if err != nil {
return nil, errors.New("cannot encryptWithPubKey")
return inEvent, nil, errors.New("cannot encryptWithPubKey")
}
responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
ParticipantId: proposal.ParticipantId,
Title: proposal.Title,
PubKeyFingerprint: pubKeyFingerprint,
EncryptedInvitation: encryptedInvitationSecret,
@ -67,22 +81,66 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
}
// Change state
return responseData, nil
return inEvent, responseData, nil
}
//
func (m *SignatureProposalFSM) actionConfirmProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionConfirmProposalByParticipant")
// TODO: Add timeout checking
func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
// m.payloadMu.Lock()
// defer m.payloadMu.Unlock()
if len(args) != 1 {
err = errors.New("{arg0} required {SignatureProposalParticipantRequest}")
return
}
request, ok := args[0].(requests.SignatureProposalParticipantRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {SignatureProposalParticipantRequest}")
return
}
if err = request.Validate(); err != nil {
return
}
signatureProposalParticipant, ok := m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint]
if !ok {
err = errors.New("{PubKeyFingerprint} not exist in quorum")
return
}
if signatureProposalParticipant.InvitationSecret != request.DecryptedInvitation {
err = errors.New("{InvitationSecret} not match {DecryptedInvitation}")
return
}
signatureProposalParticipant.Status = internal.SignatureConfirmed
signatureProposalParticipant.UpdatedAt = request.CreatedAt
m.payload.ConfirmationProposalPayload[request.PubKeyFingerprint] = signatureProposalParticipant
outEvent, response, err = m.actionValidateProposal(eventValidateProposalInternal)
return
}
func (m *SignatureProposalFSM) actionDeclineProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDeclineProposalByParticipant")
return
}
func (m *SignatureProposalFSM) actionValidateProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
// m.payloadMu.Lock()
// defer m.payloadMu.Unlock()
func (m *SignatureProposalFSM) actionValidateProposal(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionValidateProposal")
unconfirmedParticipants := len(m.payload.ConfirmationProposalPayload)
for _, participant := range m.payload.ConfirmationProposalPayload {
if participant.Status == internal.SignatureConfirmed {
unconfirmedParticipants--
}
}
if unconfirmedParticipants > 0 {
return
}
outEvent = eventSetProposalValidatedInternal
return
}

View File

@ -1,10 +1,10 @@
package signature_proposal_fsm
import (
"crypto/sha256"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"encoding/base64"
"math/rand"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"github.com/depools/dc4bc/fsm/types/responses"
)
@ -27,7 +27,7 @@ func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum
// Common functions
func createFingerprint(data *[]byte) string {
hash := sha256.Sum256(*data)
hash := sha1.Sum(*data)
return base64.StdEncoding.EncodeToString(hash[:])
}
@ -58,6 +58,13 @@ func generateRandomString(s int) (string, error) {
return base64.URLEncoding.EncodeToString(b), err
}
func encryptWithPubKey(key []byte, value string) (string, error) {
return value, nil
func encryptWithPubKey(pubKey *rsa.PublicKey, value string) (string, error) {
r := rand.Reader
encryptedData, err := rsa.EncryptPKCS1v15(r, pubKey, []byte(value))
if err != nil {
return "", err
}
return string(encryptedData), nil
}

View File

@ -7,23 +7,25 @@ import (
)
const (
fsmName = "signature_proposal_fsm"
FsmName = "signature_proposal_fsm"
signingIdLen = 32
StateAwaitParticipantsConfirmations = fsm.State("state_validation_await_participants_confirmations") // waiting participants
StateParticipantsConfirmationsInit = fsm.StateGlobalIdle
StateValidationCanceledByParticipant = fsm.State("state_validation_canceled_by_participant")
StateValidationCanceledByTimeout = fsm.State("state_validation_canceled_by_timeout")
StateAwaitParticipantsConfirmations = fsm.State("state_sig_proposal_await_participants_confirmations") // waiting participants
StateValidationCompleted = fsm.State("state_validation_completed")
StateValidationCanceledByParticipant = fsm.State("state_sig_proposal_canceled_by_participant")
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
EventInitProposal = fsm.Event("event_proposal_init")
EventConfirmProposal = fsm.Event("event_proposal_confirm_by_participant")
EventDeclineProposal = fsm.Event("event_proposal_decline_by_participant")
EventValidateProposal = fsm.Event("event_proposal_validate")
EventSetProposalValidated = fsm.Event("event_proposal_set_validated")
StateValidationCompleted = fsm.State("state_sig_proposal_completed")
eventSetValidationCanceledByTimeout = fsm.Event("proposal_canceled_timeout")
EventInitProposal = fsm.Event("event_sig_proposal_init")
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant")
eventValidateProposalInternal = fsm.Event("event_sig_proposal_validate")
eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated")
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
// Switch to next fsm
@ -39,13 +41,13 @@ func New() internal.DumpedMachineProvider {
machine := &SignatureProposalFSM{}
machine.FSM = fsm.MustNewFSM(
fsmName,
FsmName,
fsm.StateGlobalIdle,
[]fsm.EventDesc{
// {Name: "", SrcState: []string{""}, DstState: ""},
// Init
{Name: EventInitProposal, SrcState: []fsm.State{fsm.StateGlobalIdle}, DstState: StateAwaitParticipantsConfirmations},
{Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations},
// Validate by participants
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
@ -53,20 +55,20 @@ func New() internal.DumpedMachineProvider {
// Now set for external emitting.
{Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant},
{Name: EventValidateProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
{Name: eventValidateProposalInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations, IsInternal: true},
// eventProposalValidate internal or from client?
// yay
// Exit point
{Name: EventSetProposalValidated, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: fsm.State("state_dkg_pub_keys_sending_required"), IsInternal: true},
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: fsm.State("state_dkg_pub_keys_sending_required"), IsInternal: true},
// nan
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
},
fsm.Callbacks{
EventInitProposal: machine.actionInitProposal,
EventConfirmProposal: machine.actionConfirmProposalByParticipant,
EventDeclineProposal: machine.actionDeclineProposalByParticipant,
EventValidateProposal: machine.actionValidateProposal,
EventInitProposal: machine.actionInitProposal,
EventConfirmProposal: machine.actionProposalResponseByParticipant,
EventDeclineProposal: machine.actionProposalResponseByParticipant,
eventValidateProposalInternal: machine.actionValidateProposal,
},
)
return machine

View File

@ -1,16 +1,49 @@
package requests
// Event: "event_dkg_pub_keys_sent"
type DKGProposalPubKeysSendingRequest struct {
PubKeys map[int][]byte
import "time"
// States: "state_dkg_pub_keys_await_confirmations"
// Events: "event_dkg_pub_key_confirm_received"
type DKGProposalPubKeyConfirmationRequest struct {
ParticipantId int
PubKey []byte
CreatedAt *time.Time
}
// Event: "event_dkg_commits_sent"
type DKGProposalCommitsSendingRequest struct {
Commits map[int][]byte
// States: "state_dkg_commits_sending_await_confirmations"
// Events: "event_dkg_commit_confirm_received"
type DKGProposalCommitConfirmationRequest struct {
ParticipantId int
Commit []byte
CreatedAt *time.Time
}
// Event: "event_dkg_deals_sent"
type DKGProposalDealsSendingRequest struct {
Deals map[int][]byte
// States: "state_dkg_deals_await_confirmations"
// Events: "event_dkg_deal_confirm_received"
type DKGProposalDealConfirmationRequest struct {
ParticipantId int
Deal []byte
CreatedAt *time.Time
}
// States: "state_dkg_responses_await_confirmations"
// Events: "event_dkg_response_confirm_received"
type DKGProposalResponseConfirmationRequest struct {
ParticipantId int
Response []byte
CreatedAt *time.Time
}
// States: "state_dkg_pub_keys_await_confirmations"
// "state_dkg_commits_sending_await_confirmations"
// "state_dkg_deals_await_confirmations"
// "state_dkg_responses_await_confirmations"
//
// Events: "event_dkg_pub_key_confirm_canceled_by_error",
// "event_dkg_commit_confirm_canceled_by_error"
// "event_dkg_deal_confirm_canceled_by_error"
// "event_dkg_response_confirm_canceled_by_error"
type DKGProposalConfirmationErrorRequest struct {
ParticipantId int
CreatedAt *time.Time
}

View File

@ -1 +1,79 @@
package requests
import "errors"
func (r *DKGProposalPubKeyConfirmationRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.PubKey) == 0 {
return errors.New("{PubKey} cannot zero length")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}
func (r *DKGProposalCommitConfirmationRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.Commit) == 0 {
return errors.New("{Commit} cannot zero length")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}
func (r *DKGProposalDealConfirmationRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.Deal) == 0 {
return errors.New("{Deal} cannot zero length")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}
func (r *DKGProposalResponseConfirmationRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.Response) == 0 {
return errors.New("{Response} cannot zero length")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}
func (r *DKGProposalConfirmationErrorRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}

View File

@ -1,17 +1,28 @@
package requests
import "time"
// Requests
type SignatureProposalParticipantsListRequest []SignatureProposalParticipantsEntry
// States: "__idle"
// Events: "event_sig_proposal_init"
type SignatureProposalParticipantsListRequest struct {
Participants []*SignatureProposalParticipantsEntry
CreatedAt *time.Time
}
type SignatureProposalParticipantsEntry struct {
// Public title for address, such as name, nickname, organization
Title string
PublicKey []byte
Title string
PubKey []byte
}
// States: "__idle"
// Events: "event_sig_proposal_confirm_by_participant"
// "event_sig_proposal_decline_by_participant"
type SignatureProposalParticipantRequest struct {
// Key for link invitations to participants
PubKeyFingerprint string
EncryptedInvitation string
DecryptedInvitation string
CreatedAt *time.Time
}

View File

@ -2,31 +2,47 @@ package requests
import (
"errors"
"fmt"
"github.com/depools/dc4bc/fsm/config"
)
func (r *SignatureProposalParticipantsListRequest) Validate() error {
if len(*r) < config.ParticipantsMinCount {
return errors.New("too few participants")
if len(r.Participants) < config.ParticipantsMinCount {
return errors.New(fmt.Sprintf("too few participants, minimum is {%d}", config.ParticipantsMinCount))
}
for _, participant := range *r {
for _, participant := range r.Participants {
if len(participant.Title) < 3 {
return errors.New("title too short")
return errors.New("{Title} minimum length is {3}")
}
if len(participant.Title) > 150 {
return errors.New("title too long")
return errors.New("{Title} maximum length is {150}")
}
if len(participant.PublicKey) < 10 {
return errors.New("pub key too short")
if len(participant.PubKey) < 10 {
return errors.New("{PubKey} too short")
}
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}
func (r *SignatureProposalParticipantRequest) Validate() error {
if len(r.PubKeyFingerprint) == 0 {
return errors.New("{PubKeyFingerprint} cannot zero length")
}
if len(r.DecryptedInvitation) == 0 {
return errors.New("{DecryptedInvitation} cannot zero length")
}
if r.CreatedAt == nil {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil
}

View File

@ -1,6 +1,6 @@
package responses
// Responses
// Response
const (
ProposalConfirmationStatusIdle = iota