2020-07-30 08:09:13 -07:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2020-09-04 08:35:22 -07:00
|
|
|
"crypto/md5"
|
|
|
|
"encoding/hex"
|
2020-07-30 08:09:13 -07:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2020-10-05 08:00:54 -07:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
|
2020-10-05 08:08:55 -07:00
|
|
|
"time"
|
|
|
|
|
2020-08-20 08:08:11 -07:00
|
|
|
"github.com/depools/dc4bc/client/types"
|
2020-09-04 08:35:22 -07:00
|
|
|
"github.com/depools/dc4bc/fsm/fsm"
|
|
|
|
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
|
|
|
sif "github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
|
2020-10-02 04:49:32 -07:00
|
|
|
"github.com/depools/dc4bc/fsm/types/requests"
|
2020-09-04 08:35:22 -07:00
|
|
|
"github.com/google/uuid"
|
2020-08-03 23:34:10 -07:00
|
|
|
|
|
|
|
"github.com/depools/dc4bc/qr"
|
|
|
|
"github.com/depools/dc4bc/storage"
|
2020-07-30 08:09:13 -07:00
|
|
|
)
|
|
|
|
|
2020-09-03 10:09:25 -07:00
|
|
|
type Response struct {
|
2020-09-04 08:35:22 -07:00
|
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
|
|
Result interface{} `json:"result"`
|
2020-09-03 10:09:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func rawResponse(w http.ResponseWriter, response []byte) {
|
|
|
|
if _, err := w.Write(response); err != nil {
|
|
|
|
panic(fmt.Sprintf("failed to write response: %v", err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func errorResponse(w http.ResponseWriter, statusCode int, error string) {
|
2020-07-30 08:09:13 -07:00
|
|
|
w.WriteHeader(statusCode)
|
2020-09-03 10:09:25 -07:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
resp := Response{ErrorMessage: error}
|
|
|
|
respBz, err := json.Marshal(resp)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Failed to marshal response: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := w.Write(respBz); err != nil {
|
2020-07-30 08:09:13 -07:00
|
|
|
panic(fmt.Sprintf("failed to write response: %v", err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 10:09:25 -07:00
|
|
|
func successResponse(w http.ResponseWriter, response interface{}) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
resp := Response{Result: response}
|
|
|
|
respBz, err := json.Marshal(resp)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Failed to marshal response: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err := w.Write(respBz); err != nil {
|
2020-07-30 08:09:13 -07:00
|
|
|
panic(fmt.Sprintf("failed to write response: %v", err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) StartHTTPServer(listenAddr string) error {
|
2020-08-09 14:37:53 -07:00
|
|
|
mux := http.NewServeMux()
|
2020-09-10 04:51:53 -07:00
|
|
|
|
2020-09-15 06:45:35 -07:00
|
|
|
mux.HandleFunc("/getUsername", c.getUsernameHandler)
|
2020-09-10 04:51:53 -07:00
|
|
|
mux.HandleFunc("/getPubKey", c.getPubkeyHandler)
|
|
|
|
|
2020-08-09 14:37:53 -07:00
|
|
|
mux.HandleFunc("/sendMessage", c.sendMessageHandler)
|
|
|
|
mux.HandleFunc("/getOperations", c.getOperationsHandler)
|
|
|
|
mux.HandleFunc("/getOperationQRPath", c.getOperationQRPathHandler)
|
|
|
|
|
2020-10-05 03:58:52 -07:00
|
|
|
mux.HandleFunc("/getSignatures", c.getSignaturesHandler)
|
|
|
|
mux.HandleFunc("/getSignatureByDataHash", c.getSignatureByDataHashHandler)
|
|
|
|
|
2020-08-09 14:37:53 -07:00
|
|
|
mux.HandleFunc("/getOperationQR", c.getOperationQRToBodyHandler)
|
2020-09-04 09:40:15 -07:00
|
|
|
mux.HandleFunc("/handleProcessedOperationJSON", c.handleJSONOperationHandler)
|
2020-09-30 02:45:24 -07:00
|
|
|
mux.HandleFunc("/getOperation", c.getOperationHandler)
|
2020-08-09 14:37:53 -07:00
|
|
|
|
2020-09-04 08:35:22 -07:00
|
|
|
mux.HandleFunc("/startDKG", c.startDKGHandler)
|
|
|
|
mux.HandleFunc("/proposeSignMessage", c.proposeSignDataHandler)
|
2020-08-09 14:37:53 -07:00
|
|
|
|
2020-09-04 08:35:22 -07:00
|
|
|
c.Logger.Log("Starting HTTP server on address: %s", listenAddr)
|
2020-08-09 14:37:53 -07:00
|
|
|
return http.ListenAndServe(listenAddr, mux)
|
2020-07-30 08:09:13 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getUsernameHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-10 04:51:53 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
2020-09-15 06:45:35 -07:00
|
|
|
successResponse(w, c.GetUsername())
|
2020-09-10 04:51:53 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getPubkeyHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-10 04:51:53 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
successResponse(w, c.GetPubKey())
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) sendMessageHandler(w http.ResponseWriter, r *http.Request) {
|
2020-07-30 08:09:13 -07:00
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reqBytes, err := ioutil.ReadAll(r.Body)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to read request body: %v", err))
|
|
|
|
return
|
|
|
|
}
|
2020-09-04 08:35:22 -07:00
|
|
|
defer r.Body.Close()
|
2020-07-30 08:09:13 -07:00
|
|
|
|
|
|
|
var msg storage.Message
|
|
|
|
if err = json.Unmarshal(reqBytes, &msg); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to unmarshal message: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = c.SendMessage(msg); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to send message to the storage: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-03 10:09:25 -07:00
|
|
|
successResponse(w, "ok")
|
2020-07-30 08:09:13 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getOperationsHandler(w http.ResponseWriter, r *http.Request) {
|
2020-07-30 08:09:13 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
operations, err := c.GetOperations()
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operations: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-03 10:09:25 -07:00
|
|
|
successResponse(w, operations)
|
2020-07-30 08:09:13 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:08:55 -07:00
|
|
|
func (c *BaseClient) getSignaturesHandler(w http.ResponseWriter, r *http.Request) {
|
2020-10-05 03:58:52 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
signatures, err := c.GetSignatures(r.URL.Query().Get("dkgID"))
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get signatures: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
successResponse(w, signatures)
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:08:55 -07:00
|
|
|
func (c *BaseClient) getSignatureByDataHashHandler(w http.ResponseWriter, r *http.Request) {
|
2020-10-05 03:58:52 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
signature, err := c.GetSignatureByDataHash(r.URL.Query().Get("dkgID"), r.URL.Query().Get("hash"))
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get signature: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
successResponse(w, signature)
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getOperationQRPathHandler(w http.ResponseWriter, r *http.Request) {
|
2020-07-30 08:09:13 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
operationID := r.URL.Query().Get("operationID")
|
|
|
|
|
2020-09-01 08:06:37 -07:00
|
|
|
qrPaths, err := c.GetOperationQRPath(operationID)
|
2020-07-30 08:09:13 -07:00
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operation QR path: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-07 04:27:39 -07:00
|
|
|
successResponse(w, qrPaths)
|
2020-07-30 08:09:13 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getOperationHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-30 02:45:24 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
operationID := r.URL.Query().Get("operationID")
|
|
|
|
|
|
|
|
operation, err := c.getOperationJSON(operationID)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operation: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
successResponse(w, operation)
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) getOperationQRToBodyHandler(w http.ResponseWriter, r *http.Request) {
|
2020-07-31 07:55:47 -07:00
|
|
|
if r.Method != http.MethodGet {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
operationID := r.URL.Query().Get("operationID")
|
|
|
|
|
|
|
|
operationJSON, err := c.getOperationJSON(operationID)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operation in JSON: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedData, err := qr.EncodeQR(operationJSON)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to encode operation: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(encodedData)))
|
2020-09-03 10:09:25 -07:00
|
|
|
rawResponse(w, encodedData)
|
2020-07-31 07:55:47 -07:00
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) startDKGHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-04 08:35:22 -07:00
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reqBody, err := ioutil.ReadAll(r.Body)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to read body: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
dkgRoundID := md5.Sum(reqBody)
|
|
|
|
message, err := c.buildMessage(hex.EncodeToString(dkgRoundID[:]), spf.EventInitProposal, reqBody)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to build message: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = c.SendMessage(*message); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to send message: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
successResponse(w, "ok")
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) proposeSignDataHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-04 08:35:22 -07:00
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reqBody, err := ioutil.ReadAll(r.Body)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to read body: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
var req map[string][]byte
|
|
|
|
if err = json.Unmarshal(reqBody, &req); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to umarshal request: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-02 04:49:32 -07:00
|
|
|
fsmInstance, err := c.getFSMInstance(hex.EncodeToString(req["dkgID"]))
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get FSM instance: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
participantID, err := fsmInstance.GetIDByAddr(c.GetUsername())
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get participantID: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
messageDataSign := requests.SigningProposalStartRequest{
|
|
|
|
ParticipantId: participantID,
|
|
|
|
SrcPayload: req["data"],
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
}
|
|
|
|
messageDataSignBz, err := json.Marshal(messageDataSign)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to marshal SigningProposalStartRequest: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
message, err := c.buildMessage(hex.EncodeToString(req["dkgID"]), sif.EventSigningStart, messageDataSignBz)
|
2020-09-04 08:35:22 -07:00
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to build message: %v", err))
|
2020-10-02 04:49:32 -07:00
|
|
|
return
|
2020-09-04 08:35:22 -07:00
|
|
|
}
|
|
|
|
if err = c.SendMessage(*message); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to send message: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
successResponse(w, "ok")
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) handleJSONOperationHandler(w http.ResponseWriter, r *http.Request) {
|
2020-09-04 09:40:15 -07:00
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reqBody, err := ioutil.ReadAll(r.Body)
|
|
|
|
if err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to read body: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
var req types.Operation
|
|
|
|
if err = json.Unmarshal(reqBody, &req); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to umarshal request: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = c.handleProcessedOperation(req); err != nil {
|
|
|
|
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to handle processed operation: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
successResponse(w, "ok")
|
|
|
|
}
|
|
|
|
|
2020-10-05 08:00:54 -07:00
|
|
|
func (c *BaseClient) buildMessage(dkgRoundID string, event fsm.Event, data []byte) (*storage.Message, error) {
|
2020-09-04 08:35:22 -07:00
|
|
|
message := storage.Message{
|
|
|
|
ID: uuid.New().String(),
|
|
|
|
DkgRoundID: dkgRoundID,
|
|
|
|
Event: string(event),
|
|
|
|
Data: data,
|
2020-09-15 06:45:35 -07:00
|
|
|
SenderAddr: c.GetUsername(),
|
2020-09-04 08:35:22 -07:00
|
|
|
}
|
|
|
|
signature, err := c.signMessage(message.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to sign message: %w", err)
|
|
|
|
}
|
|
|
|
message.Signature = signature
|
|
|
|
return &message, nil
|
2020-07-31 07:55:47 -07:00
|
|
|
}
|