Merge pull request #13 from depools/feat-dkg-proposal

feat: dkg_proposal
This commit is contained in:
Dmitriy 2020-08-07 10:58:23 +03:00 committed by GitHub
commit b86868ea92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 672 additions and 148 deletions

View File

@ -13,9 +13,9 @@ func main() {
fsmMachine, err := state_machines.New([]byte{}) fsmMachine, err := state_machines.New([]byte{})
log.Println(fsmMachine, err) log.Println(fsmMachine, err)
resp, dump, err := fsmMachine.Do( resp, dump, err := fsmMachine.Do(
"proposal_init", "event_proposal_init",
"d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257", "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257",
requests.ProposalParticipantsListRequest{ requests.SignatureProposalParticipantsListRequest{
{ {
"John Doe", "John Doe",
[]byte("pubkey123123"), []byte("pubkey123123"),
@ -41,7 +41,7 @@ func processResponse(resp *fsm.Response) {
switch resp.State { switch resp.State {
// Await proposals // Await proposals
case fsm.State("validate_proposal"): case fsm.State("validate_proposal"):
data, ok := resp.Data.(responses.ProposalParticipantInvitationsResponse) data, ok := resp.Data.(responses.SignatureProposalParticipantInvitationsResponse)
if !ok { if !ok {
log.Printf("undefined response type for state \"%s\"\n", resp.State) log.Printf("undefined response type for state \"%s\"\n", resp.State)
return return
@ -57,7 +57,7 @@ func processResponse(resp *fsm.Response) {
} }
} }
func sendInvitations(invitations responses.ProposalParticipantInvitationsResponse) { func sendInvitations(invitations responses.SignatureProposalParticipantInvitationsResponse) {
for _, invitation := range invitations { for _, invitation := range invitations {
log.Printf( log.Printf(
"Dear %s, please encrypt value \"%s\" with your key, fingerprint: %s\n", "Dear %s, please encrypt value \"%s\" with your key, fingerprint: %s\n",

View File

@ -73,8 +73,10 @@ type trKey struct {
// Transition lightweight event description // Transition lightweight event description
type trEvent struct { type trEvent struct {
event Event
dstState State dstState State
isInternal bool isInternal bool
isDstInit bool
} }
type EventDesc struct { type EventDesc struct {
@ -87,6 +89,9 @@ type EventDesc struct {
// Internal events, cannot be emitted from external call // Internal events, cannot be emitted from external call
IsInternal bool IsInternal bool
// Set dst state before execute action
IsDstInit bool
} }
type Callback func(event Event, args ...interface{}) (interface{}, error) type Callback func(event Event, args ...interface{}) (interface{}, error)
@ -168,7 +173,12 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
panic("duplicate dst for pair `source + event`") panic("duplicate dst for pair `source + event`")
} }
f.transitions[tKey] = &trEvent{event.DstState, event.IsInternal} f.transitions[tKey] = &trEvent{
tKey.event,
event.DstState,
event.IsInternal,
event.IsDstInit,
}
// For using provider, event must use with IsGlobal = true // For using provider, event must use with IsGlobal = true
if sourceState == initialState { if sourceState == initialState {
@ -221,34 +231,58 @@ func MustNewFSM(machineName string, initialState State, events []EventDesc, call
return f return f
} }
func (f *FSM) Do(event Event, args ...interface{}) (resp *Response, err error) { func (f *FSM) DoInternal(event Event, args ...interface{}) (resp *Response, err error) {
f.eventMu.Lock()
defer f.eventMu.Unlock()
trEvent, ok := f.transitions[trKey{f.currentState, event}] trEvent, ok := f.transitions[trKey{f.currentState, event}]
if !ok { if !ok {
return nil, errors.New("cannot execute event for this state") return nil, errors.New(fmt.Sprintf("cannot execute event \"%s\" for state \"%s\"", event, f.currentState))
}
return f.do(trEvent, args...)
}
func (f *FSM) Do(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))
} }
if trEvent.isInternal { if trEvent.isInternal {
return nil, errors.New("event is internal") return nil, errors.New("event is internal")
} }
return f.do(trEvent, args...)
}
func (f *FSM) do(trEvent *trEvent, args ...interface{}) (resp *Response, err error) {
// f.eventMu.Lock()
// defer f.eventMu.Unlock()
if trEvent.isDstInit {
err = f.SetState(trEvent.event)
if err != nil {
resp = &Response{
State: f.State(),
}
return resp, err
}
}
resp = &Response{ resp = &Response{
State: f.State(), State: f.State(),
} }
if callback, ok := f.callbacks[event]; ok { if callback, ok := f.callbacks[trEvent.event]; ok {
resp.Data, err = callback(event, args...) resp.Data, err = callback(trEvent.event, args...)
// Do not try change state on error // Do not try change state on error
if err != nil { if err != nil {
return resp, err return resp, err
} }
} }
err = f.setState(event) if !trEvent.isDstInit {
if err == nil { err = f.SetState(trEvent.event)
resp.State = f.currentState
} }
resp.State = f.State()
return return
} }
@ -259,9 +293,9 @@ func (f *FSM) State() State {
return f.currentState return f.currentState
} }
// setState allows the user to move to the given state from currentState state. // SetState allows the user to move to the given state from currentState state.
// The call does not trigger any callbacks, if defined. // The call does not trigger any callbacks, if defined.
func (f *FSM) setState(event Event) error { func (f *FSM) SetState(event Event) error {
f.stateMu.Lock() f.stateMu.Lock()
defer f.stateMu.Unlock() defer f.stateMu.Unlock()

View File

@ -2,6 +2,7 @@ package fsm_pool
import ( import (
"errors" "errors"
"fmt"
"github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/fsm"
) )
@ -15,6 +16,8 @@ type MachineProvider interface {
InitialState() fsm.State InitialState() fsm.State
State() fsm.State
// Process event // Process event
Do(event fsm.Event, args ...interface{}) (*fsm.Response, error) Do(event fsm.Event, args ...interface{}) (*fsm.Response, error)
@ -75,7 +78,7 @@ func Init(machines ...MachineProvider) *FSMPool {
machineEvents := machine.EventsList() machineEvents := machine.EventsList()
for _, event := range machineEvents { for _, event := range machineEvents {
if _, exists := p.events[event]; exists { if _, exists := p.events[event]; exists {
panic("duplicate public event") panic(fmt.Sprintf("duplicate public event \"%s\"", event))
} }
p.events[event] = machineName p.events[event] = machineName
} }
@ -106,7 +109,7 @@ func Init(machines ...MachineProvider) *FSMPool {
} }
} }
if name, exists := p.states[state]; exists && name != machineName { if name, exists := p.states[state]; exists && name != machineName {
panic("duplicate state for machines") panic(fmt.Sprintf("duplicate state for machines \"%s\"", state))
} }
p.states[state] = machineName p.states[state] = machineName
@ -120,8 +123,6 @@ func Init(machines ...MachineProvider) *FSMPool {
} }
func (p *FSMPool) EntryPointMachine() (MachineProvider, error) { func (p *FSMPool) EntryPointMachine() (MachineProvider, error) {
// StateGlobalIdle
// TODO: Short code
entryStateMachineName := p.events[p.fsmInitialEvent] entryStateMachineName := p.events[p.fsmInitialEvent]
machine, exists := p.mapper[entryStateMachineName] machine, exists := p.mapper[entryStateMachineName]
@ -142,6 +143,7 @@ func (p *FSMPool) MachineByEvent(event fsm.Event) (MachineProvider, error) {
return machine, nil return machine, nil
} }
// Out states now is not returns machine
func (p *FSMPool) MachineByState(state fsm.State) (MachineProvider, error) { func (p *FSMPool) MachineByState(state fsm.State) (MachineProvider, error) {
eventMachineName := p.states[state] eventMachineName := p.states[state]
machine, exists := p.mapper[eventMachineName] machine, exists := p.mapper[eventMachineName]

View File

@ -1,18 +1,284 @@
package fsm_pool package fsm_pool
/*
import ( import (
"github.com/depools/dc4bc/fsm/fsm"
"testing" "testing"
) )
var ( type testMachineFSM1 struct {
*fsm.FSM
) data int
func init() {
} }
func Test_InitPool(t *testing.T) { const (
testVal1 = 100
testVal2 = 17
)
} */ const (
fsm1Name = "fsm1"
// Init process from global idle state
fsm1StateInit = fsm.StateGlobalIdle
// Set up data
fsm1StateStage1 = fsm.State("state_fsm1_stage1")
// Process data
fsm1StateStage2 = fsm.State("state_fsm1_stage2")
// Cancelled with internal event
fsm1StateCanceledByInternal = fsm.State("state_fsm1_canceled")
// Cancelled with external event
fsm1StateCanceled2 = fsm.State("state_fsm1_canceled2")
// Out endpoint to switch
fsm1StateOutToFSM2 = fsm.State("state_fsm1_out_to_fsm2")
// Events
eventFSM1Init = fsm.Event("event_fsm1_init")
eventFSM1Cancel = fsm.Event("event_fsm1_cancel")
eventFSM1Process = fsm.Event("event_fsm1_process")
// Internal events
eventFSM1Internal = fsm.Event("event_internal_fsm1")
eventFSM1CancelByInternal = fsm.Event("event_internal_fsm1_cancel")
eventFSM1InternalOut2 = fsm.Event("event_internal_fsm1_out")
)
var (
testing1Events = []fsm.EventDesc{
// Init
{Name: eventFSM1Init, SrcState: []fsm.State{fsm1StateInit}, DstState: fsm1StateStage1, IsDstInit: true},
{Name: eventFSM1Internal, SrcState: []fsm.State{fsm1StateStage1}, DstState: fsm1StateStage2, IsInternal: true},
// Cancellation events
{Name: eventFSM1CancelByInternal, SrcState: []fsm.State{fsm1StateStage2}, DstState: fsm1StateCanceledByInternal, IsInternal: true},
{Name: eventFSM1Cancel, SrcState: []fsm.State{fsm1StateStage2}, DstState: fsm1StateCanceled2},
// Out
{Name: eventFSM1Process, SrcState: []fsm.State{fsm1StateStage2}, DstState: fsm1StateOutToFSM2},
{Name: eventFSM1InternalOut2, SrcState: []fsm.State{fsm1StateStage2}, DstState: fsm1StateOutToFSM2, IsInternal: true},
}
)
func NewFSM1() MachineProvider {
machine := &testMachineFSM1{}
machine.FSM = fsm.MustNewFSM(
fsm1Name,
fsm1StateInit,
testing1Events,
fsm.Callbacks{
eventFSM1Init: machine.actionFSM1SetUpData,
eventFSM1InternalOut2: machine.actionFSM1EmitOut2,
eventFSM1Process: machine.actionFSM1ProcessData,
},
)
return machine
}
func (m *testMachineFSM1) actionFSM1SetUpData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
m.data = testVal1
return m.DoInternal(eventFSM1Internal)
}
func (m *testMachineFSM1) actionFSM1ProcessData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
if len(args) == 1 {
if val, ok := args[0].(int); ok {
m.data -= val
}
}
return m.data, nil
}
func (m *testMachineFSM1) actionFSM1EmitOut2(event fsm.Event, args ...interface{}) (response interface{}, err error) {
return
}
// Second test machine
type testMachineFSM2 struct {
*fsm.FSM
data int
}
const (
fsm2Name = "fsm2"
// Init process from global idle state
fsm2StateInit = fsm1StateOutToFSM2
// Process data
fsm2StateStage1 = fsm.State("state_fsm2_stage1")
fsm2StateStage2 = fsm.State("state_fsm2_stage2")
// Cancelled with internal event
fsm2StateCanceledByInternal = fsm.State("state_fsm2_canceled")
// Out endpoint to switch
fsm2StateOutToFSM3 = fsm.State("state_fsm2_out_to_fsm3")
// Events
eventFSM2Init = fsm.Event("event_fsm2_init")
eventFSM2Process = fsm.Event("event_fsm2_process")
// Internal events
eventFSM2Internal = fsm.Event("event_internal_fsm2")
eventFSM2CancelByInternal = fsm.Event("event_internal_fsm2_cancel")
eventFSM2InternalOut = fsm.Event("event_internal_fsm2_out")
)
var (
testing2Events = []fsm.EventDesc{
// Init
{Name: eventFSM2Init, SrcState: []fsm.State{fsm2StateInit}, DstState: fsm2StateStage1},
{Name: eventFSM2Internal, SrcState: []fsm.State{fsm2StateStage1}, DstState: fsm2StateStage2, IsInternal: true},
// Cancellation events
{Name: eventFSM2CancelByInternal, SrcState: []fsm.State{fsm2StateStage2}, DstState: fsm2StateCanceledByInternal, IsInternal: true},
// Out
{Name: eventFSM2Process, SrcState: []fsm.State{fsm2StateStage2}, DstState: fsm.StateGlobalDone},
{Name: eventFSM2InternalOut, SrcState: []fsm.State{fsm2StateStage2}, DstState: fsm.StateGlobalDone, IsInternal: true},
}
testing2Callbacks = fsm.Callbacks{}
)
func NewFSM2() MachineProvider {
machine := &testMachineFSM1{}
machine.FSM = fsm.MustNewFSM(
fsm2Name,
fsm2StateInit,
testing2Events,
testing2Callbacks,
)
return machine
}
func (m *testMachineFSM2) actionFSM2SetUpData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
return
}
func (m *testMachineFSM2) actionFSM2ProcessData(event fsm.Event, args ...interface{}) (response interface{}, err error) {
return
}
func (m *testMachineFSM2) actionFSM2EmitOut2(event fsm.Event, args ...interface{}) (response interface{}, err error) {
return
}
var testPoolProvider *FSMPool
func init() {
testPoolProvider = Init(
NewFSM1(),
NewFSM2(),
)
}
func TestFSMPool_Init_EventsMap(t *testing.T) {
if len(testPoolProvider.events) == 0 {
t.Errorf("expected initialized events map")
}
}
func TestFSMPool_Init_StatesMap(t *testing.T) {
if len(testPoolProvider.states) == 0 {
t.Errorf("expected initialized states map")
}
}
func TestFSMPool_EntryPointMachine(t *testing.T) {
m, err := testPoolProvider.EntryPointMachine()
if err != nil || m.Name() != fsm1Name {
t.Errorf("expected entry point machine")
}
}
func TestFSMPool_MachineByState(t *testing.T) {
fsm1States := []fsm.State{
fsm1StateInit,
fsm1StateStage1,
fsm1StateStage2,
}
for _, state := range fsm1States {
machine, err := testPoolProvider.MachineByState(state)
if err != nil || machine.Name() != fsm1Name {
t.Errorf("expected machine fsm1 for state \"%s\"", state)
continue
}
}
fsm2States := []fsm.State{
fsm2StateInit,
fsm2StateStage1,
fsm2StateStage2,
}
for _, state := range fsm2States {
machine, err := testPoolProvider.MachineByState(state)
if err != nil || machine.Name() != fsm2Name {
t.Errorf("expected machine fsm2 for state \"%s\"", state)
continue
}
}
}
func TestFSMPool_MachineByEvent(t *testing.T) {
fsm1Events := []fsm.Event{
eventFSM1Init,
eventFSM1Cancel,
eventFSM1Process,
}
for _, event := range fsm1Events {
machine, err := testPoolProvider.MachineByEvent(event)
if err != nil || machine.Name() != fsm1Name {
t.Errorf("expected machine fsm1 for event \"%s\"", event)
continue
}
}
fsm2Events := []fsm.Event{
eventFSM2Init,
eventFSM2Process,
}
for _, event := range fsm2Events {
machine, err := testPoolProvider.MachineByEvent(event)
if err != nil || machine.Name() != fsm2Name {
t.Errorf("expected machine fsm2 for event \"%s\"", event)
continue
}
}
}
func TestFSMPool_WorkFlow(t *testing.T) {
machine, err := testPoolProvider.MachineByState(fsm1StateInit)
if err != nil || machine.Name() != fsm1Name {
t.Fatalf("expected machine fsm1 for state \"%s\"", fsm1StateInit)
}
if machine.State() != fsm1StateInit {
t.Fatalf("expected machine state \"%s\", got \"%s\"", fsm1StateInit, machine.State())
}
resp, err := machine.Do(eventFSM1Init)
if err != nil {
t.Fatalf("expected response without error, got \"%s\"", err)
}
if resp.State != fsm1StateStage2 {
t.Fatalf("expected machine state \"%s\", got \"%s\"", fsm1StateStage2, resp.State)
}
resp, err = machine.Do(eventFSM1Process, testVal2)
data, ok := resp.Data.(int)
if !ok {
t.Fatalf("expected response data int, got \"%s\"", resp.Data)
}
if data != (testVal1 - testVal2) {
t.Fatalf("expected response data value, got \"%d\"", data)
}
}

View File

@ -1 +0,0 @@
package dkg_commit_fsm

View File

@ -1 +0,0 @@
package dkg_deals_fsm

View File

@ -0,0 +1,54 @@
package dkg_proposal_fsm
import (
"github.com/depools/dc4bc/fsm/fsm"
"log"
)
// Pub keys
func (s *DKGProposalFSM) actionDKGPubKeysSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeysSent")
return
}
func (s *DKGProposalFSM) actionDKGPubKeyConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeyConfirmationReceived")
return
}
func (s *DKGProposalFSM) actionDKGPubKeyConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGPubKeyConfirmationError")
return
}
// Commits
func (s *DKGProposalFSM) actionDKGCommitsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitsSent")
return
}
func (s *DKGProposalFSM) actionDKGCommitConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitConfirmationReceived")
return
}
func (s *DKGProposalFSM) actionDKGCommitConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGCommitConfirmationError")
return
}
// Deals
func (s *DKGProposalFSM) actionDKGDealsSent(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealsSent")
return
}
func (s *DKGProposalFSM) actionDKGDealConfirmationReceived(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealConfirmationReceived")
return
}
func (s *DKGProposalFSM) actionDKGDealConfirmationError(event fsm.Event, args ...interface{}) (response interface{}, err error) {
log.Println("I'm actionDKGDealConfirmationError")
return
}

View File

@ -1 +1,133 @@
package dkg_proposal_fsm package dkg_proposal_fsm
import (
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/fsm_pool"
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
)
const (
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")
// Cancelled
StateDkgPubKeysSendingCancelled = fsm.State("state_dkg_pub_keys_sending_cancelled")
StateDkgPubKeysSendingCancelledByTimeout = fsm.State("state_dkg_pub_keys_sending_cancelled_by_timeout")
// Confirmed
StateDkgPubKeysSendingConfirmed = fsm.State("state_dkg_pub_keys_sending_confirmed")
// Sending dkg commits
StateDkgCommitsSendingRequired = fsm.State("state_dkg_commits_sending_required")
StateDkgCommitsSendingAwaitConfirmations = 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")
// Confirmed
StateDkgCommitsSendingConfirmed = fsm.State("state_dkg_commits_sending_confirmed")
// Sending dkg deals
StateDkgDealsSendingRequired = fsm.State("state_dkg_deals_sending_required")
StateDkgDealsSendingAwaitConfirmations = fsm.State("state_dkg_deals_sending_await_confirmations")
// Cancelled
StateDkgDealsSendingCancelled = fsm.State("state_dkg_deals_sending_cancelled")
StateDkgDealsSendingCancelledByTimeout = fsm.State("state_dkg_deals_sending_cancelled_by_timeout")
// Confirmed
StateDkgDealsSendingConfirmed = fsm.State("state_dkg_deals_sending_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")
EventDKGPubKeysConfirmedInternal = fsm.Event("event_dkg_pub_keys_confirmed_internal")
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")
EventDKGCommitsConfirmedInternal = fsm.Event("event_dkg_commits_confirmed_internal")
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")
EventDKGMasterKeyRequiredInternal = fsm.Event("event_dkg_master_key_required_internal")
)
type DKGProposalFSM struct {
*fsm.FSM
}
func New() fsm_pool.MachineProvider {
machine := &DKGProposalFSM{}
machine.FSM = fsm.MustNewFSM(
fsmName,
StateDkgInitial,
[]fsm.EventDesc{
// Init
// Switch to pub keys required
{Name: EventDKGPubKeysSendingRequiredInternal, SrcState: []fsm.State{StateDkgInitial}, DstState: StateDkgPubKeysSendingRequired, IsInternal: true},
// Pub keys sending
{Name: EventDKGPubKeysSent, SrcState: []fsm.State{StateDkgPubKeysSendingRequired}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
{Name: EventDKGPubKeyConfirmationReceived, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingAwaitConfirmations},
// Cancelled
{Name: EventDKGPubKeyConfirmationError, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelled},
{Name: EventDKGPubKeysConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingCancelledByTimeout, IsInternal: true},
// Confirmed
{Name: EventDKGPubKeysConfirmedInternal, SrcState: []fsm.State{StateDkgPubKeysSendingAwaitConfirmations}, DstState: StateDkgPubKeysSendingConfirmed, IsInternal: true},
// Switch to commits required
{Name: EventDKGCommitsSendingRequiredInternal, SrcState: []fsm.State{StateDkgPubKeysSendingConfirmed}, DstState: StateDkgCommitsSendingRequired, IsInternal: true},
// Commits
{Name: EventDKGCommitsSent, SrcState: []fsm.State{StateDkgCommitsSendingRequired}, DstState: StateDkgCommitsSendingAwaitConfirmations},
{Name: EventDKGCommitConfirmationReceived, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingAwaitConfirmations},
// Cancelled
{Name: EventDKGCommitConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelled},
{Name: EventDKGCommitsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingCancelledByTimeout, IsInternal: true},
// Confirmed
{Name: EventDKGCommitsConfirmedInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgCommitsSendingConfirmed, IsInternal: true},
// Switch to deals required
{Name: EventDKGDealsSendingRequiredInternal, SrcState: []fsm.State{StateDkgDealsSendingConfirmed}, DstState: StateDkgDealsSendingRequired, IsInternal: true},
// Deals
{Name: EventDKGDealsSent, SrcState: []fsm.State{StateDkgDealsSendingRequired}, DstState: StateDkgDealsSendingAwaitConfirmations},
{Name: EventDKGDealConfirmationReceived, SrcState: []fsm.State{StateDkgDealsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingAwaitConfirmations},
// Cancelled
{Name: EventDKGDealConfirmationError, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelled},
{Name: EventDKGDealsConfirmationCancelByTimeoutInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: StateDkgDealsSendingCancelledByTimeout, IsInternal: true},
// Done
{Name: EventDKGMasterKeyRequiredInternal, SrcState: []fsm.State{StateDkgCommitsSendingAwaitConfirmations}, DstState: fsm.StateGlobalDone, IsInternal: true},
},
fsm.Callbacks{
EventDKGPubKeysSent: machine.actionDKGPubKeysSent,
EventDKGPubKeyConfirmationReceived: machine.actionDKGPubKeyConfirmationReceived,
EventDKGPubKeyConfirmationError: machine.actionDKGPubKeyConfirmationError,
EventDKGCommitsSent: machine.actionDKGCommitsSent,
EventDKGCommitConfirmationReceived: machine.actionDKGCommitConfirmationReceived,
EventDKGCommitConfirmationError: machine.actionDKGCommitConfirmationError,
EventDKGDealsSent: machine.actionDKGDealsSent,
EventDKGDealConfirmationReceived: machine.actionDKGDealConfirmationReceived,
EventDKGDealConfirmationError: machine.actionDKGDealConfirmationError,
},
)
return machine
}

View File

@ -1,8 +1,8 @@
package internal package internal
type MachineStatePayload struct { type MachineStatePayload struct {
ProposalPayload ProposalConfirmationPrivateQuorum ConfirmationProposalPayload ConfirmationProposalPrivateQuorum
SigningPayload map[string]interface{} DKGProposalPayload DKGProposalPrivateQuorum
} }
// Using combine response for modify data with chain // Using combine response for modify data with chain

View File

@ -4,6 +4,7 @@ import "time"
type ProposalParticipantPrivate struct { type ProposalParticipantPrivate struct {
// Public title for address, such as name, nickname, organization // Public title for address, such as name, nickname, organization
ParticipantId int
Title string Title string
PublicKey []byte PublicKey []byte
// For validation user confirmation: sign(InvitationSecret, PublicKey) => user // For validation user confirmation: sign(InvitationSecret, PublicKey) => user
@ -14,4 +15,14 @@ type ProposalParticipantPrivate struct {
// Unique alias for map iteration - Public Key Fingerprint // Unique alias for map iteration - Public Key Fingerprint
// Excludes array merge and rotate operations // Excludes array merge and rotate operations
type ProposalConfirmationPrivateQuorum map[string]ProposalParticipantPrivate type ConfirmationProposalPrivateQuorum map[string]ProposalParticipantPrivate
type ProposalDKGParticipantPrivate struct {
Title string
PublicKey []byte
Commit []byte
Deal []byte
UpdatedAt *time.Time
}
type DKGProposalPrivateQuorum map[int]ProposalParticipantPrivate

View File

@ -3,11 +3,11 @@ package state_machines
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
"github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/fsm_pool" "github.com/depools/dc4bc/fsm/fsm_pool"
"github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/state_machines/internal"
"github.com/depools/dc4bc/fsm/state_machines/signature_construct_fsm"
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm" "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
) )
@ -30,7 +30,7 @@ var (
func init() { func init() {
fsmPoolProvider = fsm_pool.Init( fsmPoolProvider = fsm_pool.Init(
signature_proposal_fsm.New(), signature_proposal_fsm.New(),
signature_construct_fsm.New(), dkg_proposal_fsm.New(),
) )
} }

View File

@ -1,39 +0,0 @@
package signature_construct_fsm
import (
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/fsm_pool"
)
const (
fsmName = "signature_construct_fsm"
stateConstructorEntryPoint = "process_sig"
awaitConstructor = "validate_process_sig" // waiting participants
eventInitSignatureConstructor = "process_sig_init"
eventInitSignatureFinishTmp = "process_sig_fin"
)
type SignatureConstructFSM struct {
*fsm.FSM
}
func New() fsm_pool.MachineProvider {
machine := &SignatureConstructFSM{}
machine.FSM = fsm.MustNewFSM(
fsmName,
stateConstructorEntryPoint,
[]fsm.EventDesc{
// {Name: "", SrcState: []string{""}, DstState: ""},
// Init
{Name: eventInitSignatureConstructor, SrcState: []fsm.State{stateConstructorEntryPoint}, DstState: awaitConstructor},
{Name: eventInitSignatureFinishTmp, SrcState: []fsm.State{awaitConstructor}, DstState: "dkg_proposal_fsm"},
},
fsm.Callbacks{},
)
return machine
}

View File

@ -45,7 +45,7 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
return return
} }
request, ok := args[2].(requests.ProposalParticipantsListRequest) request, ok := args[2].(requests.SignatureProposalParticipantsListRequest)
if !ok { if !ok {
err = errors.New("cannot cast participants list") err = errors.New("cannot cast participants list")
@ -56,15 +56,16 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
return return
} }
payload.ProposalPayload = make(internal.ProposalConfirmationPrivateQuorum) payload.ConfirmationProposalPayload = make(internal.ConfirmationProposalPrivateQuorum)
for _, participant := range request { for participantIntId, participant := range request {
participantId := createFingerprint(&participant.PublicKey) participantId := createFingerprint(&participant.PublicKey)
secret, err := generateRandomString(32) secret, err := generateRandomString(32)
if err != nil { if err != nil {
return nil, errors.New("cannot generateRandomString") return nil, errors.New("cannot generateRandomString")
} }
payload.ProposalPayload[participantId] = internal.ProposalParticipantPrivate{ payload.ConfirmationProposalPayload[participantId] = internal.ProposalParticipantPrivate{
ParticipantId: participantIntId,
Title: participant.Title, Title: participant.Title,
PublicKey: participant.PublicKey, PublicKey: participant.PublicKey,
InvitationSecret: secret, InvitationSecret: secret,
@ -74,16 +75,16 @@ func (s *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
// Make response // Make response
responseData := make(responses.ProposalParticipantInvitationsResponse, 0) responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
for participantId, proposal := range payload.ProposalPayload { for pubKeyFingerprint, proposal := range payload.ConfirmationProposalPayload {
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret) encryptedInvitationSecret, err := encryptWithPubKey(proposal.PublicKey, proposal.InvitationSecret)
if err != nil { if err != nil {
return nil, errors.New("cannot encryptWithPubKey") return nil, errors.New("cannot encryptWithPubKey")
} }
responseEntry := &responses.ProposalParticipantInvitationEntryResponse{ responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
Title: proposal.Title, Title: proposal.Title,
PubKeyFingerprint: participantId, PubKeyFingerprint: pubKeyFingerprint,
EncryptedInvitation: encryptedInvitationSecret, EncryptedInvitation: encryptedInvitationSecret,
} }
responseData = append(responseData, responseEntry) responseData = append(responseData, responseEntry)

View File

@ -11,10 +11,10 @@ import (
// Request and response mutators // Request and response mutators
func ProposalParticipantsQuorumToResponse(list *internal.ProposalConfirmationPrivateQuorum) responses.ProposalParticipantInvitationsResponse { func ProposalParticipantsQuorumToResponse(list *internal.ConfirmationProposalPrivateQuorum) responses.SignatureProposalParticipantInvitationsResponse {
var response responses.ProposalParticipantInvitationsResponse var response responses.SignatureProposalParticipantInvitationsResponse
for quorumId, parcipant := range *list { for quorumId, parcipant := range *list {
response = append(response, &responses.ProposalParticipantInvitationEntryResponse{ response = append(response, &responses.SignatureProposalParticipantInvitationEntry{
Title: parcipant.Title, Title: parcipant.Title,
PubKeyFingerprint: quorumId, PubKeyFingerprint: quorumId,
// TODO: Add encryption // TODO: Add encryption

View File

@ -9,21 +9,23 @@ const (
fsmName = "signature_proposal_fsm" fsmName = "signature_proposal_fsm"
signingIdLen = 32 signingIdLen = 32
stateAwaitProposalConfirmation = fsm.State("validate_proposal") // waiting participants StateAwaitParticipantsConfirmations = fsm.State("state_validation_await_participants_confirmations") // waiting participants
stateValidationCanceledByParticipant = fsm.State("validation_canceled_by_participant") StateValidationCanceledByParticipant = fsm.State("state_validation_canceled_by_participant")
stateValidationCanceledByTimeout = fsm.State("validation_canceled_by_timeout") StateValidationCanceledByTimeout = fsm.State("state_validation_canceled_by_timeout")
stateProposed = "proposed" StateValidationCompleted = fsm.State("state_validation_completed")
eventInitProposal = fsm.Event("proposal_init") EventInitProposal = fsm.Event("event_proposal_init")
eventConfirmProposal = fsm.Event("proposal_confirm_by_participant") EventConfirmProposal = fsm.Event("event_proposal_confirm_by_participant")
eventDeclineProposal = fsm.Event("proposal_decline_by_participant") EventDeclineProposal = fsm.Event("event_proposal_decline_by_participant")
eventValidateProposal = fsm.Event("proposal_validate") EventValidateProposal = fsm.Event("event_proposal_validate")
eventSetProposalValidated = fsm.Event("proposal_set_validated") EventSetProposalValidated = fsm.Event("event_proposal_set_validated")
eventSetValidationCanceledByTimeout = fsm.Event("proposal_canceled_timeout") eventSetValidationCanceledByTimeout = fsm.Event("proposal_canceled_timeout")
eventSwitchProposedToSigning = fsm.Event("switch_state_to_signing")
// Switch to next fsm
) )
type SignatureProposalFSM struct { type SignatureProposalFSM struct {
@ -40,28 +42,28 @@ func New() fsm_pool.MachineProvider {
// {Name: "", SrcState: []string{""}, DstState: ""}, // {Name: "", SrcState: []string{""}, DstState: ""},
// Init // Init
{Name: eventInitProposal, SrcState: []fsm.State{fsm.StateGlobalIdle}, DstState: stateAwaitProposalConfirmation}, {Name: EventInitProposal, SrcState: []fsm.State{fsm.StateGlobalIdle}, DstState: StateAwaitParticipantsConfirmations},
// Validate by participants // Validate by participants
{Name: eventConfirmProposal, SrcState: []fsm.State{stateAwaitProposalConfirmation}, DstState: stateAwaitProposalConfirmation}, {Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
// Is decline event should auto change state to default, or it process will initiated by client (external emit)? // Is decline event should auto change state to default, or it process will initiated by client (external emit)?
// Now set for external emitting. // Now set for external emitting.
{Name: eventDeclineProposal, SrcState: []fsm.State{stateAwaitProposalConfirmation}, DstState: stateValidationCanceledByParticipant}, {Name: EventDeclineProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByParticipant},
{Name: eventValidateProposal, SrcState: []fsm.State{stateAwaitProposalConfirmation}, DstState: stateAwaitProposalConfirmation}, {Name: EventValidateProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
// eventProposalValidate internal or from client? // eventProposalValidate internal or from client?
// yay // yay
// Exit point // Exit point
{Name: eventSetProposalValidated, SrcState: []fsm.State{stateAwaitProposalConfirmation}, DstState: "process_sig", IsInternal: true}, {Name: EventSetProposalValidated, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: fsm.State("state_dkg_pub_keys_sending_required"), IsInternal: true},
// nan // nan
{Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{stateAwaitProposalConfirmation}, DstState: stateValidationCanceledByTimeout, IsInternal: true}, {Name: eventSetValidationCanceledByTimeout, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateValidationCanceledByTimeout, IsInternal: true},
}, },
fsm.Callbacks{ fsm.Callbacks{
eventInitProposal: machine.actionInitProposal, EventInitProposal: machine.actionInitProposal,
eventConfirmProposal: machine.actionConfirmProposalByParticipant, EventConfirmProposal: machine.actionConfirmProposalByParticipant,
eventDeclineProposal: machine.actionDeclineProposalByParticipant, EventDeclineProposal: machine.actionDeclineProposalByParticipant,
eventValidateProposal: machine.actionValidateProposal, EventValidateProposal: machine.actionValidateProposal,
}, },
) )
return machine return machine

View File

@ -0,0 +1,16 @@
package requests
// Event: "event_dkg_pub_keys_sent"
type DKGProposalPubKeysSendingRequest struct {
PubKeys map[int][]byte
}
// Event: "event_dkg_commits_sent"
type DKGProposalCommitsSendingRequest struct {
Commits map[int][]byte
}
// Event: "event_dkg_deals_sent"
type DKGProposalDealsSendingRequest struct {
Deals map[int][]byte
}

View File

@ -0,0 +1 @@
package requests

View File

@ -1,49 +1,17 @@
package requests package requests
import (
"errors"
"github.com/depools/dc4bc/fsm/config"
)
// Requests // Requests
type ProposalParticipantsListRequest []ProposalParticipantsEntryRequest type SignatureProposalParticipantsListRequest []SignatureProposalParticipantsEntry
type ProposalParticipantsEntryRequest struct { type SignatureProposalParticipantsEntry struct {
// Public title for address, such as name, nickname, organization // Public title for address, such as name, nickname, organization
Title string Title string
PublicKey []byte PublicKey []byte
} }
func (r *ProposalParticipantsListRequest) Validate() error { type SignatureProposalParticipantRequest struct {
if len(*r) < config.ParticipantsMinCount { // Key for link invitations to participants
return errors.New("too few participants") PubKeyFingerprint string
}
for _, participant := range *r {
if len(participant.Title) < 3 {
return errors.New("title too short")
}
if len(participant.Title) > 150 {
return errors.New("title too long")
}
if len(participant.PublicKey) < 10 {
return errors.New("pub key too short")
}
}
return nil
}
type ProposalParticipantConfirmationRequest struct {
// Public title for address, such as name, nickname, organization
ParticipantId string
EncryptedInvitation string EncryptedInvitation string
} }
func (r *ProposalParticipantConfirmationRequest) Validate() error {
return nil
}

View File

@ -0,0 +1,32 @@
package requests
import (
"errors"
"github.com/depools/dc4bc/fsm/config"
)
func (r *SignatureProposalParticipantsListRequest) Validate() error {
if len(*r) < config.ParticipantsMinCount {
return errors.New("too few participants")
}
for _, participant := range *r {
if len(participant.Title) < 3 {
return errors.New("title too short")
}
if len(participant.Title) > 150 {
return errors.New("title too long")
}
if len(participant.PublicKey) < 10 {
return errors.New("pub key too short")
}
}
return nil
}
func (r *SignatureProposalParticipantRequest) Validate() error {
return nil
}

View File

@ -0,0 +1,25 @@
package responses
type DKGProposalPubKeyParticipantResponse []*DKGProposalPubKeyParticipantEntry
type DKGProposalPubKeyParticipantEntry struct {
ParticipantId int
Title string
PubKey []byte
}
type DKGProposalCommitParticipantResponse []*DKGProposalCommitParticipantEntry
type DKGProposalCommitParticipantEntry struct {
ParticipantId int
Title string
Commit []byte
}
type DKGProposalDealParticipantResponse []*DKGProposalDealParticipantEntry
type DKGProposalDealParticipantEntry struct {
ParticipantId int
Title string
Deal []byte
}

View File

@ -2,9 +2,19 @@ package responses
// Responses // Responses
type ProposalParticipantInvitationsResponse []*ProposalParticipantInvitationEntryResponse const (
ProposalConfirmationStatusIdle = iota
ProposalConfirmationStatusAccepted
ProposalConfirmationStatusCanceled
ProposalConfirmationStatusTimeout
)
type ProposalParticipantInvitationEntryResponse struct { // States: "validate_proposal"
type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry
type SignatureProposalParticipantInvitationEntry struct {
ParticipantId int
// Public title for address, such as name, nickname, organization // Public title for address, such as name, nickname, organization
Title string Title string
// Key for link invitations to participants // Key for link invitations to participants
@ -12,3 +22,14 @@ type ProposalParticipantInvitationEntryResponse struct {
// Encrypted with public key secret // Encrypted with public key secret
EncryptedInvitation string EncryptedInvitation string
} }
// Public lists for proposal confirmation process
// States: "validation_canceled_by_participant", "validation_canceled_by_timeout",
type SignatureProposalParticipantStatusResponse []*SignatureProposalParticipantStatusEntry
type SignatureProposalParticipantStatusEntry struct {
ParticipantId int
Title string
PubKeyFingerprint string
Status int
}