mirror of https://github.com/certusone/dc4bc.git
WIP
This commit is contained in:
parent
3856a364d8
commit
9617d92797
|
@ -40,7 +40,6 @@ type Poller interface {
|
|||
ProcessMessage(message storage.Message) error
|
||||
GetOperations() (map[string]*types.Operation, error)
|
||||
GetOperationQRPath(operationID string) ([]string, error)
|
||||
ReadProcessedOperation() error
|
||||
StartHTTPServer(listenAddr string) error
|
||||
GetLogger() *logger
|
||||
}
|
||||
|
@ -278,23 +277,6 @@ func (c *Client) GetOperationQRPath(operationID string) ([]string, error) {
|
|||
return qrs, nil
|
||||
}
|
||||
|
||||
// ReadProcessedOperation reads the processed operation from camera, checks that
|
||||
// the processed operation has its unprocessed counterpart in our state,
|
||||
// posts a Message to the storage and deletes the operation from our state.
|
||||
func (c *Client) ReadProcessedOperation() error {
|
||||
bz, err := qr.ReadDataFromQRChunks(c.qrProcessor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to ReadQR: %s", err)
|
||||
}
|
||||
|
||||
var operation types.Operation
|
||||
if err = json.Unmarshal(bz, &operation); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal processed operation")
|
||||
}
|
||||
|
||||
return c.handleProcessedOperation(operation)
|
||||
}
|
||||
|
||||
func (c *Client) handleProcessedOperation(operation types.Operation) error {
|
||||
storedOperation, err := c.state.GetOperationByID(operation.ID)
|
||||
if err != nil {
|
||||
|
|
|
@ -203,54 +203,3 @@ func TestClient_GetOperationQRPath(t *testing.T) {
|
|||
req.NoError(err)
|
||||
req.Equal(expectedQrPath, qrPath)
|
||||
}
|
||||
|
||||
func TestClient_ReadProcessedOperation(t *testing.T) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
req = require.New(t)
|
||||
ctrl = gomock.NewController(t)
|
||||
)
|
||||
defer ctrl.Finish()
|
||||
|
||||
userName := "test_client"
|
||||
|
||||
keyStore := clientMocks.NewMockKeyStore(ctrl)
|
||||
testClientKeyPair := client.NewKeyPair()
|
||||
keyStore.EXPECT().LoadKeys(userName, "").Times(1).Return(testClientKeyPair, nil)
|
||||
|
||||
state := clientMocks.NewMockState(ctrl)
|
||||
stg := storageMocks.NewMockStorage(ctrl)
|
||||
qrProcessor := qrMocks.NewMockProcessor(ctrl)
|
||||
|
||||
clt, err := client.NewClient(
|
||||
ctx,
|
||||
userName,
|
||||
state,
|
||||
stg,
|
||||
keyStore,
|
||||
qrProcessor,
|
||||
)
|
||||
req.NoError(err)
|
||||
|
||||
operation := &types.Operation{
|
||||
ID: "operation_id",
|
||||
Type: types.DKGCommits,
|
||||
Payload: []byte("operation_payload"),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
processedOperation := &types.Operation{
|
||||
ID: "operation_id",
|
||||
Type: types.DKGCommits,
|
||||
Payload: []byte("operation_payload"),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
processedOperationBz, err := json.Marshal(processedOperation)
|
||||
req.NoError(err)
|
||||
|
||||
qrProcessor.EXPECT().ReadQR().Return(processedOperationBz, nil).Times(1)
|
||||
state.EXPECT().GetOperationByID(processedOperation.ID).Times(1).Return(operation, nil)
|
||||
state.EXPECT().DeleteOperation(processedOperation.ID).Times(1)
|
||||
|
||||
err = clt.ReadProcessedOperation()
|
||||
req.NoError(err)
|
||||
}
|
||||
|
|
|
@ -62,7 +62,6 @@ func (c *Client) StartHTTPServer(listenAddr string) error {
|
|||
mux.HandleFunc("/sendMessage", c.sendMessageHandler)
|
||||
mux.HandleFunc("/getOperations", c.getOperationsHandler)
|
||||
mux.HandleFunc("/getOperationQRPath", c.getOperationQRPathHandler)
|
||||
mux.HandleFunc("/readProcessedOperationFromCamera", c.readProcessedOperationFromCameraHandler)
|
||||
|
||||
mux.HandleFunc("/readProcessedOperation", c.readProcessedOperationFromBodyHandler)
|
||||
mux.HandleFunc("/getOperationQR", c.getOperationQRToBodyHandler)
|
||||
|
@ -156,21 +155,6 @@ func (c *Client) getOperationQRToBodyHandler(w http.ResponseWriter, r *http.Requ
|
|||
rawResponse(w, encodedData)
|
||||
}
|
||||
|
||||
func (c *Client) readProcessedOperationFromCameraHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.ReadProcessedOperation(); err != nil {
|
||||
errorResponse(w, http.StatusInternalServerError,
|
||||
fmt.Sprintf("failed to handle processed operation from camera path: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
successResponse(w, "ok")
|
||||
}
|
||||
|
||||
func (c *Client) readProcessedOperationFromBodyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/client/types"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type OperationsResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result map[string]*types.Operation `json:"result"`
|
||||
}
|
||||
|
||||
func getOperations(host string) (*OperationsResponse, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/getOperations", host))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get operations for node %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body %w", err)
|
||||
}
|
||||
|
||||
var response OperationsResponse
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func getOperationsCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get_operations",
|
||||
Short: "returns all operations that should be processed on the airgapped machine",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
operations, err := getOperations(listenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get operations: %w", err)
|
||||
}
|
||||
if operations.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to get operations: %s", operations.ErrorMessage)
|
||||
}
|
||||
for _, operation := range operations.Result {
|
||||
fmt.Printf("Operation ID: %s\n", operation.ID)
|
||||
operationBz, err := json.Marshal(operation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal operation: %w", err)
|
||||
}
|
||||
fmt.Printf("Operation: %s\n", string(operationBz))
|
||||
fmt.Println("-----------------------------------------------------")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/client"
|
||||
"github.com/depools/dc4bc/client/types"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OperationsResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result map[string]*types.Operation `json:"result"`
|
||||
}
|
||||
|
||||
func getOperationsRequest(host string) (*OperationsResponse, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/getOperations", host))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get operations: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body: %w", err)
|
||||
}
|
||||
|
||||
var response OperationsResponse
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func getOperationsCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get_operations",
|
||||
Short: "returns all operations that should be processed on the airgapped machine",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
operations, err := getOperationsRequest(listenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get operations: %w", err)
|
||||
}
|
||||
if operations.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to get operations: %s", operations.ErrorMessage)
|
||||
}
|
||||
for _, operation := range operations.Result {
|
||||
fmt.Printf("Operation ID: %s\n", operation.ID)
|
||||
operationBz, err := json.Marshal(operation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal operation: %w", err)
|
||||
}
|
||||
fmt.Printf("Operation: %s\n", string(operationBz))
|
||||
fmt.Println("-----------------------------------------------------")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type OperationQRPathsResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result []string `json:"result"`
|
||||
}
|
||||
|
||||
func getOperationsQRPathsRequest(host string, operationID string) (*OperationQRPathsResponse, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/getOperationQRPath?operationID=%s", host, operationID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get operation: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body: %w", err)
|
||||
}
|
||||
|
||||
var response OperationQRPathsResponse
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func getOperationQRPathCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get_operation_qr [operationID]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "returns path to QR codes which contains the operation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
operationID := args[0]
|
||||
operations, err := getOperationsQRPathsRequest(listenAddr, operationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get operations: %w", err)
|
||||
}
|
||||
if operations.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to get operations: %s", operations.ErrorMessage)
|
||||
}
|
||||
fmt.Printf("List of paths to QR codes for operation %s:\n", operationID)
|
||||
for idx, path := range operations.Result {
|
||||
fmt.Printf("%d) QR code: %s\n", idx, path)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func rawGetRequest(url string) (*client.Response, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get operations for node %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body %w", err)
|
||||
}
|
||||
|
||||
var response client.Response
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func rawPostRequest(url string, contentType string, data []byte) (*client.Response, error) {
|
||||
resp, err := http.Post(url,
|
||||
contentType, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send HTTP request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read body %w", err)
|
||||
}
|
||||
|
||||
var response client.Response
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func readOperationFromCameraCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "read_from_camera",
|
||||
Short: "opens the camera and reads QR codes which should contain a processed operation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
response, err := rawGetRequest(fmt.Sprintf("http://%s/readProcessedOperationFromCamera", listenAddr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get operations: %w", err)
|
||||
}
|
||||
if response.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to read operation from camera: %s", response.ErrorMessage)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func startDKGCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "startDKG [participants count] [threshold]",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Short: "sends a propose message to start a DKG process",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
participantsCount, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get participants count: %w", err)
|
||||
}
|
||||
if participantsCount < 0 {
|
||||
return fmt.Errorf("invalid number of participants: %d", participantsCount)
|
||||
}
|
||||
|
||||
threshold, err := strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get threshold: %w", err)
|
||||
}
|
||||
if participantsCount < 0 || threshold > participantsCount {
|
||||
return fmt.Errorf("invalid threshold: %d", threshold)
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
var participants []*requests.SignatureProposalParticipantsEntry
|
||||
for i := 0; i < participantsCount; i++ {
|
||||
p := &requests.SignatureProposalParticipantsEntry{}
|
||||
fmt.Printf("Enter a necessary data for participant %d:\n", i)
|
||||
fmt.Printf("Enter address: ")
|
||||
p.Addr, err = reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read addr: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Enter pubkey (base64): ")
|
||||
pubKeyStr, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read pubKey: %w", err)
|
||||
}
|
||||
p.PubKey, err = base64.StdEncoding.DecodeString(pubKeyStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode pubKey: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Enter DKGPubKey (base64): ")
|
||||
DKGPubKeyStr, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read DKGPubKey: %w", err)
|
||||
}
|
||||
p.DkgPubKey, err = base64.StdEncoding.DecodeString(DKGPubKeyStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode DKGPubKey: %w", err)
|
||||
}
|
||||
participants = append(participants, p)
|
||||
}
|
||||
|
||||
messageData := requests.SignatureProposalParticipantsListRequest{
|
||||
Participants: participants,
|
||||
SigningThreshold: threshold,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
messageDataBz, err := json.Marshal(messageData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal SignatureProposalParticipantsListRequest: %v\n", err)
|
||||
}
|
||||
resp, err := rawPostRequest(fmt.Sprintf("http://%s/startDKG", listenAddr),
|
||||
"application/json", messageDataBz)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make HTTP request to start DKG: %w", err)
|
||||
}
|
||||
if resp.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to make HTTP request to start DKG: %w", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
flagUserName = "username"
|
||||
flagListenAddr = "listen_addr"
|
||||
flagStateDBDSN = "state_dbdsn"
|
||||
flagStorageDBDSN = "storage_dbdsn"
|
||||
flagStoreDBDSN = "key_store_dbdsn"
|
||||
)
|
||||
|
||||
func init() {
|
||||
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(flagStorageDBDSN, "./dc4bc_file_storage", "Storage DBDSN")
|
||||
rootCmd.PersistentFlags().String(flagStoreDBDSN, "./dc4bc_key_store", "Key Store DBDSN")
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "dc4bc_cli",
|
||||
Short: "dc4bc client cli utilities implementation",
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootCmd.AddCommand(
|
||||
getOperationsCommand(),
|
||||
getOperationQRPathCommand(),
|
||||
readOperationFromCameraCommand(),
|
||||
startDKGCommand(),
|
||||
)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("Failed to execute root command: %v", err)
|
||||
}
|
||||
}
|
|
@ -140,15 +140,14 @@ func startClientCommand() *cobra.Command {
|
|||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "dc4bc_client",
|
||||
Short: "dc4bc client implementation",
|
||||
Use: "dc4bc_d",
|
||||
Short: "dc4bc client daemon implementation",
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootCmd.AddCommand(
|
||||
startClientCommand(),
|
||||
genKeyPairCommand(),
|
||||
getOperationsCommand(),
|
||||
)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("Failed to execute root command: %v", err)
|
Loading…
Reference in New Issue