mirror of https://github.com/certusone/dc4bc.git
Merge branch 'feat/client-tests' into feat/airgapped-client-tests
This commit is contained in:
commit
bfcd7d6eb4
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
|
@ -79,7 +80,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)
|
||||
|
@ -90,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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
148
main.go
148
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()
|
||||
//}
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue