feat: updated flow

This commit is contained in:
x88 2020-08-13 18:22:06 +03:00
parent 4ed9afc8dd
commit 33ecea0a9a
14 changed files with 185 additions and 353 deletions

View File

@ -41,7 +41,7 @@ func (e *Event) String() string {
return string(*e)
}
func (e *Event) IsEmpty() bool {
func (e Event) IsEmpty() bool {
return e.String() == ""
}
@ -63,7 +63,7 @@ type FSM struct {
// May be mapping must require pair source + event?
transitions map[trKey]*trEvent
autoTransitions map[State]*trEvent
autoTransitions map[trAutoKeyEvent]*trEvent
callbacks Callbacks
@ -94,6 +94,11 @@ type trEvent struct {
runMode EventRunMode
}
type trAutoKeyEvent struct {
state State
runMode EventRunMode
}
type EventDesc struct {
Name Event
@ -138,7 +143,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
currentState: initialState,
initialState: initialState,
transitions: make(map[trKey]*trEvent),
autoTransitions: make(map[State]*trEvent),
autoTransitions: make(map[trAutoKeyEvent]*trEvent),
finStates: make(map[State]bool),
callbacks: make(map[Event]Callback),
}
@ -217,14 +222,15 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
panic("{AutoRunMode} not set for auto event")
}
if _, ok := f.autoTransitions[sourceState]; ok {
trAutoKey := trAutoKeyEvent{sourceState, event.AutoRunMode}
if _, ok := f.autoTransitions[trAutoKey]; ok {
panic(fmt.Sprintf(
"auto event \"%s\" already exists for state \"%s\"",
event.Name,
sourceState,
))
}
f.autoTransitions[sourceState] = trEvent
f.autoTransitions[trAutoKey] = trEvent
}
allSources[sourceState] = true
@ -247,7 +253,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
}
if _, ok := allEvents[event]; !ok {
panic("callback has empty event")
panic(fmt.Sprintf("callback has unused event \"%s\"", event))
}
f.callbacks[event] = callback
@ -273,7 +279,7 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
func (f *FSM) DoInternal(event Event, args ...interface{}) (resp *Response, err error) {
trEvent, ok := f.transitions[trKey{f.currentState, event}]
if !ok {
return nil, errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState))
return nil, errors.New(fmt.Sprintf("cannot execute internal event \"%s\" for state \"%s\"", event, f.currentState))
}
return f.do(trEvent, args...)
@ -290,41 +296,55 @@ func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) {
return f.do(trEvent, args...)
}
// Check and execute auto event
func (f *FSM) processAutoEvent(mode EventRunMode, args ...interface{}) (exists bool, outEvent Event, response interface{}, err error) {
autoEvent, exists := f.autoTransitions[trAutoKeyEvent{f.State(), mode}]
if !exists {
return
}
if f.isCallbackExists(autoEvent.event) {
outEvent, response, err = f.execCallback(autoEvent.event, args...)
// Do not try change state on error
if err != nil {
return exists, "", response, err
}
}
if outEvent.IsEmpty() || autoEvent.event == outEvent {
err = f.SetState(autoEvent.event)
} else {
err = f.SetState(outEvent)
}
return
}
func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) {
var outEvent Event
// f.eventMu.Lock()
// defer f.eventMu.Unlock()
resp = &Response{}
// Process auto event
if autoEvent, ok := f.autoTransitions[f.State()]; ok {
autoEventResp := &Response{
State: f.State(),
isAutoEventExecuted, outEvent, data, err := f.processAutoEvent(EventRunBefore, args...)
if isAutoEventExecuted {
resp.State = f.State()
if data != nil {
resp.Data = data
}
if autoEvent.runMode == EventRunBefore {
if callback, ok := f.callbacks[autoEvent.event]; ok {
outEvent, autoEventResp.Data, err = callback(autoEvent.event, args...)
if err != nil {
return autoEventResp, err
}
}
if outEvent.IsEmpty() || autoEvent.event == outEvent {
err = f.SetState(autoEvent.event)
} else {
err = f.SetState(outEvent)
}
if err != nil {
return autoEventResp, err
}
if err != nil {
return resp, err
}
outEvent = ""
}
resp = &Response{
State: f.State(),
}
if callback, ok := f.callbacks[trEvent.event]; ok {
outEvent, resp.Data, err = callback(trEvent.event, args...)
if f.isCallbackExists(trEvent.event) {
outEvent, resp.Data, err = f.execCallback(trEvent.event, args...)
// Do not try change state on error
if err != nil {
return resp, err
@ -334,36 +354,32 @@ func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err err
// Set state when callback executed
if outEvent.IsEmpty() || trEvent.event == outEvent {
err = f.SetState(trEvent.event)
if err != nil {
return resp, err
}
} else {
err = f.SetState(outEvent)
}
// Process auto event
if autoEvent, ok := f.autoTransitions[f.State()]; ok {
autoEventResp := &Response{
State: f.State(),
if err != nil {
return resp, err
}
if autoEvent.runMode == EventRunAfter {
if callback, ok := f.callbacks[autoEvent.event]; ok {
outEvent, autoEventResp.Data, err = callback(autoEvent.event, args...)
if err != nil {
return autoEventResp, err
}
}
if outEvent.IsEmpty() || autoEvent.event == outEvent {
err = f.SetState(autoEvent.event)
} else {
err = f.SetState(outEvent)
}
if err != nil {
return autoEventResp, err
}
}
outEvent = ""
}
resp.State = f.State()
// Process auto event
isAutoEventExecuted, outEvent, data, err = f.processAutoEvent(EventRunAfter, args...)
if isAutoEventExecuted {
resp.State = f.State()
if data != nil {
resp.Data = data
}
if err != nil {
return resp, err
}
}
return
}
@ -382,7 +398,7 @@ func (f *FSM) SetState(event Event) error {
trEvent, ok := f.transitions[trKey{f.currentState, event}]
if !ok {
return errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState))
return errors.New(fmt.Sprintf("cannot set state by event \"%s\" for state \"%s\"", event, f.currentState))
}
f.currentState = trEvent.dstState
@ -457,6 +473,16 @@ func (f *FSM) StatesSourcesList() (states []State) {
return
}
func (f *FSM) isCallbackExists(event Event) bool {
_, exists := f.callbacks[event]
return exists
}
func (f *FSM) execCallback(event Event, args ...interface{}) (Event, interface{}, error) {
callback, _ := f.callbacks[event]
return callback(event, args...)
}
func (f *FSM) IsFinState(state State) bool {
_, exists := f.finStates[state]
return exists

View File

@ -160,17 +160,3 @@ func (p *FSMPool) MachineByState(state fsm.State) (MachineProvider, error) {
}
return machine, nil
}
/*func (p *FSMPool) Do(machine MachineProvider, event fsm.Event, args ...interface{}) (resp *fsm.Response, err error) {
panic("llslsl")
resp, err = machine.Do(event, args...)
if err != nil {
return resp, err
}
if machine.IsFinState(resp.State) {
log.Println("Final!!!!")
}
return
}
*/

View File

@ -11,94 +11,27 @@ import (
"time"
)
// Pub keys
// Init
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}")
func (m *DKGProposalFSM) actionInitDKGProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
if m.payload.DKGProposalPayload != nil {
return
}
request, ok := args[0].(requests.DKGProposalPubKeyConfirmationRequest)
if !ok {
err = errors.New("cannot cast {arg0} to type {DKGProposalPubKeyConfirmationRequest}")
return
m.payload.DKGProposalPayload = &internal.DKGConfirmation{
Quorum: make(internal.DKGProposalQuorum),
}
if err = request.Validate(); err != nil {
return
}
if !m.payload.DKGQuorumExists(request.ParticipantId) {
err = errors.New("{ParticipantId} not exist in quorum")
return
}
dkgProposalParticipant := m.payload.DKGQuorumGet(request.ParticipantId)
if dkgProposalParticipant.Status != internal.PubKeyAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm pubkey with {Status} = {\"%s\"}", dkgProposalParticipant.Status))
return
}
copy(dkgProposalParticipant.PubKey, request.PubKey)
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.Status = internal.PubKeyConfirmed
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
return
}
func (m *DKGProposalFSM) actionValidateDkgProposalPubKeys(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
var (
isContainsError, isContainsExpired bool
)
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
tm := time.Now()
unconfirmedParticipants := m.payload.DKGQuorumCount()
for _, participant := range m.payload.DKGProposalPayload.Quorum {
if participant.Status == internal.PubKeyAwaitConfirmation {
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
isContainsExpired = true
}
} else {
if participant.Status == internal.PubKeyConfirmationError {
isContainsError = true
} else if participant.Status == internal.PubKeyConfirmed {
unconfirmedParticipants--
}
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{
Title: participant.Title,
Status: internal.CommitAwaitConfirmation,
UpdatedAt: participant.UpdatedAt,
}
copy(m.payload.DKGProposalPayload.Quorum[participant.ParticipantId].PubKey, participant.DkgPubKey)
}
if isContainsError {
outEvent = eventDKGSetPubKeysConfirmationCanceledByErrorInternal
return
}
if isContainsExpired {
outEvent = eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal
return
}
// The are no declined and timed out participants, check for all confirmations
if unconfirmedParticipants > 0 {
return
}
outEvent = eventDKGSetPubKeysConfirmedInternal
for _, participant := range m.payload.DKGProposalPayload.Quorum {
participant.Status = internal.CommitAwaitConfirmation
}
// Remove m.payload.SignatureProposalPayload?
return
}
@ -138,7 +71,7 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
}
copy(dkgProposalParticipant.Commit, request.Commit)
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.CommitConfirmed
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
@ -230,7 +163,7 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
}
copy(dkgProposalParticipant.Deal, request.Deal)
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.DealConfirmed
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
@ -322,7 +255,7 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
}
copy(dkgProposalParticipant.Response, request.Response)
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.ResponseConfirmed
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
@ -414,7 +347,7 @@ func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event,
}
copy(dkgProposalParticipant.MasterKey, request.MasterKey)
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.UpdatedAt = request.CreatedAt
dkgProposalParticipant.Status = internal.MasterKeyConfirmed
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
@ -525,21 +458,6 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
// TODO: Move to methods
switch inEvent {
case EventDKGPubKeyConfirmationError:
switch dkgProposalParticipant.Status {
case internal.PubKeyAwaitConfirmation:
dkgProposalParticipant.Status = internal.PubKeyConfirmationError
case internal.PubKeyConfirmed:
err = errors.New("{Status} already confirmed")
case internal.PubKeyConfirmationError:
err = errors.New(fmt.Sprintf("{Status} already has {\"%s\"}", internal.PubKeyConfirmationError))
default:
err = errors.New(fmt.Sprintf(
"{Status} now is \"%s\" and cannot set to {\"%s\"}",
dkgProposalParticipant.Status,
internal.PubKeyConfirmationError,
))
}
case EventDKGCommitConfirmationError:
switch dkgProposalParticipant.Status {
case internal.CommitAwaitConfirmation:
@ -609,7 +527,7 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
}
dkgProposalParticipant.Error = request.Error
dkgProposalParticipant.UpdatedAt = &request.CreatedAt
dkgProposalParticipant.UpdatedAt = request.CreatedAt
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)

View File

@ -9,14 +9,7 @@ import (
const (
FsmName = "dkg_proposal_fsm"
StateDkgInitial = StateDkgPubKeysAwaitConfirmations
StateDkgPubKeysAwaitConfirmations = fsm.State("state_dkg_pub_keys_await_confirmations")
// Canceled
StateDkgPubKeysAwaitCanceled = fsm.State("state_dkg_pub_keys_await_canceled")
StateDkgPubKeysAwaitCanceledByTimeout = fsm.State("state_dkg_pub_keys_await_canceled_by_timeout")
// Confirmed
// StateDkgPubKeysAwaitConfirmed = fsm.State("state_dkg_pub_keys_await_confirmed")
StateDkgInitial = StateDkgCommitsAwaitConfirmations
// Sending dkg commits
StateDkgCommitsAwaitConfirmations = fsm.State("state_dkg_commits_await_confirmations")
@ -24,7 +17,7 @@ const (
StateDkgCommitsAwaitCanceled = fsm.State("state_dkg_commits_await_canceled")
StateDkgCommitsAwaitCanceledByTimeout = fsm.State("state_dkg_commits_await_canceled_by_timeout")
// Confirmed
StateDkgCommitsAwaitConfirmed = fsm.State("state_dkg_commits_await_confirmed")
StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected")
// Sending dkg deals
StateDkgDealsAwaitConfirmations = fsm.State("state_dkg_deals_await_confirmations")
@ -32,31 +25,23 @@ const (
StateDkgDealsAwaitCanceled = fsm.State("state_dkg_deals_await_canceled")
StateDkgDealsAwaitCanceledByTimeout = fsm.State("state_dkg_deals_await_canceled_by_timeout")
// Confirmed
//StateDkgDealsAwaitConfirmed = fsm.State("state_dkg_deals_await_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")
StateDkgResponsesAwaitCanceledByTimeout = fsm.State("state_dkg_responses_sending_canceled_by_timeout")
// Confirmed
StateDkgResponsesAwaitConfirmed = fsm.State("state_dkg_responses_await_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")
StateDkgMasterKeyAwaitCanceledByTimeout = fsm.State("state_dkg_master_key_await_canceled_by_timeout")
StateDkgMasterKeyCollected = fsm.State("state_dkg_master_key_collected")
// Events
eventAutoDKGInitialInternal = fsm.Event("event_dkg_init_internal")
EventDKGPubKeyConfirmationReceived = fsm.Event("event_dkg_pub_key_confirm_received")
EventDKGPubKeyConfirmationError = fsm.Event("event_dkg_pub_key_confirm_canceled_by_error")
eventAutoDKGValidatePubKeysConfirmationInternal = fsm.Event("event_dkg_pub_keys_validate_internal")
eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_timeout_internal")
eventDKGSetPubKeysConfirmationCanceledByErrorInternal = fsm.Event("event_dkg_pub_keys_confirm_canceled_by_error_internal")
eventDKGSetPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal")
eventAutoDKGInitialInternal = fsm.Event("event_dkg_init")
EventDKGCommitConfirmationReceived = fsm.Event("event_dkg_commit_confirm_received")
EventDKGCommitConfirmationError = fsm.Event("event_dkg_commit_confirm_canceled_by_error")
@ -109,18 +94,11 @@ func New() internal.DumpedMachineProvider {
// {Name: eventAutoDKGInitialInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore},
// Pub keys sending
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations},
// Canceled
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceled},
// StateDkgCommitsCollected = fsm.State("state_dkg_commits_collected")
{Name: eventAutoDKGValidatePubKeysConfirmationInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitConfirmations, IsInternal: true, IsAuto: true},
// {Name: eventAutoDKGInitInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore},
{Name: eventDKGSetPubKeysConfirmationCanceledByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgPubKeysAwaitCanceledByTimeout, IsInternal: true},
// Confirmed
{Name: eventDKGSetPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true},
// Switch to commits required
{Name: eventAutoDKGInitialInternal, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations, IsInternal: true, IsAuto: true, AutoRunMode: fsm.EventRunBefore},
// Commits
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsAwaitConfirmations}, DstState: StateDkgCommitsAwaitConfirmations},
@ -166,10 +144,7 @@ func New() internal.DumpedMachineProvider {
// {Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgResponsesAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
},
fsm.Callbacks{
EventDKGPubKeyConfirmationReceived: machine.actionPubKeyConfirmationReceived,
EventDKGPubKeyConfirmationError: machine.actionConfirmationError,
eventAutoDKGValidatePubKeysConfirmationInternal: machine.actionValidateDkgProposalPubKeys,
eventAutoDKGInitialInternal: machine.actionInitDKGProposal,
EventDKGCommitConfirmationReceived: machine.actionCommitConfirmationReceived,
EventDKGCommitConfirmationError: machine.actionConfirmationError,

View File

@ -7,19 +7,20 @@ import (
type SignatureConfirmation struct {
Quorum SignatureProposalQuorum
CreatedAt *time.Time
ExpiresAt *time.Time
CreatedAt time.Time
ExpiresAt time.Time
}
type SignatureProposalParticipant struct {
// Public title for address, such as name, nickname, organization
ParticipantId int
Title string
PublicKey *rsa.PublicKey
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user
PubKey *rsa.PublicKey
DkgPubKey []byte
// For validation user confirmation: sign(InvitationSecret, PubKey) => user
InvitationSecret string
Status ParticipantStatus
UpdatedAt *time.Time
UpdatedAt time.Time
}
// Unique alias for map iteration - Public Key Fingerprint
@ -33,9 +34,6 @@ const (
SignatureConfirmationConfirmed
SignatureConfirmationDeclined
SignatureConfirmationError
PubKeyAwaitConfirmation
PubKeyConfirmed
PubKeyConfirmationError
CommitAwaitConfirmation
CommitConfirmed
CommitConfirmationError
@ -59,7 +57,7 @@ type DKGProposalParticipant struct {
MasterKey []byte
Status ParticipantStatus
Error error
UpdatedAt *time.Time
UpdatedAt time.Time
}
type DKGProposalQuorum map[int]*DKGProposalParticipant
@ -83,12 +81,6 @@ func (s ParticipantStatus) String() string {
str = "SignatureConfirmationDeclined"
case SignatureConfirmationError:
str = "SignatureConfirmationError"
case PubKeyAwaitConfirmation:
str = "PubKeyAwaitConfirmation"
case PubKeyConfirmed:
str = "PubKeyConfirmed"
case PubKeyConfirmationError:
str = "PubKeyConfirmationError"
case CommitAwaitConfirmation:
str = "CommitAwaitConfirmation"
case CommitConfirmed:

View File

@ -17,14 +17,11 @@ import (
"time"
)
const (
testTransactionId = "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257"
)
type testExternalParticipants struct {
Title string
PrivKey *rsa.PrivateKey
PubKey *rsa.PublicKey
Title string
PrivKey *rsa.PrivateKey
PubKey *rsa.PublicKey
DkgPubKey []byte
}
var (
@ -34,7 +31,7 @@ var (
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
Participants: []*requests.SignatureProposalParticipantsEntry{},
CreatedAt: &tm,
CreatedAt: tm,
}
testFSMDump []byte
@ -59,10 +56,15 @@ func init() {
fingerprint := base64.StdEncoding.EncodeToString(hash[:])
pubKeyMock := make([]byte, 128)
rand.Read(pubKeyMock)
participant := &testExternalParticipants{
Title: fmt.Sprintf("User %d", i),
PrivKey: key,
PubKey: &key.PublicKey,
Title: fmt.Sprintf("User %d", i),
PrivKey: key,
PubKey: &key.PublicKey,
DkgPubKey: pubKeyMock,
}
testParticipants[fingerprint] = participant
}
@ -72,8 +74,9 @@ func init() {
for _, participant := range testParticipants {
participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{
Title: participant.Title,
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
Title: participant.Title,
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
DkgPubKey: participant.DkgPubKey,
})
}
testParticipantsListRequest.Participants = participantsForRequest
@ -197,7 +200,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
participantsMap[participant.ParticipantId] = participant
}
tm = tm.Add(10 * time.Hour)
tm = tm.Add(1 * time.Hour)
participantsCount := len(participantsMap)
@ -222,10 +225,10 @@ func Test_SignatureProposal_Positive(t *testing.T) {
t.Fatalf("cannot encrypt {DecryptedInvitation} with private key")
}
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmSignatureProposal, requests.SignatureProposalParticipantRequest{
PubKeyFingerprint: participant.PubKeyFingerprint,
DecryptedInvitation: string(encrypted),
CreatedAt: &tm,
CreatedAt: tm,
})
compareErrNil(t, err)
@ -237,45 +240,11 @@ func Test_SignatureProposal_Positive(t *testing.T) {
if participantCounter > 0 {
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
} else {
compareState(t, dpf.StateDkgInitial, fsmResponse.State)
compareState(t, spf.StateSignatureProposalCollected, fsmResponse.State)
}
}
// PubKeys
for _, participant := range participantsMap {
participantCounter--
testFSMInstance, err = FromDump(dump)
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
t.Fatalf("not found external user data for response fingerprint")
}
pubKeyMock := make([]byte, 128)
_, err := rand.Read(pubKeyMock)
if err != nil {
compareErrNil(t, err)
}
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGPubKeyConfirmationReceived, requests.DKGProposalPubKeyConfirmationRequest{
ParticipantId: participant.ParticipantId,
PubKey: pubKeyMock,
CreatedAt: tm,
})
compareErrNil(t, err)
compareDumpNotZero(t, dump)
compareFSMResponseNotNil(t, fsmResponse)
}
compareState(t, dpf.StateDkgCommitsAwaitConfirmations, fsmResponse.State)
// Commits

View File

@ -14,7 +14,7 @@ import (
// init -> awaitingConfirmations
// args: payload, signing id, participants list
func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
m.payloadMu.Lock()
defer m.payloadMu.Unlock()
@ -54,7 +54,8 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
m.payload.SignatureProposalPayload.Quorum[participantId] = &internal.SignatureProposalParticipant{
ParticipantId: index,
Title: participant.Title,
PublicKey: parsedPubKey,
PubKey: parsedPubKey,
DkgPubKey: participant.DkgPubKey,
InvitationSecret: secret,
Status: internal.SignatureConfirmationAwaitConfirmation,
UpdatedAt: request.CreatedAt,
@ -72,7 +73,7 @@ func (m *SignatureProposalFSM) actionInitProposal(inEvent fsm.Event, args ...int
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
for pubKeyFingerprint, proposal := range m.payload.SignatureProposalPayload.Quorum {
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret)
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PubKey, proposal.InvitationSecret)
if err != nil {
return inEvent, nil, errors.New("cannot encryptWithPubKey")
}
@ -122,7 +123,7 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
return
}
if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(*request.CreatedAt) {
if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(request.CreatedAt) {
outEvent = eventSetValidationCanceledByTimeout
return
}
@ -133,7 +134,7 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
}
switch inEvent {
case EventConfirmProposal:
case EventConfirmSignatureProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationConfirmed
case EventDeclineProposal:
signatureProposalParticipant.Status = internal.SignatureConfirmationDeclined
@ -189,37 +190,24 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event
return
}
outEvent = eventSetProposalValidatedInternal
m.actionSetValidatedSignatureProposal(outEvent)
return
}
func (m *SignatureProposalFSM) actionSetValidatedSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
// m.payloadMu.Lock()
// defer m.payloadMu.Unlock()
// TODO: Run once after validation
if m.payload.DKGProposalPayload != nil {
err = m.SetState(eventSetProposalValidatedInternal)
if err != nil {
return
}
m.payload.DKGProposalPayload = &internal.DKGConfirmation{
Quorum: make(internal.DKGProposalQuorum),
}
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{
Title: participant.Title,
Status: internal.PubKeyAwaitConfirmation,
UpdatedAt: participant.UpdatedAt,
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
ParticipantId: participant.ParticipantId,
Title: participant.Title,
DkgPubKey: participant.DkgPubKey,
Status: uint8(participant.Status),
}
responseData = append(responseData, responseEntry)
}
// Remove m.payload.SignatureProposalPayload?
return
return eventDoneInternal, responseData, nil
}
func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
@ -228,12 +216,11 @@ func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
for pubKeyFingerprint, participant := range m.payload.SignatureProposalPayload.Quorum {
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
ParticipantId: participant.ParticipantId,
Title: participant.Title,
PubKeyFingerprint: pubKeyFingerprint,
Status: uint8(participant.Status),
ParticipantId: participant.ParticipantId,
Title: participant.Title,
Status: uint8(participant.Status),
}
responseData = append(responseData, responseEntry)
}

View File

@ -2,7 +2,7 @@ package signature_proposal_fsm
import (
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
"github.com/depools/dc4bc/fsm/state_machines/internal"
"sync"
)
@ -21,14 +21,18 @@ const (
// Out state
EventInitProposal = fsm.Event("event_sig_proposal_init")
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
EventConfirmSignatureProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant")
eventAutoValidateProposalInternal = fsm.Event("event_sig_proposal_validate")
eventSetProposalValidatedInternal = fsm.Event("event_sig_proposal_set_validated")
eventDoneInternal = fsm.Event("event_sig_proposal_done")
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
eventSetValidationCanceledByParticipant = fsm.Event("event_sig_proposal_declined_timeout")
StateSignatureProposalCollected = fsm.State("state_sig_proposal_collected")
// Switch to next fsm
)
@ -52,7 +56,7 @@ func New() internal.DumpedMachineProvider {
{Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations},
// Validate by participants
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
{Name: EventConfirmSignatureProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
// Is decline event should auto change state to default, or it process will initiated by client (external emit)?
// Now set for external emitting.
{Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
@ -62,17 +66,19 @@ func New() internal.DumpedMachineProvider {
// eventProposalValidate internal or from client?
// yay
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateSignatureProposalCollected, IsInternal: true},
// Exit point
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: dkg_proposal_fsm.StateDkgPubKeysAwaitConfirmations, IsInternal: true},
{Name: eventDoneInternal, SrcState: []fsm.State{StateSignatureProposalCollected}, DstState: dpf.StateDkgInitial, IsInternal: true},
// nan
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
},
fsm.Callbacks{
EventInitProposal: machine.actionInitProposal,
EventConfirmProposal: machine.actionProposalResponseByParticipant,
EventInitProposal: machine.actionInitSignatureProposal,
EventConfirmSignatureProposal: machine.actionProposalResponseByParticipant,
EventDeclineProposal: machine.actionProposalResponseByParticipant,
eventAutoValidateProposalInternal: machine.actionValidateSignatureProposal,
eventSetProposalValidatedInternal: machine.actionSetValidatedSignatureProposal,
},
)
return machine

View File

@ -2,14 +2,6 @@ package requests
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
}
// States: "state_dkg_commits_sending_await_confirmations"
// Events: "event_dkg_commit_confirm_received"
type DKGProposalCommitConfirmationRequest struct {

View File

@ -2,22 +2,6 @@ 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.IsZero() {
return errors.New("{CreatedAt} is not set")
}
return nil
}
func (r *DKGProposalCommitConfirmationRequest) Validate() error {
if r.ParticipantId < 0 {
return errors.New("{ParticipantId} cannot be a negative number")

View File

@ -8,13 +8,14 @@ import "time"
// Events: "event_sig_proposal_init"
type SignatureProposalParticipantsListRequest struct {
Participants []*SignatureProposalParticipantsEntry
CreatedAt *time.Time
CreatedAt time.Time
}
type SignatureProposalParticipantsEntry struct {
// Public title for address, such as name, nickname, organization
Title string
PubKey []byte
Title string
PubKey []byte
DkgPubKey []byte
}
// States: "__idle"
@ -24,5 +25,5 @@ type SignatureProposalParticipantRequest struct {
// Key for link invitations to participants
PubKeyFingerprint string
DecryptedInvitation string
CreatedAt *time.Time
CreatedAt time.Time
}

View File

@ -23,9 +23,13 @@ func (r *SignatureProposalParticipantsListRequest) Validate() error {
if len(participant.PubKey) < 10 {
return errors.New("{PubKey} too short")
}
if len(participant.DkgPubKey) < 10 {
return errors.New("{DkgPubKey} too short")
}
}
if r.CreatedAt == nil {
if r.CreatedAt.IsZero() {
return errors.New("{CreatedAt} cannot be a nil")
}
@ -41,7 +45,7 @@ func (r *SignatureProposalParticipantRequest) Validate() error {
return errors.New("{DecryptedInvitation} cannot zero length")
}
if r.CreatedAt == nil {
if r.CreatedAt.IsZero() {
return errors.New("{CreatedAt} cannot be a nil")
}
return nil

View File

@ -1,13 +1,5 @@
package responses
type DKGProposalPubKeyParticipantResponse []*DKGProposalPubKeyParticipantEntry
type DKGProposalPubKeyParticipantEntry struct {
ParticipantId int
Title string
PubKey []byte
}
type DKGProposalCommitParticipantResponse []*DKGProposalCommitParticipantEntry
type DKGProposalCommitParticipantEntry struct {

View File

@ -22,8 +22,8 @@ type SignatureProposalParticipantInvitationEntry struct {
type SignatureProposalParticipantStatusResponse []*SignatureProposalParticipantStatusEntry
type SignatureProposalParticipantStatusEntry struct {
ParticipantId int
Title string
PubKeyFingerprint string
Status uint8
ParticipantId int
Title string
DkgPubKey []byte
Status uint8
}