mirror of https://github.com/certusone/dc4bc.git
Merge branch 'fsm-draft' into feat/airgapped-qr
This commit is contained in:
commit
0a42c75725
|
@ -138,7 +138,7 @@ func TestAirgappedAllSteps(t *testing.T) {
|
||||||
}
|
}
|
||||||
entry := &responses.SignatureProposalParticipantStatusEntry{
|
entry := &responses.SignatureProposalParticipantStatusEntry{
|
||||||
ParticipantId: n.ParticipantID,
|
ParticipantId: n.ParticipantID,
|
||||||
Title: n.Participant,
|
Addr: n.Participant,
|
||||||
DkgPubKey: pubKey,
|
DkgPubKey: pubKey,
|
||||||
}
|
}
|
||||||
getCommitsRequest = append(getCommitsRequest, entry)
|
getCommitsRequest = append(getCommitsRequest, entry)
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (am *AirgappedMachine) handleStateDkgCommitsAwaitConfirmations(o *client.Op
|
||||||
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
||||||
}
|
}
|
||||||
dkgInstance.StorePubKey(entry.Title, entry.ParticipantId, pubKey)
|
dkgInstance.StorePubKey(entry.Addr, entry.ParticipantId, pubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = dkgInstance.InitDKGInstance(); err != nil {
|
if err = dkgInstance.InitDKGInstance(); err != nil {
|
||||||
|
|
204
client/client.go
204
client/client.go
|
@ -2,20 +2,23 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
dkgFSM "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
|
||||||
"go.dedis.ch/kyber/v3"
|
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
fsmStateMachines "github.com/depools/dc4bc/fsm/state_machines"
|
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||||
|
|
||||||
|
"github.com/depools/dc4bc/fsm/state_machines"
|
||||||
|
|
||||||
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
|
dkgFSM "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||||
"github.com/depools/dc4bc/qr"
|
"github.com/depools/dc4bc/qr"
|
||||||
"github.com/depools/dc4bc/storage"
|
"github.com/depools/dc4bc/storage"
|
||||||
sign "go.dedis.ch/kyber/v3/sign/schnorr"
|
|
||||||
"go.dedis.ch/kyber/v3/util/key"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -25,29 +28,35 @@ const (
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
userName string
|
||||||
|
address string
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
fsm *fsmStateMachines.FSMInstance
|
|
||||||
state State
|
state State
|
||||||
storage storage.Storage
|
storage storage.Storage
|
||||||
|
keyStore KeyStore
|
||||||
qrProcessor qr.Processor
|
qrProcessor qr.Processor
|
||||||
|
|
||||||
// these just a template
|
|
||||||
suite key.Suite
|
|
||||||
authKeyPair *key.Pair
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(
|
func NewClient(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
fsm *fsmStateMachines.FSMInstance,
|
userName string,
|
||||||
state State,
|
state State,
|
||||||
storage storage.Storage,
|
storage storage.Storage,
|
||||||
|
keyStore KeyStore,
|
||||||
qrProcessor qr.Processor,
|
qrProcessor qr.Processor,
|
||||||
) (*Client, error) {
|
) (*Client, error) {
|
||||||
|
keyPair, err := keyStore.LoadKeys(userName, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to LoadKeys: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
fsm: fsm,
|
userName: userName,
|
||||||
|
address: keyPair.GetAddr(),
|
||||||
state: state,
|
state: state,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
|
keyStore: keyStore,
|
||||||
qrProcessor: qrProcessor,
|
qrProcessor: qrProcessor,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -76,48 +85,8 @@ func (c *Client) Poll() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
log.Println("Message:", message)
|
if err := c.ProcessMessage(message); err != nil {
|
||||||
|
log.Println("Failed to process message:", err)
|
||||||
fsmReq, err := FSMRequestFromBytes(message.Data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get FSMRequest from message data: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, fsmDump, err := c.fsm.Do(fsmReq.Event, fsmReq.Args...)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to Do operation in FSM: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation *Operation
|
|
||||||
|
|
||||||
switch resp.State {
|
|
||||||
// if the new state is waiting for RPC to airgapped machine
|
|
||||||
case dkgFSM.StateDkgCommitsAwaitConfirmations, dkgFSM.StateDkgDealsAwaitConfirmations,
|
|
||||||
dkgFSM.StateDkgResponsesAwaitConfirmations:
|
|
||||||
bz, err := json.Marshal(resp.Data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to marshal FSM response: %w", err)
|
|
||||||
}
|
|
||||||
operation = &Operation{
|
|
||||||
Type: OperationType(resp.State),
|
|
||||||
Payload: bz,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Printf("State %s does not require an operation", resp.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation != nil {
|
|
||||||
if err := c.state.PutOperation(operation); err != nil {
|
|
||||||
return fmt.Errorf("failed to PutOperation: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.state.SaveOffset(message.Offset); err != nil {
|
|
||||||
return fmt.Errorf("failed to SaveOffset: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.state.SaveFSM(fsmDump); err != nil {
|
|
||||||
return fmt.Errorf("failed to SaveFSM: %w", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-c.ctx.Done():
|
case <-c.ctx.Done():
|
||||||
|
@ -127,6 +96,65 @@ func (c *Client) Poll() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ProcessMessage(message storage.Message) error {
|
||||||
|
fsmInstance, err := c.getFSMInstance(message.DkgRoundID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to getFSMInstance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fsm.Event(message.Event) != signature_proposal_fsm.EventInitProposal {
|
||||||
|
if err := c.verifyMessage(fsmInstance, message); err != nil {
|
||||||
|
return fmt.Errorf("failed to verifyMessage %+v: %w", message, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fsmReq, err := FSMRequestFromMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get FSMRequestFromMessage: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, fsmDump, err := fsmInstance.Do(fsm.Event(message.Event), fsmReq)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to Do operation in FSM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var operation *Operation
|
||||||
|
switch resp.State {
|
||||||
|
// if the new state is waiting for RPC to airgapped machine
|
||||||
|
case
|
||||||
|
dkgFSM.StateDkgCommitsAwaitConfirmations,
|
||||||
|
dkgFSM.StateDkgDealsAwaitConfirmations,
|
||||||
|
dkgFSM.StateDkgResponsesAwaitConfirmations:
|
||||||
|
bz, err := json.Marshal(resp.Data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal FSM response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
operation = &Operation{
|
||||||
|
Type: OperationType(resp.State),
|
||||||
|
Payload: bz,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("State %s does not require an operation", resp.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation != nil {
|
||||||
|
if err := c.state.PutOperation(operation); err != nil {
|
||||||
|
return fmt.Errorf("failed to PutOperation: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.state.SaveOffset(message.Offset); err != nil {
|
||||||
|
return fmt.Errorf("failed to SaveOffset: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.state.SaveFSM(message.DkgRoundID, fsmDump); err != nil {
|
||||||
|
return fmt.Errorf("failed to SaveFSM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetOperations() (map[string]*Operation, error) {
|
func (c *Client) GetOperations() (map[string]*Operation, error) {
|
||||||
return c.state.GetOperations()
|
return c.state.GetOperations()
|
||||||
}
|
}
|
||||||
|
@ -188,16 +216,16 @@ func (c *Client) handleProcessedOperation(operation Operation) error {
|
||||||
return fmt.Errorf("processed operation does not match stored operation: %w", err)
|
return fmt.Errorf("processed operation does not match stored operation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, err := c.signMessage(operation.Result)
|
message := storage.Message{
|
||||||
|
Event: string(operation.Type),
|
||||||
|
Data: operation.Result,
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := c.signMessage(message.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to sign a message: %w", err)
|
return fmt.Errorf("failed to sign a message: %w", err)
|
||||||
}
|
}
|
||||||
message := storage.Message{
|
message.Signature = sig
|
||||||
To: operation.To,
|
|
||||||
Data: operation.Result,
|
|
||||||
Signature: sig,
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := c.storage.Send(message); err != nil {
|
if _, err := c.storage.Send(message); err != nil {
|
||||||
return fmt.Errorf("failed to post message: %w", err)
|
return fmt.Errorf("failed to post message: %w", err)
|
||||||
|
@ -210,21 +238,49 @@ func (c *Client) handleProcessedOperation(operation Operation) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's just a template
|
func (c *Client) getFSMInstance(dkgRoundID string) (*state_machines.FSMInstance, error) {
|
||||||
func (c *Client) signMessage(msg []byte) ([]byte, error) {
|
var err error
|
||||||
//s, err := sign.Sign(c.suite, c.authKeyPair.Private, msg)
|
fsmInstance, ok, err := c.state.LoadFSM(dkgRoundID)
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// return nil, fmt.Errorf("failed to sign a message: %w", err)
|
return nil, fmt.Errorf("failed to LoadFSM: %w", err)
|
||||||
//}
|
}
|
||||||
return nil, nil
|
|
||||||
|
if !ok {
|
||||||
|
fsmInstance, err = state_machines.Create(dkgRoundID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create FSM instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := fsmInstance.Dump()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to Dump FSM instance: %w", err)
|
||||||
|
}
|
||||||
|
if err := c.state.SaveFSM(dkgRoundID, bz); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to SaveFSM: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsmInstance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's just a template
|
func (c *Client) signMessage(message []byte) ([]byte, error) {
|
||||||
func (c *Client) verifyMessage(participant string, msg, signature []byte) error {
|
keyPair, err := c.keyStore.LoadKeys(c.userName, "")
|
||||||
return sign.Verify(c.suite, c.getPublicKeyOfParticipant(participant), msg, signature)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to LoadKeys: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ed25519.Sign(keyPair.Priv, message), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func should return public key of participant for checking his message signature
|
func (c *Client) verifyMessage(fsmInstance *state_machines.FSMInstance, message storage.Message) error {
|
||||||
func (c *Client) getPublicKeyOfParticipant(participant string) kyber.Point {
|
senderPubKey, err := fsmInstance.GetPubKeyByAddr(message.SenderAddr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to GetPubKeyByAddr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ed25519.Verify(senderPubKey, message.Bytes(), message.Signature) {
|
||||||
|
return errors.New("signature is corrupt")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package client_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,16 +10,98 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/depools/dc4bc/mocks/qrMocks"
|
|
||||||
|
|
||||||
"github.com/depools/dc4bc/client"
|
"github.com/depools/dc4bc/client"
|
||||||
|
"github.com/depools/dc4bc/fsm/state_machines"
|
||||||
|
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||||
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"github.com/depools/dc4bc/mocks/clientMocks"
|
"github.com/depools/dc4bc/mocks/clientMocks"
|
||||||
|
"github.com/depools/dc4bc/mocks/qrMocks"
|
||||||
"github.com/depools/dc4bc/mocks/storageMocks"
|
"github.com/depools/dc4bc/mocks/storageMocks"
|
||||||
|
"github.com/depools/dc4bc/storage"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestClient_ProcessMessage(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
req = require.New(t)
|
||||||
|
ctrl = gomock.NewController(t)
|
||||||
|
)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
dkgRoundID := "dkg_round_id"
|
||||||
|
state := clientMocks.NewMockState(ctrl)
|
||||||
|
keyStore := clientMocks.NewMockKeyStore(ctrl)
|
||||||
|
stg := storageMocks.NewMockStorage(ctrl)
|
||||||
|
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
||||||
|
|
||||||
|
testClientKeyPair := client.NewKeyPair()
|
||||||
|
keyStore.EXPECT().LoadKeys("test_client", "").Times(1).Return(testClientKeyPair, nil)
|
||||||
|
|
||||||
|
fsm, err := state_machines.Create(dkgRoundID)
|
||||||
|
state.EXPECT().LoadFSM(dkgRoundID).Times(1).Return(fsm, true, nil)
|
||||||
|
|
||||||
|
clt, err := client.NewClient(
|
||||||
|
ctx,
|
||||||
|
"test_client",
|
||||||
|
state,
|
||||||
|
stg,
|
||||||
|
keyStore,
|
||||||
|
qrProcessor,
|
||||||
|
)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
t.Run("test_process_init_dkg", func(t *testing.T) {
|
||||||
|
senderUserName := "sender_username"
|
||||||
|
senderKeyPair := client.NewKeyPair()
|
||||||
|
messageData := requests.SignatureProposalParticipantsListRequest{
|
||||||
|
Participants: []*requests.SignatureProposalParticipantsEntry{
|
||||||
|
{
|
||||||
|
Addr: senderUserName,
|
||||||
|
PubKey: senderKeyPair.Pub,
|
||||||
|
DkgPubKey: make([]byte, 128),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: "111",
|
||||||
|
PubKey: client.NewKeyPair().Pub,
|
||||||
|
DkgPubKey: make([]byte, 128),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: "222",
|
||||||
|
PubKey: client.NewKeyPair().Pub,
|
||||||
|
DkgPubKey: make([]byte, 128),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: "333",
|
||||||
|
PubKey: client.NewKeyPair().Pub,
|
||||||
|
DkgPubKey: make([]byte, 128),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
messageDataBz, err := json.Marshal(messageData)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
message := storage.Message{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
DkgRoundID: dkgRoundID,
|
||||||
|
Offset: 1,
|
||||||
|
Event: string(spf.EventInitProposal),
|
||||||
|
Data: messageDataBz,
|
||||||
|
SenderAddr: senderUserName,
|
||||||
|
}
|
||||||
|
message.Signature = ed25519.Sign(senderKeyPair.Priv, message.Bytes())
|
||||||
|
|
||||||
|
state.EXPECT().SaveOffset(uint64(1)).Times(1).Return(nil)
|
||||||
|
state.EXPECT().SaveFSM(gomock.Any(), gomock.Any()).Times(1).Return(nil)
|
||||||
|
|
||||||
|
err = clt.ProcessMessage(message)
|
||||||
|
req.NoError(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestClient_GetOperationsList(t *testing.T) {
|
func TestClient_GetOperationsList(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@ -28,10 +111,18 @@ func TestClient_GetOperationsList(t *testing.T) {
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
state := clientMocks.NewMockState(ctrl)
|
state := clientMocks.NewMockState(ctrl)
|
||||||
storage := storageMocks.NewMockStorage(ctrl)
|
keyStore := clientMocks.NewMockKeyStore(ctrl)
|
||||||
|
stg := storageMocks.NewMockStorage(ctrl)
|
||||||
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
||||||
|
|
||||||
clt, err := client.NewClient(ctx, nil, state, storage, qrProcessor)
|
clt, err := client.NewClient(
|
||||||
|
ctx,
|
||||||
|
"test_client",
|
||||||
|
state,
|
||||||
|
stg,
|
||||||
|
keyStore,
|
||||||
|
qrProcessor,
|
||||||
|
)
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
|
|
||||||
state.EXPECT().GetOperations().Times(1).Return(map[string]*client.Operation{}, nil)
|
state.EXPECT().GetOperations().Times(1).Return(map[string]*client.Operation{}, nil)
|
||||||
|
@ -62,10 +153,18 @@ func TestClient_GetOperationQRPath(t *testing.T) {
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
state := clientMocks.NewMockState(ctrl)
|
state := clientMocks.NewMockState(ctrl)
|
||||||
storage := storageMocks.NewMockStorage(ctrl)
|
keyStore := clientMocks.NewMockKeyStore(ctrl)
|
||||||
|
stg := storageMocks.NewMockStorage(ctrl)
|
||||||
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
||||||
|
|
||||||
clt, err := client.NewClient(ctx, nil, state, storage, qrProcessor)
|
clt, err := client.NewClient(
|
||||||
|
ctx,
|
||||||
|
"test_client",
|
||||||
|
state,
|
||||||
|
stg,
|
||||||
|
keyStore,
|
||||||
|
qrProcessor,
|
||||||
|
)
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
|
|
||||||
operation := &client.Operation{
|
operation := &client.Operation{
|
||||||
|
@ -100,10 +199,18 @@ func TestClient_ReadProcessedOperation(t *testing.T) {
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
state := clientMocks.NewMockState(ctrl)
|
state := clientMocks.NewMockState(ctrl)
|
||||||
storage := storageMocks.NewMockStorage(ctrl)
|
keyStore := clientMocks.NewMockKeyStore(ctrl)
|
||||||
|
stg := storageMocks.NewMockStorage(ctrl)
|
||||||
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
||||||
|
|
||||||
clt, err := client.NewClient(ctx, nil, state, storage, qrProcessor)
|
clt, err := client.NewClient(
|
||||||
|
ctx,
|
||||||
|
"test_client",
|
||||||
|
state,
|
||||||
|
stg,
|
||||||
|
keyStore,
|
||||||
|
qrProcessor,
|
||||||
|
)
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
|
|
||||||
operation := &client.Operation{
|
operation := &client.Operation{
|
||||||
|
@ -126,7 +233,11 @@ func TestClient_ReadProcessedOperation(t *testing.T) {
|
||||||
qrProcessor.EXPECT().ReadQR().Return(processedOperationBz, nil).Times(1)
|
qrProcessor.EXPECT().ReadQR().Return(processedOperationBz, nil).Times(1)
|
||||||
state.EXPECT().GetOperationByID(processedOperation.ID).Times(1).Return(operation, nil)
|
state.EXPECT().GetOperationByID(processedOperation.ID).Times(1).Return(operation, nil)
|
||||||
state.EXPECT().DeleteOperation(processedOperation.ID).Times(1)
|
state.EXPECT().DeleteOperation(processedOperation.ID).Times(1)
|
||||||
storage.EXPECT().Send(gomock.Any()).Times(1)
|
stg.EXPECT().Send(gomock.Any()).Times(1)
|
||||||
|
|
||||||
|
keyPair := client.NewKeyPair()
|
||||||
|
keyStore.EXPECT().LoadKeys("test_client", "").Times(1).Return(keyPair, nil)
|
||||||
|
|
||||||
err = clt.ReadProcessedOperation()
|
err = clt.ReadProcessedOperation()
|
||||||
req.NoError(err)
|
req.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
secretsKey = "secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyStore interface {
|
||||||
|
LoadKeys(userName, password string) (*KeyPair, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LevelDBKeyStore is a temporary solution for keeping hot node keys.
|
||||||
|
// The target state is an encrypted storage with password authentication.
|
||||||
|
type LevelDBKeyStore struct {
|
||||||
|
keystoreDb *leveldb.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLevelDBKeyStore(username, keystorePath string) (KeyStore, error) {
|
||||||
|
db, err := leveldb.OpenFile(keystorePath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open keystore: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keystore := &LevelDBKeyStore{
|
||||||
|
keystoreDb: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := keystore.keystoreDb.Get([]byte(secretsKey), nil); err != nil {
|
||||||
|
if err := keystore.initJsonKey(secretsKey, map[string]*KeyPair{
|
||||||
|
username: NewKeyPair(),
|
||||||
|
}); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to init %s storage: %w", operationsKey, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keystore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LevelDBKeyStore) LoadKeys(userName, password string) (*KeyPair, error) {
|
||||||
|
bz, err := s.keystoreDb.Get([]byte(secretsKey), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read keystore: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyPairs = map[string]*KeyPair{}
|
||||||
|
if err := json.Unmarshal(bz, &keyPairs); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshak key pairs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPair, ok := keyPairs[userName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("no key pair found for user %s", userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyPair, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LevelDBKeyStore) initJsonKey(key string, data interface{}) error {
|
||||||
|
if _, err := s.keystoreDb.Get([]byte(key), nil); err != nil {
|
||||||
|
operationsBz, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal storage structure: %w", err)
|
||||||
|
}
|
||||||
|
err = s.keystoreDb.Put([]byte(key), operationsBz, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to init state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyPair struct {
|
||||||
|
Pub ed25519.PublicKey
|
||||||
|
Priv ed25519.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyPair() *KeyPair {
|
||||||
|
// TODO: implement proper generation.
|
||||||
|
pub, priv, _ := ed25519.GenerateKey(nil)
|
||||||
|
return &KeyPair{
|
||||||
|
Pub: pub,
|
||||||
|
Priv: priv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *KeyPair) GetAddr() string {
|
||||||
|
return hex.EncodeToString(p.Pub)
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/depools/dc4bc/fsm/state_machines"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,8 +22,8 @@ type State interface {
|
||||||
SaveOffset(uint64) error
|
SaveOffset(uint64) error
|
||||||
LoadOffset() (uint64, error)
|
LoadOffset() (uint64, error)
|
||||||
|
|
||||||
SaveFSM([]byte) error
|
SaveFSM(dkgRoundID string, dump []byte) error
|
||||||
LoadFSM() ([]byte, error)
|
LoadFSM(dkgRoundID string) (*state_machines.FSMInstance, bool, error)
|
||||||
|
|
||||||
PutOperation(operation *Operation) error
|
PutOperation(operation *Operation) error
|
||||||
DeleteOperation(operationID string) error
|
DeleteOperation(operationID string) error
|
||||||
|
@ -45,15 +47,25 @@ func NewLevelDBState(stateDbPath string) (State, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init state key for operations JSON.
|
// Init state key for operations JSON.
|
||||||
if err := state.initJsonKey(operationsKey, map[string]*Operation{}); err != nil {
|
if _, err := state.stateDb.Get([]byte(operationsKey), nil); err != nil {
|
||||||
return nil, fmt.Errorf("failed to init %s storage: %w", operationsKey, err)
|
if err := state.initJsonKey(operationsKey, map[string]*Operation{}); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to init %s storage: %w", operationsKey, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init state key for offset bytes.
|
// Init state key for offset bytes.
|
||||||
bz := make([]byte, 8)
|
if _, err := state.stateDb.Get([]byte(offsetKey), nil); err != nil {
|
||||||
binary.LittleEndian.PutUint64(bz, 0)
|
bz := make([]byte, 8)
|
||||||
if err := db.Put([]byte(offsetKey), bz, nil); err != nil {
|
binary.LittleEndian.PutUint64(bz, 0)
|
||||||
return nil, fmt.Errorf("failed to init %s storage: %w", offsetKey, err)
|
if err := db.Put([]byte(offsetKey), bz, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to init %s storage: %w", offsetKey, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := state.stateDb.Get([]byte(fsmStateKey), nil); err != nil {
|
||||||
|
if err := db.Put([]byte(fsmStateKey), []byte{}, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to init %s storage: %w", offsetKey, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state, nil
|
return state, nil
|
||||||
|
@ -95,17 +107,48 @@ func (s *LevelDBState) LoadOffset() (uint64, error) {
|
||||||
return offset, nil
|
return offset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement.
|
func (s *LevelDBState) SaveFSM(dkgRoundID string, dump []byte) error {
|
||||||
func (s *LevelDBState) SaveFSM(fsmState []byte) error {
|
bz, err := s.stateDb.Get([]byte(fsmStateKey), nil)
|
||||||
if err := s.stateDb.Put([]byte(fsmStateKey), fsmState, nil); err != nil {
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get FSM instances: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fsmInstances = map[string][]byte{}
|
||||||
|
if err := json.Unmarshal(bz, &fsmInstances); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal FSM instances: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsmInstances[dkgRoundID] = dump
|
||||||
|
|
||||||
|
if err := s.stateDb.Put([]byte(fsmStateKey), dump, nil); err != nil {
|
||||||
return fmt.Errorf("failed to save fsm state: %w", err)
|
return fmt.Errorf("failed to save fsm state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement.
|
func (s *LevelDBState) LoadFSM(dkgRoundID string) (*state_machines.FSMInstance, bool, error) {
|
||||||
func (s *LevelDBState) LoadFSM() ([]byte, error) {
|
bz, err := s.stateDb.Get([]byte(fsmStateKey), nil)
|
||||||
return s.stateDb.Get([]byte(fsmStateKey), nil)
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to get FSM instances: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fsmInstances = map[string][]byte{}
|
||||||
|
if err := json.Unmarshal(bz, &fsmInstances); err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to unmarshal FSM instances: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsmInstanceBz, ok := fsmInstances[dkgRoundID]
|
||||||
|
if !ok {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fsmInstance, err := state_machines.FromDump(fsmInstanceBz)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, fmt.Errorf("failed to restore FSM instance from dump: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsmInstance, ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LevelDBState) PutOperation(operation *Operation) error {
|
func (s *LevelDBState) PutOperation(operation *Operation) error {
|
||||||
|
|
|
@ -4,8 +4,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/depools/dc4bc/fsm/fsm"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
|
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||||
|
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||||
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
|
"github.com/depools/dc4bc/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperationType string
|
type OperationType string
|
||||||
|
@ -41,26 +46,42 @@ func (o *Operation) Check(o2 *Operation) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FSMRequest struct {
|
func FSMRequestFromMessage(message storage.Message) (interface{}, error) {
|
||||||
Event fsm.Event
|
var resolvedValue interface{}
|
||||||
Args []interface{}
|
switch fsm.Event(message.Event) {
|
||||||
}
|
case signature_proposal_fsm.EventInitProposal:
|
||||||
|
var req requests.SignatureProposalParticipantsListRequest
|
||||||
func FSMRequestFromBytes(data []byte) (FSMRequest, error) {
|
if err := json.Unmarshal(message.Data, &req); err != nil {
|
||||||
var (
|
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
|
||||||
r FSMRequest
|
}
|
||||||
err error
|
resolvedValue = req
|
||||||
)
|
case dkg_proposal_fsm.EventDKGCommitConfirmationReceived:
|
||||||
if err = json.Unmarshal(data, &r); err != nil {
|
var req requests.DKGProposalCommitConfirmationRequest
|
||||||
return r, err
|
if err := json.Unmarshal(message.Data, &req); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
|
||||||
|
}
|
||||||
|
resolvedValue = req
|
||||||
|
case dkg_proposal_fsm.EventDKGDealConfirmationReceived:
|
||||||
|
var req requests.DKGProposalDealConfirmationRequest
|
||||||
|
if err := json.Unmarshal(message.Data, &req); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
|
||||||
|
}
|
||||||
|
resolvedValue = req
|
||||||
|
case dkg_proposal_fsm.EventDKGResponseConfirmationReceived:
|
||||||
|
var req requests.DKGProposalResponseConfirmationRequest
|
||||||
|
if err := json.Unmarshal(message.Data, &req); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
|
||||||
|
}
|
||||||
|
resolvedValue = req
|
||||||
|
case dkg_proposal_fsm.EventDKGMasterKeyConfirmationReceived:
|
||||||
|
var req requests.DKGProposalMasterKeyConfirmationRequest
|
||||||
|
if err := json.Unmarshal(message.Data, &req); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
|
||||||
|
}
|
||||||
|
resolvedValue = req
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid event: %s", message.Event)
|
||||||
}
|
}
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func FSMRequestToBytes(event fsm.Event, req interface{}) ([]byte, error) {
|
return resolvedValue, nil
|
||||||
fsmReq := FSMRequest{
|
|
||||||
Event: event,
|
|
||||||
Args: []interface{}{req},
|
|
||||||
}
|
|
||||||
return json.Marshal(fsmReq)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
flagUserName = "username"
|
||||||
flagListenAddr = "listen_addr"
|
flagListenAddr = "listen_addr"
|
||||||
flagStateDBDSN = "state_dbdsn"
|
flagStateDBDSN = "state_dbdsn"
|
||||||
flagStorageDBDSN = "storage_dbdsn"
|
flagStorageDBDSN = "storage_dbdsn"
|
||||||
|
flagStoreDBDSN = "key_store_dbdsn"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().String(flagListenAddr, "localhost:8080", "Listen address")
|
rootCmd.PersistentFlags().String(flagUserName, "testUser", "Username")
|
||||||
|
rootCmd.PersistentFlags().String(flagListenAddr, "localhost:8080", "Listen Address")
|
||||||
rootCmd.PersistentFlags().String(flagStateDBDSN, "./dc4bc_client_state", "State DBDSN")
|
rootCmd.PersistentFlags().String(flagStateDBDSN, "./dc4bc_client_state", "State DBDSN")
|
||||||
rootCmd.PersistentFlags().String(flagStorageDBDSN, "./dc4bc_file_storage", "Storage DBDSN")
|
rootCmd.PersistentFlags().String(flagStorageDBDSN, "./dc4bc_file_storage", "Storage DBDSN")
|
||||||
|
rootCmd.PersistentFlags().String(flagStoreDBDSN, "./dc4bc_jey_store", "Key Store DBDSN")
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "dc4bc_client",
|
Use: "dc4bc_client",
|
||||||
Short: "dc4bc client implementation",
|
Short: "dc4bc client implementation",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
userName, err := cmd.PersistentFlags().GetString(flagUserName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to read configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
listenAddr, err := cmd.PersistentFlags().GetString(flagListenAddr)
|
listenAddr, err := cmd.PersistentFlags().GetString(flagListenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to read configuration: %v", err)
|
log.Fatalf("failed to read configuration: %v", err)
|
||||||
|
@ -45,6 +54,11 @@ var rootCmd = &cobra.Command{
|
||||||
log.Fatalf("failed to read configuration: %v", err)
|
log.Fatalf("failed to read configuration: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyStoreDBDSN, err := cmd.PersistentFlags().GetString(flagStoreDBDSN)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to read configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
@ -58,11 +72,16 @@ var rootCmd = &cobra.Command{
|
||||||
log.Fatalf("Failed to init storage client: %v", err)
|
log.Fatalf("Failed to init storage client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyStore, err := client.NewLevelDBKeyStore(userName, keyStoreDBDSN)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to init key store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
processor := qr.NewCameraProcessor()
|
processor := qr.NewCameraProcessor()
|
||||||
|
|
||||||
// TODO: create state machine.
|
// TODO: create state machine.
|
||||||
|
|
||||||
cli, err := client.NewClient(ctx, nil, state, stg, processor)
|
cli, err := client.NewClient(ctx, userName, nil, state, stg, keyStore, processor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to init client: %v", err)
|
log.Fatalf("Failed to init client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,27 +8,43 @@ import (
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionInitDKGProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionInitDKGProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
|
m.payloadMu.Lock()
|
||||||
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
if m.payload.DKGProposalPayload != nil {
|
if m.payload.DKGProposalPayload != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m.payload.DKGProposalPayload = &internal.DKGConfirmation{
|
if len(args) != 1 {
|
||||||
Quorum: make(internal.DKGProposalQuorum),
|
err = errors.New("{arg0} required {DefaultRequest}")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
request, ok := args[0].(requests.DefaultRequest)
|
||||||
m.payload.DKGProposalPayload.Quorum[participant.ParticipantId] = &internal.DKGProposalParticipant{
|
|
||||||
Title: participant.Title,
|
if !ok {
|
||||||
|
err = errors.New("cannot cast {arg0} to type {DefaultRequest}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.payload.DKGProposalPayload = &internal.DKGConfirmation{
|
||||||
|
Quorum: make(internal.DKGProposalQuorum),
|
||||||
|
CreatedAt: request.CreatedAt,
|
||||||
|
ExpiresAt: request.CreatedAt.Add(config.DkgConfirmationDeadline),
|
||||||
|
}
|
||||||
|
|
||||||
|
for participantId, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
|
m.payload.DKGProposalPayload.Quorum[participantId] = &internal.DKGProposalParticipant{
|
||||||
|
Addr: participant.Addr,
|
||||||
Status: internal.CommitAwaitConfirmation,
|
Status: internal.CommitAwaitConfirmation,
|
||||||
UpdatedAt: participant.UpdatedAt,
|
UpdatedAt: participant.UpdatedAt,
|
||||||
}
|
}
|
||||||
copy(m.payload.DKGProposalPayload.Quorum[participant.ParticipantId].PubKey, participant.DkgPubKey)
|
copy(m.payload.DKGProposalPayload.Quorum[participantId].DkgPubKey, participant.DkgPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove m.payload.SignatureProposalPayload?
|
// Remove m.payload.SignatureProposalPayload?
|
||||||
|
@ -71,9 +87,11 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Commit, request.Commit)
|
copy(dkgProposalParticipant.Commit, request.Commit)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
|
||||||
dkgProposalParticipant.Status = internal.CommitConfirmed
|
dkgProposalParticipant.Status = internal.CommitConfirmed
|
||||||
|
|
||||||
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
m.payload.SignatureProposalPayload.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -81,26 +99,23 @@ func (m *DKGProposalFSM) actionCommitConfirmationReceived(inEvent fsm.Event, arg
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
isContainsError, isContainsExpired bool
|
isContainsError bool
|
||||||
)
|
)
|
||||||
|
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
tm := time.Now()
|
if m.payload.DKGProposalPayload.IsExpired() {
|
||||||
|
outEvent = eventDKGCommitsConfirmationCancelByErrorInternal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||||
if participant.Status == internal.CommitAwaitConfirmation {
|
if participant.Status == internal.CommitConfirmationError {
|
||||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
isContainsError = true
|
||||||
isContainsExpired = true
|
} else if participant.Status == internal.CommitConfirmed {
|
||||||
}
|
unconfirmedParticipants--
|
||||||
} else {
|
|
||||||
if participant.Status == internal.CommitConfirmationError {
|
|
||||||
isContainsError = true
|
|
||||||
} else if participant.Status == internal.CommitConfirmed {
|
|
||||||
unconfirmedParticipants--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,11 +124,6 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitCommits(inEvent fsm.Event
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isContainsExpired {
|
|
||||||
outEvent = eventDKGCommitsConfirmationCancelByErrorInternal
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The are no declined and timed out participants, check for all confirmations
|
// The are no declined and timed out participants, check for all confirmations
|
||||||
if unconfirmedParticipants > 0 {
|
if unconfirmedParticipants > 0 {
|
||||||
return
|
return
|
||||||
|
@ -163,9 +173,11 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Deal, request.Deal)
|
copy(dkgProposalParticipant.Deal, request.Deal)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
|
||||||
dkgProposalParticipant.Status = internal.DealConfirmed
|
dkgProposalParticipant.Status = internal.DealConfirmed
|
||||||
|
|
||||||
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
m.payload.SignatureProposalPayload.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -173,26 +185,23 @@ func (m *DKGProposalFSM) actionDealConfirmationReceived(inEvent fsm.Event, args
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
isContainsError, isContainsExpired bool
|
isContainsError bool
|
||||||
)
|
)
|
||||||
|
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
tm := time.Now()
|
if m.payload.DKGProposalPayload.IsExpired() {
|
||||||
|
outEvent = eventDKGDealsConfirmationCancelByTimeoutInternal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||||
if participant.Status == internal.DealAwaitConfirmation {
|
if participant.Status == internal.DealConfirmationError {
|
||||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
isContainsError = true
|
||||||
isContainsExpired = true
|
} else if participant.Status == internal.DealConfirmed {
|
||||||
}
|
unconfirmedParticipants--
|
||||||
} else {
|
|
||||||
if participant.Status == internal.DealConfirmationError {
|
|
||||||
isContainsError = true
|
|
||||||
} else if participant.Status == internal.DealConfirmed {
|
|
||||||
unconfirmedParticipants--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,11 +210,6 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitDeals(inEvent fsm.Event,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isContainsExpired {
|
|
||||||
outEvent = eventDKGDealsConfirmationCancelByTimeoutInternal
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The are no declined and timed out participants, check for all confirmations
|
// The are no declined and timed out participants, check for all confirmations
|
||||||
if unconfirmedParticipants > 0 {
|
if unconfirmedParticipants > 0 {
|
||||||
return
|
return
|
||||||
|
@ -255,9 +259,11 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.Response, request.Response)
|
copy(dkgProposalParticipant.Response, request.Response)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
|
||||||
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
dkgProposalParticipant.Status = internal.ResponseConfirmed
|
||||||
|
|
||||||
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
m.payload.SignatureProposalPayload.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -265,26 +271,23 @@ func (m *DKGProposalFSM) actionResponseConfirmationReceived(inEvent fsm.Event, a
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
isContainsError, isContainsExpired bool
|
isContainsError bool
|
||||||
)
|
)
|
||||||
|
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
tm := time.Now()
|
if m.payload.DKGProposalPayload.IsExpired() {
|
||||||
|
outEvent = eventDKGResponseConfirmationCancelByTimeoutInternal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||||
if participant.Status == internal.ResponseAwaitConfirmation {
|
if participant.Status == internal.ResponseConfirmationError {
|
||||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
isContainsError = true
|
||||||
isContainsExpired = true
|
} else if participant.Status == internal.ResponseConfirmed {
|
||||||
}
|
unconfirmedParticipants--
|
||||||
} else {
|
|
||||||
if participant.Status == internal.ResponseConfirmationError {
|
|
||||||
isContainsError = true
|
|
||||||
} else if participant.Status == internal.ResponseConfirmed {
|
|
||||||
unconfirmedParticipants--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,11 +296,6 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitResponses(inEvent fsm.Eve
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isContainsExpired {
|
|
||||||
outEvent = eventDKGResponseConfirmationCancelByTimeoutInternal
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The are no declined and timed out participants, check for all confirmations
|
// The are no declined and timed out participants, check for all confirmations
|
||||||
if unconfirmedParticipants > 0 {
|
if unconfirmedParticipants > 0 {
|
||||||
return
|
return
|
||||||
|
@ -347,9 +345,11 @@ func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event,
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(dkgProposalParticipant.MasterKey, request.MasterKey)
|
copy(dkgProposalParticipant.MasterKey, request.MasterKey)
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
|
||||||
dkgProposalParticipant.Status = internal.MasterKeyConfirmed
|
dkgProposalParticipant.Status = internal.MasterKeyConfirmed
|
||||||
|
|
||||||
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
m.payload.SignatureProposalPayload.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -357,29 +357,26 @@ func (m *DKGProposalFSM) actionMasterKeyConfirmationReceived(inEvent fsm.Event,
|
||||||
|
|
||||||
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitMasterKey(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *DKGProposalFSM) actionValidateDkgProposalAwaitMasterKey(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
isContainsError, isContainsExpired bool
|
isContainsError bool
|
||||||
masterKeys [][]byte
|
masterKeys [][]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
tm := time.Now()
|
if m.payload.DKGProposalPayload.IsExpired() {
|
||||||
|
outEvent = eventDKGMasterKeyConfirmationCancelByTimeoutInternal
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
unconfirmedParticipants := m.payload.DKGQuorumCount()
|
||||||
|
|
||||||
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
for _, participant := range m.payload.DKGProposalPayload.Quorum {
|
||||||
if participant.Status == internal.MasterKeyAwaitConfirmation {
|
if participant.Status == internal.MasterKeyConfirmationError {
|
||||||
if participant.UpdatedAt.Add(config.DkgConfirmationDeadline).Before(tm) {
|
isContainsError = true
|
||||||
isContainsExpired = true
|
} else if participant.Status == internal.MasterKeyConfirmed {
|
||||||
}
|
masterKeys = append(masterKeys, participant.MasterKey)
|
||||||
} else {
|
unconfirmedParticipants--
|
||||||
if participant.Status == internal.MasterKeyConfirmationError {
|
|
||||||
isContainsError = true
|
|
||||||
} else if participant.Status == internal.MasterKeyConfirmed {
|
|
||||||
masterKeys = append(masterKeys, participant.MasterKey)
|
|
||||||
unconfirmedParticipants--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,11 +385,6 @@ func (m *DKGProposalFSM) actionValidateDkgProposalAwaitMasterKey(inEvent fsm.Eve
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isContainsExpired {
|
|
||||||
outEvent = eventDKGMasterKeyConfirmationCancelByTimeoutInternal
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary simplest match master keys
|
// Temporary simplest match master keys
|
||||||
if len(masterKeys) > 1 {
|
if len(masterKeys) > 1 {
|
||||||
for i, masterKey := range masterKeys {
|
for i, masterKey := range masterKeys {
|
||||||
|
@ -527,7 +519,9 @@ func (m *DKGProposalFSM) actionConfirmationError(inEvent fsm.Event, args ...inte
|
||||||
}
|
}
|
||||||
|
|
||||||
dkgProposalParticipant.Error = request.Error
|
dkgProposalParticipant.Error = request.Error
|
||||||
|
|
||||||
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
dkgProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
m.payload.SignatureProposalPayload.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
m.payload.DKGQuorumUpdate(request.ParticipantId, dkgProposalParticipant)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import "github.com/depools/dc4bc/fsm/fsm_pool"
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"github.com/depools/dc4bc/fsm/fsm_pool"
|
||||||
|
)
|
||||||
|
|
||||||
type DumpedMachineProvider interface {
|
type DumpedMachineProvider interface {
|
||||||
fsm_pool.MachineProvider
|
fsm_pool.MachineProvider
|
||||||
|
@ -10,10 +15,11 @@ type DumpedMachineProvider interface {
|
||||||
// DKG and other stages quorums are separated,
|
// DKG and other stages quorums are separated,
|
||||||
// because unnecessary data may be unset
|
// because unnecessary data may be unset
|
||||||
type DumpedMachineStatePayload struct {
|
type DumpedMachineStatePayload struct {
|
||||||
TransactionId string
|
DkgId string
|
||||||
SignatureProposalPayload *SignatureConfirmation
|
SignatureProposalPayload *SignatureConfirmation
|
||||||
DKGProposalPayload *DKGConfirmation
|
DKGProposalPayload *DKGConfirmation
|
||||||
SigningProposalPayload *SigningConfirmation
|
SigningProposalPayload *SigningConfirmation
|
||||||
|
PubKeys map[string]ed25519.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature quorum
|
// Signature quorum
|
||||||
|
@ -26,7 +32,7 @@ func (p *DumpedMachineStatePayload) SigQuorumCount() int {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DumpedMachineStatePayload) SigQuorumExists(id string) bool {
|
func (p *DumpedMachineStatePayload) SigQuorumExists(id int) bool {
|
||||||
var exists bool
|
var exists bool
|
||||||
if p.SignatureProposalPayload.Quorum != nil {
|
if p.SignatureProposalPayload.Quorum != nil {
|
||||||
_, exists = p.SignatureProposalPayload.Quorum[id]
|
_, exists = p.SignatureProposalPayload.Quorum[id]
|
||||||
|
@ -34,14 +40,14 @@ func (p *DumpedMachineStatePayload) SigQuorumExists(id string) bool {
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DumpedMachineStatePayload) SigQuorumGet(id string) (participant *SignatureProposalParticipant) {
|
func (p *DumpedMachineStatePayload) SigQuorumGet(id int) (participant *SignatureProposalParticipant) {
|
||||||
if p.SignatureProposalPayload.Quorum != nil {
|
if p.SignatureProposalPayload.Quorum != nil {
|
||||||
participant, _ = p.SignatureProposalPayload.Quorum[id]
|
participant, _ = p.SignatureProposalPayload.Quorum[id]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DumpedMachineStatePayload) SigQuorumUpdate(id string, participant *SignatureProposalParticipant) {
|
func (p *DumpedMachineStatePayload) SigQuorumUpdate(id int, participant *SignatureProposalParticipant) {
|
||||||
if p.SignatureProposalPayload.Quorum != nil {
|
if p.SignatureProposalPayload.Quorum != nil {
|
||||||
p.SignatureProposalPayload.Quorum[id] = participant
|
p.SignatureProposalPayload.Quorum[id] = participant
|
||||||
}
|
}
|
||||||
|
@ -111,3 +117,27 @@ func (p *DumpedMachineStatePayload) SigningQuorumUpdate(id int, participant *Sig
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *DumpedMachineStatePayload) SetAddrHexPubKey(addr string, pubKey ed25519.PublicKey) {
|
||||||
|
if p.PubKeys == nil {
|
||||||
|
p.PubKeys = make(map[string]ed25519.PublicKey)
|
||||||
|
}
|
||||||
|
hexAddr := hex.EncodeToString([]byte(addr))
|
||||||
|
p.PubKeys[hexAddr] = pubKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DumpedMachineStatePayload) GetPubKeyByAddr(addr string) (ed25519.PublicKey, error) {
|
||||||
|
if p.PubKeys == nil {
|
||||||
|
return nil, errors.New("{PubKeys} not initialized")
|
||||||
|
}
|
||||||
|
if addr == "" {
|
||||||
|
return nil, errors.New("{addr} cannot be empty")
|
||||||
|
}
|
||||||
|
pubKey, ok := p.PubKeys[addr]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("cannot find public key by {addr}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubKey, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/ed25519"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,24 +32,27 @@ func (s ConfirmationParticipantStatus) String() string {
|
||||||
type SignatureConfirmation struct {
|
type SignatureConfirmation struct {
|
||||||
Quorum SignatureProposalQuorum
|
Quorum SignatureProposalQuorum
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
ExpiresAt time.Time
|
ExpiresAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignatureProposalParticipant struct {
|
type SignatureProposalParticipant struct {
|
||||||
// Public title for address, such as name, nickname, organization
|
Addr string
|
||||||
ParticipantId int
|
PubKey ed25519.PublicKey
|
||||||
Title string
|
DkgPubKey []byte
|
||||||
PubKey *rsa.PublicKey
|
|
||||||
DkgPubKey []byte
|
|
||||||
// For validation user confirmation: sign(InvitationSecret, PubKey) => user
|
// For validation user confirmation: sign(InvitationSecret, PubKey) => user
|
||||||
InvitationSecret string
|
InvitationSecret string
|
||||||
Status ConfirmationParticipantStatus
|
Status ConfirmationParticipantStatus
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *SignatureConfirmation) IsExpired() bool {
|
||||||
|
return c.ExpiresAt.Before(c.UpdatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
// 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 SignatureProposalQuorum map[string]*SignatureProposalParticipant
|
type SignatureProposalQuorum map[int]*SignatureProposalParticipant
|
||||||
|
|
||||||
// DKG proposal
|
// DKG proposal
|
||||||
|
|
||||||
|
@ -71,8 +74,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DKGProposalParticipant struct {
|
type DKGProposalParticipant struct {
|
||||||
Title string
|
Addr string
|
||||||
PubKey []byte
|
DkgPubKey []byte
|
||||||
Commit []byte
|
Commit []byte
|
||||||
Deal []byte
|
Deal []byte
|
||||||
Response []byte
|
Response []byte
|
||||||
|
@ -86,8 +89,13 @@ type DKGProposalQuorum map[int]*DKGProposalParticipant
|
||||||
|
|
||||||
type DKGConfirmation struct {
|
type DKGConfirmation struct {
|
||||||
Quorum DKGProposalQuorum
|
Quorum DKGProposalQuorum
|
||||||
CreatedAt *time.Time
|
CreatedAt time.Time
|
||||||
ExpiresAt *time.Time
|
UpdatedAt time.Time
|
||||||
|
ExpiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DKGConfirmation) IsExpired() bool {
|
||||||
|
return c.ExpiresAt.Before(c.UpdatedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DKGProposalParticipantStatus uint8
|
type DKGProposalParticipantStatus uint8
|
||||||
|
@ -131,9 +139,14 @@ type SigningConfirmation struct {
|
||||||
SrcPayload []byte
|
SrcPayload []byte
|
||||||
EncryptedPayload []byte
|
EncryptedPayload []byte
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
ExpiresAt time.Time
|
ExpiresAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *SigningConfirmation) IsExpired() bool {
|
||||||
|
return c.ExpiresAt.Before(c.UpdatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
type SigningProposalQuorum map[int]*SigningProposalParticipant
|
type SigningProposalQuorum map[int]*SigningProposalParticipant
|
||||||
|
|
||||||
type SigningParticipantStatus uint8
|
type SigningParticipantStatus uint8
|
||||||
|
@ -171,7 +184,7 @@ func (s SigningParticipantStatus) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SigningProposalParticipant struct {
|
type SigningProposalParticipant struct {
|
||||||
Title string
|
Addr string
|
||||||
Status SigningParticipantStatus
|
Status SigningParticipantStatus
|
||||||
PartialKey []byte
|
PartialKey []byte
|
||||||
Error error
|
Error error
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
package state_machines
|
package state_machines
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
@ -43,19 +45,13 @@ func init() {
|
||||||
|
|
||||||
// Create new fsm with unique id
|
// Create new fsm with unique id
|
||||||
// transactionId required for unique identify dump
|
// transactionId required for unique identify dump
|
||||||
func Create() (*FSMInstance, error) {
|
func Create(dkgID string) (*FSMInstance, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
i = &FSMInstance{}
|
i = &FSMInstance{}
|
||||||
)
|
)
|
||||||
transactionId, err := generateDkgTransactionId()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = i.InitDump(transactionId)
|
|
||||||
|
|
||||||
|
err = i.InitDump(dkgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -90,6 +86,14 @@ func FromDump(data []byte) (*FSMInstance, error) {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *FSMInstance) GetPubKeyByAddr(addr string) (ed25519.PublicKey, error) {
|
||||||
|
if i.dump == nil {
|
||||||
|
return nil, errors.New("dump not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.dump.Payload.GetPubKeyByAddr(addr)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Response, dump []byte, err error) {
|
func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Response, dump []byte, err error) {
|
||||||
var dumpErr error
|
var dumpErr error
|
||||||
|
|
||||||
|
@ -112,22 +116,22 @@ func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Resp
|
||||||
return result, dump, err
|
return result, dump, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *FSMInstance) InitDump(transactionId string) error {
|
func (i *FSMInstance) InitDump(dkgID string) error {
|
||||||
if i.dump != nil {
|
if i.dump != nil {
|
||||||
return errors.New("dump already initialized")
|
return errors.New("dump already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionId = strings.TrimSpace(transactionId)
|
dkgID = strings.TrimSpace(dkgID)
|
||||||
|
|
||||||
if transactionId == "" {
|
if dkgID == "" {
|
||||||
return errors.New("empty transaction id")
|
return errors.New("empty {dkgID}")
|
||||||
}
|
}
|
||||||
|
|
||||||
i.dump = &FSMDump{
|
i.dump = &FSMDump{
|
||||||
TransactionId: transactionId,
|
TransactionId: dkgID,
|
||||||
State: fsm.StateGlobalIdle,
|
State: fsm.StateGlobalIdle,
|
||||||
Payload: &internal.DumpedMachineStatePayload{
|
Payload: &internal.DumpedMachineStatePayload{
|
||||||
TransactionId: transactionId,
|
DkgId: dkgID,
|
||||||
SignatureProposalPayload: nil,
|
SignatureProposalPayload: nil,
|
||||||
DKGProposalPayload: nil,
|
DKGProposalPayload: nil,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,22 +3,21 @@ package state_machines
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/depools/dc4bc/fsm/fsm"
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||||
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"github.com/depools/dc4bc/fsm/types/responses"
|
"github.com/depools/dc4bc/fsm/types/responses"
|
||||||
"log"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type testExternalParticipants struct {
|
type testExternalParticipants struct {
|
||||||
Title string
|
Addr string
|
||||||
PrivKey *rsa.PrivateKey
|
PrivKey *rsa.PrivateKey
|
||||||
PubKey *rsa.PublicKey
|
PubKey *rsa.PublicKey
|
||||||
DkgPubKey []byte
|
DkgPubKey []byte
|
||||||
|
@ -27,7 +26,9 @@ type testExternalParticipants struct {
|
||||||
var (
|
var (
|
||||||
tm = time.Now()
|
tm = time.Now()
|
||||||
|
|
||||||
testParticipants = map[string]*testExternalParticipants{}
|
dkgId = "1b7a6382afe0fbe2ff127a5779f5e9b042e685cabefeadcf4ef27c6089a56bfb"
|
||||||
|
|
||||||
|
testParticipants = map[int]*testExternalParticipants{}
|
||||||
|
|
||||||
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
|
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
|
||||||
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
Participants: []*requests.SignatureProposalParticipantsEntry{},
|
||||||
|
@ -51,22 +52,17 @@ func init() {
|
||||||
|
|
||||||
key.Precompute()
|
key.Precompute()
|
||||||
|
|
||||||
marshaledPubKey := x509.MarshalPKCS1PublicKey(&key.PublicKey)
|
|
||||||
hash := sha1.Sum(marshaledPubKey)
|
|
||||||
|
|
||||||
fingerprint := base64.StdEncoding.EncodeToString(hash[:])
|
|
||||||
|
|
||||||
pubKeyMock := make([]byte, 128)
|
pubKeyMock := make([]byte, 128)
|
||||||
|
|
||||||
rand.Read(pubKeyMock)
|
rand.Read(pubKeyMock)
|
||||||
|
|
||||||
participant := &testExternalParticipants{
|
participant := &testExternalParticipants{
|
||||||
Title: fmt.Sprintf("User %d", i),
|
Addr: fmt.Sprintf("User %d", i),
|
||||||
PrivKey: key,
|
PrivKey: key,
|
||||||
PubKey: &key.PublicKey,
|
PubKey: &key.PublicKey,
|
||||||
DkgPubKey: pubKeyMock,
|
DkgPubKey: pubKeyMock,
|
||||||
}
|
}
|
||||||
testParticipants[fingerprint] = participant
|
testParticipants[i] = participant
|
||||||
}
|
}
|
||||||
|
|
||||||
participantsForRequest := make([]*requests.SignatureProposalParticipantsEntry, 0)
|
participantsForRequest := make([]*requests.SignatureProposalParticipantsEntry, 0)
|
||||||
|
@ -74,7 +70,7 @@ func init() {
|
||||||
for _, participant := range testParticipants {
|
for _, participant := range testParticipants {
|
||||||
|
|
||||||
participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{
|
participantsForRequest = append(participantsForRequest, &requests.SignatureProposalParticipantsEntry{
|
||||||
Title: participant.Title,
|
Addr: participant.Addr,
|
||||||
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
|
PubKey: x509.MarshalPKCS1PublicKey(participant.PubKey),
|
||||||
DkgPubKey: participant.DkgPubKey,
|
DkgPubKey: participant.DkgPubKey,
|
||||||
})
|
})
|
||||||
|
@ -84,7 +80,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreate_Positive(t *testing.T) {
|
func TestCreate_Positive(t *testing.T) {
|
||||||
testFSMInstance, err := Create()
|
testFSMInstance, err := Create(dkgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected nil error, got {%s}", err)
|
t.Fatalf("expected nil error, got {%s}", err)
|
||||||
}
|
}
|
||||||
|
@ -127,7 +123,7 @@ func compareState(t *testing.T, expected fsm.State, got fsm.State) {
|
||||||
// Test Workflow
|
// Test Workflow
|
||||||
|
|
||||||
func Test_SignatureProposal_Init(t *testing.T) {
|
func Test_SignatureProposal_Init(t *testing.T) {
|
||||||
testFSMInstance, err := Create()
|
testFSMInstance, err := Create(dkgId)
|
||||||
|
|
||||||
compareErrNil(t, err)
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
@ -186,16 +182,8 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
t.Fatalf("expected unique {ParticipantId}")
|
t.Fatalf("expected unique {ParticipantId}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if participant.Title == "" {
|
if participant.Addr == "" {
|
||||||
t.Fatalf("expected not empty {Title}")
|
t.Fatalf("expected not empty {Addr}")
|
||||||
}
|
|
||||||
|
|
||||||
if participant.EncryptedInvitation == "" {
|
|
||||||
t.Fatalf("expected not empty {DecryptedInvitation}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if participant.PubKeyFingerprint == "" {
|
|
||||||
t.Fatalf("expected not empty {PubKeyFingerprint}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
participantsMap[participant.ParticipantId] = participant
|
participantsMap[participant.ParticipantId] = participant
|
||||||
|
@ -215,21 +203,9 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
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.EventConfirmSignatureProposal, requests.SignatureProposalParticipantRequest{
|
fsmResponse, dump, err = testFSMInstance.Do(spf.EventConfirmSignatureProposal, requests.SignatureProposalParticipantRequest{
|
||||||
PubKeyFingerprint: participant.PubKeyFingerprint,
|
ParticipantId: participant.ParticipantId,
|
||||||
DecryptedInvitation: string(encrypted),
|
CreatedAt: tm,
|
||||||
CreatedAt: tm,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
compareErrNil(t, err)
|
compareErrNil(t, err)
|
||||||
|
@ -248,7 +224,9 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
testFSMInstance, err = FromDump(dump)
|
testFSMInstance, err = FromDump(dump)
|
||||||
|
|
||||||
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGInitProcess)
|
fsmResponse, dump, err = testFSMInstance.Do(dpf.EventDKGInitProcess, requests.DefaultRequest{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
compareErrNil(t, err)
|
compareErrNil(t, err)
|
||||||
|
|
||||||
|
@ -268,7 +246,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
if _, ok := testParticipants[participant.ParticipantId]; !ok {
|
||||||
t.Fatalf("not found external user data for response fingerprint")
|
t.Fatalf("not found external user data for response fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +282,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
if _, ok := testParticipants[participant.ParticipantId]; !ok {
|
||||||
t.Fatalf("not found external user data for response fingerprint")
|
t.Fatalf("not found external user data for response fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +318,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
if _, ok := testParticipants[participant.ParticipantId]; !ok {
|
||||||
t.Fatalf("not found external user data for response fingerprint")
|
t.Fatalf("not found external user data for response fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +360,7 @@ func Test_SignatureProposal_Positive(t *testing.T) {
|
||||||
|
|
||||||
compareFSMInstanceNotNil(t, testFSMInstance)
|
compareFSMInstanceNotNil(t, testFSMInstance)
|
||||||
|
|
||||||
if _, ok := testParticipants[participant.PubKeyFingerprint]; !ok {
|
if _, ok := testParticipants[participant.ParticipantId]; !ok {
|
||||||
t.Fatalf("not found external user data for response fingerprint")
|
t.Fatalf("not found external user data for response fingerprint")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package signature_proposal_fsm
|
package signature_proposal_fsm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/depools/dc4bc/fsm/config"
|
"github.com/depools/dc4bc/fsm/config"
|
||||||
|
@ -9,7 +8,6 @@ import (
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"github.com/depools/dc4bc/fsm/types/responses"
|
"github.com/depools/dc4bc/fsm/types/responses"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// init -> awaitingConfirmations
|
// init -> awaitingConfirmations
|
||||||
|
@ -35,31 +33,22 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar
|
||||||
}
|
}
|
||||||
|
|
||||||
m.payload.SignatureProposalPayload = &internal.SignatureConfirmation{
|
m.payload.SignatureProposalPayload = &internal.SignatureConfirmation{
|
||||||
Quorum: make(internal.SignatureProposalQuorum),
|
Quorum: make(internal.SignatureProposalQuorum),
|
||||||
|
CreatedAt: request.CreatedAt,
|
||||||
|
ExpiresAt: request.CreatedAt.Add(config.SignatureProposalConfirmationDeadline),
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, participant := range request.Participants {
|
for index, participant := range request.Participants {
|
||||||
participantId := createFingerprint(&participant.PubKey)
|
//participantId := createFingerprint(&participant.DkgPubKey)
|
||||||
secret, err := generateRandomString(32)
|
m.payload.SignatureProposalPayload.Quorum[index] = &internal.SignatureProposalParticipant{
|
||||||
if err != nil {
|
Addr: participant.Addr,
|
||||||
return inEvent, nil, errors.New("cannot generate source for {InvitationSecret}")
|
PubKey: participant.PubKey,
|
||||||
|
DkgPubKey: participant.DkgPubKey,
|
||||||
|
Status: internal.SigConfirmationAwaitConfirmation,
|
||||||
|
UpdatedAt: request.CreatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedPubKey, err := x509.ParsePKCS1PublicKey(participant.PubKey)
|
m.payload.SetAddrHexPubKey(participant.Addr, participant.PubKey)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return inEvent, nil, errors.New("cannot parse {PubKey}")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.payload.SignatureProposalPayload.Quorum[participantId] = &internal.SignatureProposalParticipant{
|
|
||||||
ParticipantId: index,
|
|
||||||
Title: participant.Title,
|
|
||||||
PubKey: parsedPubKey,
|
|
||||||
DkgPubKey: participant.DkgPubKey,
|
|
||||||
InvitationSecret: secret,
|
|
||||||
Status: internal.SigConfirmationAwaitConfirmation,
|
|
||||||
UpdatedAt: request.CreatedAt,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checking fo quorum length
|
// Checking fo quorum length
|
||||||
|
@ -72,16 +61,10 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar
|
||||||
|
|
||||||
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
|
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
|
||||||
|
|
||||||
for pubKeyFingerprint, proposal := range m.payload.SignatureProposalPayload.Quorum {
|
for participantId, proposal := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
encryptedInvitationSecret, err := encryptWithPubKey(proposal.PubKey, proposal.InvitationSecret)
|
|
||||||
if err != nil {
|
|
||||||
return inEvent, nil, errors.New("cannot encryptWithPubKey")
|
|
||||||
}
|
|
||||||
responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
|
responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
|
||||||
ParticipantId: proposal.ParticipantId,
|
ParticipantId: participantId,
|
||||||
Title: proposal.Title,
|
Addr: proposal.Addr,
|
||||||
PubKeyFingerprint: pubKeyFingerprint,
|
|
||||||
EncryptedInvitation: encryptedInvitationSecret,
|
|
||||||
}
|
}
|
||||||
responseData = append(responseData, responseEntry)
|
responseData = append(responseData, responseEntry)
|
||||||
}
|
}
|
||||||
|
@ -111,18 +94,12 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.payload.SigQuorumExists(request.PubKeyFingerprint) {
|
if !m.payload.SigQuorumExists(request.ParticipantId) {
|
||||||
err = errors.New("{PubKeyFingerprint} not exist in quorum")
|
err = errors.New("{ParticipantId} not exist in quorum")
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureProposalParticipant := m.payload.SigQuorumGet(request.PubKeyFingerprint)
|
|
||||||
|
|
||||||
if signatureProposalParticipant.InvitationSecret != request.DecryptedInvitation {
|
|
||||||
err = errors.New("{InvitationSecret} not match {DecryptedInvitation}")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signatureProposalParticipant := m.payload.SigQuorumGet(request.ParticipantId)
|
||||||
if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(request.CreatedAt) {
|
if signatureProposalParticipant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(request.CreatedAt) {
|
||||||
outEvent = eventSetValidationCanceledByTimeout
|
outEvent = eventSetValidationCanceledByTimeout
|
||||||
return
|
return
|
||||||
|
@ -145,33 +122,31 @@ func (m *SignatureProposalFSM) actionProposalResponseByParticipant(inEvent fsm.E
|
||||||
|
|
||||||
signatureProposalParticipant.UpdatedAt = request.CreatedAt
|
signatureProposalParticipant.UpdatedAt = request.CreatedAt
|
||||||
|
|
||||||
m.payload.SigQuorumUpdate(request.PubKeyFingerprint, signatureProposalParticipant)
|
m.payload.SigQuorumUpdate(request.ParticipantId, signatureProposalParticipant)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
|
||||||
var (
|
var (
|
||||||
isContainsDeclined, isContainsExpired bool
|
isContainsDeclined bool
|
||||||
)
|
)
|
||||||
|
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
tm := time.Now()
|
if m.payload.SignatureProposalPayload.IsExpired() {
|
||||||
|
outEvent = eventSetValidationCanceledByTimeout
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
unconfirmedParticipants := m.payload.SigQuorumCount()
|
unconfirmedParticipants := m.payload.SigQuorumCount()
|
||||||
|
|
||||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
if participant.Status == internal.SigConfirmationAwaitConfirmation {
|
if participant.Status == internal.SigConfirmationConfirmed {
|
||||||
if participant.UpdatedAt.Add(config.SignatureProposalConfirmationDeadline).Before(tm) {
|
unconfirmedParticipants--
|
||||||
isContainsExpired = true
|
} else if participant.Status == internal.SigConfirmationDeclined {
|
||||||
}
|
isContainsDeclined = true
|
||||||
} else {
|
|
||||||
if participant.Status == internal.SigConfirmationConfirmed {
|
|
||||||
unconfirmedParticipants--
|
|
||||||
} else if participant.Status == internal.SigConfirmationDeclined {
|
|
||||||
isContainsDeclined = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +155,6 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isContainsExpired {
|
|
||||||
outEvent = eventSetValidationCanceledByTimeout
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The are no declined and timed out participants, check for all confirmations
|
// The are no declined and timed out participants, check for all confirmations
|
||||||
if unconfirmedParticipants > 0 {
|
if unconfirmedParticipants > 0 {
|
||||||
return
|
return
|
||||||
|
@ -192,11 +162,10 @@ func (m *SignatureProposalFSM) actionValidateSignatureProposal(inEvent fsm.Event
|
||||||
|
|
||||||
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
|
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
|
||||||
|
|
||||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
for participantId, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
||||||
ParticipantId: participant.ParticipantId,
|
ParticipantId: participantId,
|
||||||
Title: participant.Title,
|
Addr: participant.Addr,
|
||||||
DkgPubKey: participant.DkgPubKey,
|
|
||||||
Status: uint8(participant.Status),
|
Status: uint8(participant.Status),
|
||||||
}
|
}
|
||||||
responseData = append(responseData, responseEntry)
|
responseData = append(responseData, responseEntry)
|
||||||
|
@ -211,10 +180,10 @@ func (m *SignatureProposalFSM) actionSignatureProposalCanceledByTimeout(inEvent
|
||||||
|
|
||||||
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
|
responseData := make(responses.SignatureProposalParticipantStatusResponse, 0)
|
||||||
|
|
||||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
for participantId, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
responseEntry := &responses.SignatureProposalParticipantStatusEntry{
|
||||||
ParticipantId: participant.ParticipantId,
|
ParticipantId: participantId,
|
||||||
Title: participant.Title,
|
Addr: participant.Addr,
|
||||||
Status: uint8(participant.Status),
|
Status: uint8(participant.Status),
|
||||||
}
|
}
|
||||||
responseData = append(responseData, responseEntry)
|
responseData = append(responseData, responseEntry)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
"github.com/depools/dc4bc/fsm/state_machines/internal"
|
||||||
"github.com/depools/dc4bc/fsm/types/responses"
|
"github.com/depools/dc4bc/fsm/types/responses"
|
||||||
)
|
)
|
||||||
|
@ -13,12 +14,9 @@ import (
|
||||||
|
|
||||||
func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum) responses.SignatureProposalParticipantInvitationsResponse {
|
func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum) responses.SignatureProposalParticipantInvitationsResponse {
|
||||||
var response responses.SignatureProposalParticipantInvitationsResponse
|
var response responses.SignatureProposalParticipantInvitationsResponse
|
||||||
for quorumId, parcipant := range *list {
|
for _, participant := range *list {
|
||||||
response = append(response, &responses.SignatureProposalParticipantInvitationEntry{
|
response = append(response, &responses.SignatureProposalParticipantInvitationEntry{
|
||||||
Title: parcipant.Title,
|
Addr: participant.Addr,
|
||||||
PubKeyFingerprint: quorumId,
|
|
||||||
// TODO: Add encryption
|
|
||||||
EncryptedInvitation: parcipant.InvitationSecret,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -14,13 +14,31 @@ func (m *SigningProposalFSM) actionInitSigningProposal(inEvent fsm.Event, args .
|
||||||
m.payloadMu.Lock()
|
m.payloadMu.Lock()
|
||||||
defer m.payloadMu.Unlock()
|
defer m.payloadMu.Unlock()
|
||||||
|
|
||||||
m.payload.SigningProposalPayload = &internal.SigningConfirmation{
|
if len(args) != 1 {
|
||||||
Quorum: make(internal.SigningProposalQuorum),
|
err = errors.New("{arg0} required {DefaultRequest}")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, participant := range m.payload.SignatureProposalPayload.Quorum {
|
request, ok := args[0].(requests.DefaultRequest)
|
||||||
m.payload.SigningProposalPayload.Quorum[participant.ParticipantId] = &internal.SigningProposalParticipant{
|
|
||||||
Title: participant.Title,
|
if !ok {
|
||||||
|
err = errors.New("cannot cast {arg0} to type {DefaultRequest}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = request.Validate(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.payload.SigningProposalPayload = &internal.SigningConfirmation{
|
||||||
|
Quorum: make(internal.SigningProposalQuorum),
|
||||||
|
CreatedAt: request.CreatedAt,
|
||||||
|
ExpiresAt: request.CreatedAt.Add(config.SigningConfirmationDeadline),
|
||||||
|
}
|
||||||
|
|
||||||
|
for participantId, participant := range m.payload.SignatureProposalPayload.Quorum {
|
||||||
|
m.payload.SigningProposalPayload.Quorum[participantId] = &internal.SigningProposalParticipant{
|
||||||
|
Addr: participant.Addr,
|
||||||
Status: internal.SigningIdle,
|
Status: internal.SigningIdle,
|
||||||
UpdatedAt: participant.UpdatedAt,
|
UpdatedAt: participant.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
@ -49,6 +67,8 @@ func (m *SigningProposalFSM) actionStartSigningProposal(inEvent fsm.Event, args
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.payload.SigningProposalPayload.CreatedAt = request.CreatedAt
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,14 @@ const (
|
||||||
|
|
||||||
StateSigningAwaitPartialKeys = fsm.State("state_signing_await_partial_keys")
|
StateSigningAwaitPartialKeys = fsm.State("state_signing_await_partial_keys")
|
||||||
// Cancelled
|
// Cancelled
|
||||||
StateSigningPartialKeysAwaitCancelledByTimeout = fsm.State("state_signing_partial_keys_await_cancelled_by_timeout")
|
StateSigningPartialKeysAwaitCancelledByTimeout = fsm.State("state_signing_partial_signatures_await_cancelled_by_timeout")
|
||||||
StateSigningPartialKeysAwaitCancelledByParticipant = fsm.State("state_signing_partial_keys_await_cancelled_by_participant")
|
StateSigningPartialKeysAwaitCancelledByParticipant = fsm.State("state_signing_partial_signatures_await_cancelled_by_participant")
|
||||||
|
|
||||||
StateSigningPartialKeysCollected = fsm.State("state_signing_partial_keys_collected")
|
StateSigningPartialKeysCollected = fsm.State("state_signing_partial_signatures_collected")
|
||||||
|
|
||||||
|
// await full
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package requests
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type DefaultRequest struct {
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package requests
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func (r *DefaultRequest) Validate() error {
|
||||||
|
if r.CreatedAt.IsZero() {
|
||||||
|
return errors.New("{CreatedAt} is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ type SignatureProposalParticipantsListRequest struct {
|
||||||
|
|
||||||
type SignatureProposalParticipantsEntry struct {
|
type SignatureProposalParticipantsEntry struct {
|
||||||
// Public title for address, such as name, nickname, organization
|
// Public title for address, such as name, nickname, organization
|
||||||
Title string
|
Addr string
|
||||||
PubKey []byte
|
PubKey []byte
|
||||||
DkgPubKey []byte
|
DkgPubKey []byte
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ type SignatureProposalParticipantsEntry struct {
|
||||||
// "event_sig_proposal_decline_by_participant"
|
// "event_sig_proposal_decline_by_participant"
|
||||||
type SignatureProposalParticipantRequest struct {
|
type SignatureProposalParticipantRequest struct {
|
||||||
// Key for link invitations to participants
|
// Key for link invitations to participants
|
||||||
PubKeyFingerprint string
|
ParticipantId int
|
||||||
DecryptedInvitation string
|
CreatedAt time.Time
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package requests
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/depools/dc4bc/fsm/config"
|
"github.com/depools/dc4bc/fsm/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,12 +21,12 @@ func (r *SignatureProposalParticipantsListRequest) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, participant := range r.Participants {
|
for _, participant := range r.Participants {
|
||||||
if len(participant.Title) < 3 {
|
if len(participant.Addr) < 3 {
|
||||||
return errors.New("{Title} minimum length is {3}")
|
return errors.New("{Addr} minimum length is {3}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(participant.Title) > 150 {
|
if len(participant.Addr) > 150 {
|
||||||
return errors.New("{Title} maximum length is {150}")
|
return errors.New("{Addr} maximum length is {150}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(participant.PubKey) < 10 {
|
if len(participant.PubKey) < 10 {
|
||||||
|
@ -45,12 +46,8 @@ func (r *SignatureProposalParticipantsListRequest) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SignatureProposalParticipantRequest) Validate() error {
|
func (r *SignatureProposalParticipantRequest) Validate() error {
|
||||||
if len(r.PubKeyFingerprint) == 0 {
|
if r.ParticipantId < 0 {
|
||||||
return errors.New("{PubKeyFingerprint} cannot zero length")
|
return errors.New("{ParticipantId} cannot be a negative number")
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.DecryptedInvitation) == 0 {
|
|
||||||
return errors.New("{DecryptedInvitation} cannot zero length")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.CreatedAt.IsZero() {
|
if r.CreatedAt.IsZero() {
|
||||||
|
|
|
@ -10,11 +10,7 @@ type SignatureProposalParticipantInvitationsResponse []*SignatureProposalPartici
|
||||||
type SignatureProposalParticipantInvitationEntry struct {
|
type SignatureProposalParticipantInvitationEntry struct {
|
||||||
ParticipantId int
|
ParticipantId int
|
||||||
// Public title for address, such as name, nickname, organization
|
// Public title for address, such as name, nickname, organization
|
||||||
Title string
|
Addr string
|
||||||
// Key for link invitations to participants
|
|
||||||
PubKeyFingerprint string
|
|
||||||
// Encrypted with public key secret
|
|
||||||
EncryptedInvitation string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public lists for proposal confirmation process
|
// Public lists for proposal confirmation process
|
||||||
|
@ -23,7 +19,7 @@ type SignatureProposalParticipantStatusResponse []*SignatureProposalParticipantS
|
||||||
|
|
||||||
type SignatureProposalParticipantStatusEntry struct {
|
type SignatureProposalParticipantStatusEntry struct {
|
||||||
ParticipantId int
|
ParticipantId int
|
||||||
Title string
|
Addr string
|
||||||
DkgPubKey []byte
|
|
||||||
Status uint8
|
Status uint8
|
||||||
|
DkgPubKey []byte
|
||||||
}
|
}
|
||||||
|
|
8
main.go
8
main.go
|
@ -29,6 +29,7 @@ func main() {
|
||||||
var numNodes = 4
|
var numNodes = 4
|
||||||
for nodeID := 0; nodeID < numNodes; nodeID++ {
|
for nodeID := 0; nodeID < numNodes; nodeID++ {
|
||||||
var ctx = context.Background()
|
var ctx = context.Background()
|
||||||
|
var userName = fmt.Sprintf("node_%d", nodeID)
|
||||||
var state, err = client.NewLevelDBState(fmt.Sprintf("/tmp/dc4bc_node_%d_state", nodeID))
|
var state, err = client.NewLevelDBState(fmt.Sprintf("/tmp/dc4bc_node_%d_state", nodeID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("node %d failed to init state: %v\n", nodeID, err)
|
log.Fatalf("node %d failed to init state: %v\n", nodeID, err)
|
||||||
|
@ -39,11 +40,18 @@ func main() {
|
||||||
log.Fatalf("node %d failed to init storage: %v\n", nodeID, err)
|
log.Fatalf("node %d failed to init storage: %v\n", nodeID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyStore, err := client.NewLevelDBKeyStore(userName, fmt.Sprintf("/tmp/dc4bc_node_%d_key_store", nodeID))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to init key store: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
clt, err := client.NewClient(
|
clt, err := client.NewClient(
|
||||||
ctx,
|
ctx,
|
||||||
|
userName,
|
||||||
nil,
|
nil,
|
||||||
state,
|
state,
|
||||||
stg,
|
stg,
|
||||||
|
keyStore,
|
||||||
qr.NewCameraProcessor(),
|
qr.NewCameraProcessor(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
package clientMocks
|
package clientMocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
client "github.com/depools/dc4bc/client"
|
client "github.com/depools/dc4bc/client"
|
||||||
|
state_machines "github.com/depools/dc4bc/fsm/state_machines"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockState is a mock of State interface
|
// MockState is a mock of State interface
|
||||||
|
@ -64,32 +64,33 @@ func (mr *MockStateMockRecorder) LoadOffset() *gomock.Call {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveFSM mocks base method
|
// SaveFSM mocks base method
|
||||||
func (m *MockState) SaveFSM(arg0 []byte) error {
|
func (m *MockState) SaveFSM(dkgRoundID string, dump []byte) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SaveFSM", arg0)
|
ret := m.ctrl.Call(m, "SaveFSM", dkgRoundID, dump)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveFSM indicates an expected call of SaveFSM
|
// SaveFSM indicates an expected call of SaveFSM
|
||||||
func (mr *MockStateMockRecorder) SaveFSM(arg0 []byte) *gomock.Call {
|
func (mr *MockStateMockRecorder) SaveFSM(dkgRoundID, dump interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveFSM", reflect.TypeOf((*MockState)(nil).SaveFSM), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveFSM", reflect.TypeOf((*MockState)(nil).SaveFSM), dkgRoundID, dump)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadFSM mocks base method
|
// LoadFSM mocks base method
|
||||||
func (m *MockState) LoadFSM() ([]byte, error) {
|
func (m *MockState) LoadFSM(dkgRoundID string) (*state_machines.FSMInstance, bool, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "LoadFSM")
|
ret := m.ctrl.Call(m, "LoadFSM", dkgRoundID)
|
||||||
ret0, _ := ret[0].([]byte)
|
ret0, _ := ret[0].(*state_machines.FSMInstance)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(bool)
|
||||||
return ret0, ret1
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadFSM indicates an expected call of LoadFSM
|
// LoadFSM indicates an expected call of LoadFSM
|
||||||
func (mr *MockStateMockRecorder) LoadFSM() *gomock.Call {
|
func (mr *MockStateMockRecorder) LoadFSM(dkgRoundID interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadFSM", reflect.TypeOf((*MockState)(nil).LoadFSM))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadFSM", reflect.TypeOf((*MockState)(nil).LoadFSM), dkgRoundID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutOperation mocks base method
|
// PutOperation mocks base method
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
//go:generate mockgen -source=./../client/state.go -destination=./clientMocks/state_mock.go -package=clientMocks
|
//go:generate mockgen -source=./../client/state.go -destination=./clientMocks/state_mock.go -package=clientMocks
|
||||||
|
//go:generate mockgen -source=./../client/keystore.go -destination=./clientMocks/keystore_mock.go -package=clientMocks
|
||||||
//go:generate mockgen -source=./../storage/types.go -destination=./storageMocks/storage_mock.go -package=storageMocks
|
//go:generate mockgen -source=./../storage/types.go -destination=./storageMocks/storage_mock.go -package=storageMocks
|
||||||
//go:generate mockgen -source=./../qr/qr.go -destination=./qrMocks/qr_mock.go -package=qrMocks
|
//go:generate mockgen -source=./../qr/qr.go -destination=./qrMocks/qr_mock.go -package=qrMocks
|
||||||
|
|
|
@ -5,10 +5,9 @@
|
||||||
package storageMocks
|
package storageMocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
storage "github.com/depools/dc4bc/storage"
|
storage "github.com/depools/dc4bc/storage"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockStorage is a mock of Storage interface
|
// MockStorage is a mock of Storage interface
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
To string `json:"to"`
|
ID string `json:"id"`
|
||||||
Data []byte `json:"data"`
|
DkgRoundID string `json:"dkg_round_id"`
|
||||||
Signature []byte `json:"signature"`
|
Offset uint64 `json:"offset"`
|
||||||
ID string `json:"id"`
|
Event string `json:"event"`
|
||||||
Offset uint64 `json:"offset"`
|
Data []byte `json:"data"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
Signature []byte `json:"signature"`
|
||||||
|
SenderAddr string `json:"sender"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) Bytes() []byte {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
buf.Write(m.Data)
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) Verify(pubKey ed25519.PublicKey) bool {
|
||||||
|
return ed25519.Verify(pubKey, m.Bytes(), m.Signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
Send(message Message) (Message, error)
|
Send(message Message) (Message, error)
|
||||||
GetMessages(offset uint64) ([]Message, error)
|
GetMessages(offset uint64) ([]Message, error)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
Loading…
Reference in New Issue