From 2572c51bf7e75230f2bab1b00db4e8fc6de3c21a Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Wed, 19 Aug 2020 18:10:46 +0300 Subject: [PATCH 1/3] feat: more client tests --- client/client_test.go | 3 +- mocks/clientMocks/keystore_mock.go | 49 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 mocks/clientMocks/keystore_mock.go diff --git a/client/client_test.go b/client/client_test.go index 487db28..115daed 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -79,7 +79,8 @@ func TestClient_ProcessMessage(t *testing.T) { DkgPubKey: make([]byte, 128), }, }, - CreatedAt: time.Now(), + CreatedAt: time.Now(), + SigningThreshold: 2, } messageDataBz, err := json.Marshal(messageData) req.NoError(err) diff --git a/mocks/clientMocks/keystore_mock.go b/mocks/clientMocks/keystore_mock.go new file mode 100644 index 0000000..11a4de1 --- /dev/null +++ b/mocks/clientMocks/keystore_mock.go @@ -0,0 +1,49 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./../client/keystore.go + +// Package clientMocks is a generated GoMock package. +package clientMocks + +import ( + client "github.com/depools/dc4bc/client" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockKeyStore is a mock of KeyStore interface +type MockKeyStore struct { + ctrl *gomock.Controller + recorder *MockKeyStoreMockRecorder +} + +// MockKeyStoreMockRecorder is the mock recorder for MockKeyStore +type MockKeyStoreMockRecorder struct { + mock *MockKeyStore +} + +// NewMockKeyStore creates a new mock instance +func NewMockKeyStore(ctrl *gomock.Controller) *MockKeyStore { + mock := &MockKeyStore{ctrl: ctrl} + mock.recorder = &MockKeyStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockKeyStore) EXPECT() *MockKeyStoreMockRecorder { + return m.recorder +} + +// LoadKeys mocks base method +func (m *MockKeyStore) LoadKeys(userName, password string) (*client.KeyPair, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadKeys", userName, password) + ret0, _ := ret[0].(*client.KeyPair) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LoadKeys indicates an expected call of LoadKeys +func (mr *MockKeyStoreMockRecorder) LoadKeys(userName, password interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadKeys", reflect.TypeOf((*MockKeyStore)(nil).LoadKeys), userName, password) +} From b5780402e398fa69c4f63f9a75d4280747d623b9 Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Wed, 19 Aug 2020 18:16:40 +0300 Subject: [PATCH 2/3] wip --- cmd/client/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/client/main.go b/cmd/client/main.go index ac17e83..3d02d4f 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -81,7 +81,7 @@ var rootCmd = &cobra.Command{ // TODO: create state machine. - cli, err := client.NewClient(ctx, userName, nil, state, stg, keyStore, processor) + cli, err := client.NewClient(ctx, userName, state, stg, keyStore, processor) if err != nil { log.Fatalf("Failed to init client: %v", err) } From 33eeba1854639ea660cef5318ae29e2a794bc3fa Mon Sep 17 00:00:00 2001 From: Andrej Zavgorodnij Date: Wed, 19 Aug 2020 19:04:41 +0300 Subject: [PATCH 3/3] wip --- client/client.go | 30 ++++++--- client/client_test.go | 20 +++--- client/keystore.go | 36 ++++++++-- main.go | 148 +++++++++++++++++++----------------------- 4 files changed, 130 insertions(+), 104 deletions(-) diff --git a/client/client.go b/client/client.go index a54d6d9..b25d272 100644 --- a/client/client.go +++ b/client/client.go @@ -12,11 +12,12 @@ import ( "time" "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/state_machines" "github.com/depools/dc4bc/fsm/fsm" - dkgFSM "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm" + dpf "github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm" "github.com/depools/dc4bc/qr" "github.com/depools/dc4bc/storage" ) @@ -30,6 +31,7 @@ type Client struct { sync.Mutex userName string address string + pubKey ed25519.PublicKey ctx context.Context state State storage storage.Storage @@ -54,6 +56,7 @@ func NewClient( ctx: ctx, userName: userName, address: keyPair.GetAddr(), + pubKey: keyPair.Pub, state: state, storage: storage, keyStore: keyStore, @@ -61,12 +64,12 @@ func NewClient( }, nil } -func (c *Client) SendMessage(message storage.Message) error { - if _, err := c.storage.Send(message); err != nil { - return fmt.Errorf("failed to post message: %w", err) - } +func (c *Client) GetAddr() string { + return c.address +} - return nil +func (c *Client) GetPubKey() ed25519.PublicKey { + return c.pubKey } func (c *Client) Poll() error { @@ -96,6 +99,14 @@ func (c *Client) Poll() error { } } +func (c *Client) SendMessage(message storage.Message) error { + if _, err := c.storage.Send(message); err != nil { + return fmt.Errorf("failed to post message: %w", err) + } + + return nil +} + func (c *Client) ProcessMessage(message storage.Message) error { fsmInstance, err := c.getFSMInstance(message.DkgRoundID) if err != nil { @@ -122,9 +133,10 @@ func (c *Client) ProcessMessage(message storage.Message) error { switch resp.State { // if the new state is waiting for RPC to airgapped machine case - dkgFSM.StateDkgCommitsAwaitConfirmations, - dkgFSM.StateDkgDealsAwaitConfirmations, - dkgFSM.StateDkgResponsesAwaitConfirmations: + spf.StateAwaitParticipantsConfirmations, + dpf.StateDkgCommitsAwaitConfirmations, + dpf.StateDkgDealsAwaitConfirmations, + dpf.StateDkgResponsesAwaitConfirmations: bz, err := json.Marshal(resp.Data) if err != nil { return fmt.Errorf("failed to marshal FSM response: %w", err) diff --git a/client/client_test.go b/client/client_test.go index 115daed..e8e36cc 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -31,6 +31,7 @@ func TestClient_ProcessMessage(t *testing.T) { ) defer ctrl.Finish() + userName := "user_name" dkgRoundID := "dkg_round_id" state := clientMocks.NewMockState(ctrl) keyStore := clientMocks.NewMockKeyStore(ctrl) @@ -38,14 +39,11 @@ func TestClient_ProcessMessage(t *testing.T) { 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) + keyStore.EXPECT().LoadKeys(userName, "").Times(1).Return(testClientKeyPair, nil) clt, err := client.NewClient( ctx, - "test_client", + userName, state, stg, keyStore, @@ -53,13 +51,16 @@ func TestClient_ProcessMessage(t *testing.T) { ) req.NoError(err) - t.Run("test_process_init_dkg", func(t *testing.T) { - senderUserName := "sender_username" + t.Run("test_process_dkg_init", func(t *testing.T) { + fsm, err := state_machines.Create(dkgRoundID) + state.EXPECT().LoadFSM(dkgRoundID).Times(1).Return(fsm, true, nil) + senderKeyPair := client.NewKeyPair() + senderAddr := senderKeyPair.GetAddr() messageData := requests.SignatureProposalParticipantsListRequest{ Participants: []*requests.SignatureProposalParticipantsEntry{ { - Addr: senderUserName, + Addr: senderAddr, PubKey: senderKeyPair.Pub, DkgPubKey: make([]byte, 128), }, @@ -91,12 +92,13 @@ func TestClient_ProcessMessage(t *testing.T) { Offset: 1, Event: string(spf.EventInitProposal), Data: messageDataBz, - SenderAddr: senderUserName, + SenderAddr: senderAddr, } 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) + state.EXPECT().PutOperation(gomock.Any()).Times(1).Return(nil) err = clt.ProcessMessage(message) req.NoError(err) diff --git a/client/keystore.go b/client/keystore.go index 4f25a31..822cfcd 100644 --- a/client/keystore.go +++ b/client/keystore.go @@ -14,6 +14,7 @@ const ( ) type KeyStore interface { + PutKeys(username string, keyPair *KeyPair) error LoadKeys(userName, password string) (*KeyPair, error) } @@ -34,9 +35,7 @@ func NewLevelDBKeyStore(username, keystorePath string) (KeyStore, error) { } if _, err := keystore.keystoreDb.Get([]byte(secretsKey), nil); err != nil { - if err := keystore.initJsonKey(secretsKey, map[string]*KeyPair{ - username: NewKeyPair(), - }); err != nil { + if err := keystore.initJsonKey(secretsKey, map[string]*KeyPair{}); err != nil { return nil, fmt.Errorf("failed to init %s storage: %w", operationsKey, err) } } @@ -44,6 +43,32 @@ func NewLevelDBKeyStore(username, keystorePath string) (KeyStore, error) { return keystore, nil } +func (s *LevelDBKeyStore) PutKeys(username string, keyPair *KeyPair) error { + bz, err := s.keystoreDb.Get([]byte(secretsKey), nil) + if err != nil { + return fmt.Errorf("failed to read keystore: %w", err) + } + + var keyPairs = map[string]*KeyPair{} + if err := json.Unmarshal(bz, &keyPairs); err != nil { + return fmt.Errorf("failed to unmarshak key pairs: %w", err) + } + + keyPairs[username] = keyPair + + keyPairsBz, err := json.Marshal(keyPairs) + if err != nil { + return fmt.Errorf("failed to marshal key pair: %w", err) + } + + err = s.keystoreDb.Put([]byte(secretsKey), keyPairsBz, nil) + if err != nil { + return fmt.Errorf("failed to put key pairs: %w", err) + } + + return nil +} + func (s *LevelDBKeyStore) LoadKeys(userName, password string) (*KeyPair, error) { bz, err := s.keystoreDb.Get([]byte(secretsKey), nil) if err != nil { @@ -65,11 +90,11 @@ func (s *LevelDBKeyStore) LoadKeys(userName, password string) (*KeyPair, error) func (s *LevelDBKeyStore) initJsonKey(key string, data interface{}) error { if _, err := s.keystoreDb.Get([]byte(key), nil); err != nil { - operationsBz, err := json.Marshal(data) + dataBz, 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) + err = s.keystoreDb.Put([]byte(key), dataBz, nil) if err != nil { return fmt.Errorf("failed to init state: %w", err) } @@ -84,7 +109,6 @@ type KeyPair struct { } func NewKeyPair() *KeyPair { - // TODO: implement proper generation. pub, priv, _ := ed25519.GenerateKey(nil) return &KeyPair{ Pub: pub, diff --git a/main.go b/main.go index 697894a..4353091 100644 --- a/main.go +++ b/main.go @@ -2,10 +2,18 @@ package main import ( "context" + "crypto/ed25519" + "crypto/md5" + "encoding/json" "fmt" _ "image/jpeg" "log" "sync" + "time" + + spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm" + "github.com/depools/dc4bc/fsm/types/requests" + "github.com/google/uuid" "github.com/depools/dc4bc/qr" "github.com/depools/dc4bc/storage" @@ -24,9 +32,16 @@ type Transport struct { nodes []*client.Client } +type node struct { + client *client.Client + keyPair *client.KeyPair +} + func main() { - var transport = &Transport{} var numNodes = 4 + var threshold = 3 + var storagePath = "/tmp/dc4bc_storage" + var nodes = make([]*node, 4) for nodeID := 0; nodeID < numNodes; nodeID++ { var ctx = context.Background() var userName = fmt.Sprintf("node_%d", nodeID) @@ -35,7 +50,7 @@ func main() { log.Fatalf("node %d failed to init state: %v\n", nodeID, err) } - stg, err := storage.NewFileStorage("/tmp/dc4bc_storage") + stg, err := storage.NewFileStorage(storagePath) if err != nil { log.Fatalf("node %d failed to init storage: %v\n", nodeID, err) } @@ -45,10 +60,14 @@ func main() { log.Fatalf("Failed to init key store: %v", err) } + keyPair := client.NewKeyPair() + if err := keyStore.PutKeys(userName, keyPair); err != nil { + log.Fatalf("Failed to PutKeys: %v\n", err) + } + clt, err := client.NewClient( ctx, userName, - nil, state, stg, keyStore, @@ -57,94 +76,63 @@ func main() { if err != nil { log.Fatalf("node %d failed to init client: %v\n", nodeID, err) } - transport.nodes = append(transport.nodes, clt) + + nodes[nodeID] = &node{ + client: clt, + keyPair: keyPair, + } } - for nodeID, node := range transport.nodes { - go func(nodeID int, node *client.Client) { - if err := node.StartHTTPServer(fmt.Sprintf("localhost:808%d", nodeID)); err != nil { - log.Fatalf("client %d http server failed: %v\n", nodeID, err) - } - }(nodeID, node) - + // Each node starts to Poll(). + for nodeID, node := range nodes { go func(nodeID int, node *client.Client) { if err := node.Poll(); err != nil { log.Fatalf("client %d poller failed: %v\n", nodeID, err) } - }(nodeID, node) + }(nodeID, node.client) log.Printf("client %d started...\n", nodeID) } + stg, err := storage.NewFileStorage(storagePath) + if err != nil { + log.Fatalf("main namespace failed to init storage: %v\n", err) + } + + // Node1 tells other participants to start DKG. + var participants []*requests.SignatureProposalParticipantsEntry + for _, node := range nodes { + participants = append(participants, &requests.SignatureProposalParticipantsEntry{ + Addr: node.client.GetAddr(), + PubKey: node.client.GetPubKey(), + DkgPubKey: make([]byte, 128), // TODO: Use a real one. + }) + } + messageData := requests.SignatureProposalParticipantsListRequest{ + Participants: participants, + SigningThreshold: threshold, + CreatedAt: time.Now(), + } + messageDataBz, err := json.Marshal(messageData) + if err != nil { + log.Fatalf("failed to marshal SignatureProposalParticipantsListRequest: %v\n", err) + } + + dkgRoundID := md5.Sum(messageDataBz) + message := storage.Message{ + ID: uuid.New().String(), + DkgRoundID: string(dkgRoundID[:]), + Event: string(spf.EventInitProposal), + Data: messageDataBz, + SenderAddr: nodes[0].client.GetAddr(), + } + + message.Signature = ed25519.Sign(nodes[0].keyPair.Priv, message.Bytes()) + if _, err := stg.Send(message); err != nil { + log.Fatalf("Failed to send %+v to storage: %v\n", message, err) + } + var wg = sync.WaitGroup{} wg.Add(1) wg.Wait() } - -// // Participants broadcast PKs. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// transport.BroadcastPK(participantID, participant.GetPubKey()) -// wg.Done() -// }) -// -// // Participants init their DKGInstances. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// if err := participant.InitDKGInstance(threshold); err != nil { -// log.Fatalf("Failed to InitDKGInstance: %v", err) -// } -// wg.Done() -// }) -// -// // Participants broadcast their Commits. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// commits := participant.GetCommits() -// transport.BroadcastCommits(participantID, commits) -// wg.Done() -// }) -// -// // Participants broadcast their deal. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// deals, err := participant.GetDeals() -// if err != nil { -// log.Fatalf("failed to getDeals for participant %s: %v", participantID, err) -// } -// transport.BroadcastDeals(participantID, deals) -// wg.Done() -// }) -// -// // Participants broadcast their responses. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// responses, err := participant.ProcessDeals() -// if err != nil { -// log.Fatalf("failed to ProcessDeals for participant %s: %v", participantID, err) -// } -// transport.BroadcastResponses(participantID, responses) -// wg.Done() -// }) -// -// // Participants process their responses. -// runStep(transport, func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup) { -// if err := participant.ProcessResponses(); err != nil { -// log.Fatalf("failed to ProcessResponses for participant %s: %v", participantID, err) -// } -// wg.Done() -// }) -// -// for idx, node := range transport.nodes { -// if err := node.Reconstruct(); err != nil { -// fmt.Println("Node", idx, "is not ready:", err) -// } else { -// fmt.Println("Node", idx, "is ready") -// } -// } -//} -// -//func runStep(transport *Transport, cb func(participantID string, participant *dkglib.DKG, wg *sync.WaitGroup)) { -// var wg = &sync.WaitGroup{} -// for idx, node := range transport.nodes { -// wg.Add(1) -// n := node -// go cb(fmt.Sprintf("participant_%d", idx), n, wg) -// } -// wg.Wait() -//}