mirror of https://github.com/certusone/dc4bc.git
Merge pull request #61 from depools/feat/cli-show-fsm-state
Feat/cli show fsm state
This commit is contained in:
commit
200fb8a0c4
|
@ -411,3 +411,11 @@ func (c *BaseClient) verifyMessage(fsmInstance *state_machines.FSMInstance, mess
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *BaseClient) GetFSMDump(dkgID string) (*state_machines.FSMDump, error) {
|
||||
fsmInstance, err := c.getFSMInstance(dkgID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get FSM instance for DKG round ID %s: %w", dkgID, err)
|
||||
}
|
||||
return fsmInstance.FSMDump(), nil
|
||||
}
|
||||
|
|
|
@ -80,10 +80,48 @@ func (c *BaseClient) StartHTTPServer(listenAddr string) error {
|
|||
mux.HandleFunc("/startDKG", c.startDKGHandler)
|
||||
mux.HandleFunc("/proposeSignMessage", c.proposeSignDataHandler)
|
||||
|
||||
mux.HandleFunc("/getFSMDump", c.getFSMDumpHandler)
|
||||
mux.HandleFunc("/getFSMList", c.getFSMList)
|
||||
|
||||
c.Logger.Log("Starting HTTP server on address: %s", listenAddr)
|
||||
return http.ListenAndServe(listenAddr, mux)
|
||||
}
|
||||
|
||||
func (c *BaseClient) getFSMDumpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
||||
return
|
||||
}
|
||||
dump, err := c.GetFSMDump(r.URL.Query().Get("dkgID"))
|
||||
if err != nil {
|
||||
errorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
successResponse(w, dump)
|
||||
}
|
||||
|
||||
func (c *BaseClient) getFSMList(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
||||
return
|
||||
}
|
||||
fsmInstances, err := c.state.GetAllFSM()
|
||||
if err != nil {
|
||||
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get all FSM instances: %v", err))
|
||||
return
|
||||
}
|
||||
fsmInstancesStates := make(map[string]string, len(fsmInstances))
|
||||
for k, v := range fsmInstances {
|
||||
state, err := v.State()
|
||||
if err != nil {
|
||||
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get FSM state: %v", err))
|
||||
return
|
||||
}
|
||||
fsmInstancesStates[k] = state.String()
|
||||
}
|
||||
successResponse(w, fsmInstancesStates)
|
||||
}
|
||||
|
||||
func (c *BaseClient) getUsernameHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
|
||||
|
|
|
@ -31,6 +31,7 @@ type State interface {
|
|||
|
||||
SaveFSM(dkgRoundID string, dump []byte) error
|
||||
LoadFSM(dkgRoundID string) (*state_machines.FSMInstance, bool, error)
|
||||
GetAllFSM() (map[string]*state_machines.FSMInstance, error)
|
||||
|
||||
PutOperation(operation *types.Operation) error
|
||||
DeleteOperation(operationID string) error
|
||||
|
@ -146,6 +147,27 @@ func (s *LevelDBState) SaveFSM(dkgRoundID string, dump []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBState) GetAllFSM() (map[string]*state_machines.FSMInstance, error) {
|
||||
bz, err := s.stateDb.Get([]byte(fsmStateKey), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get FSM instances: %w", err)
|
||||
}
|
||||
var fsmInstancesBz = map[string][]byte{}
|
||||
if len(bz) > 0 {
|
||||
if err := json.Unmarshal(bz, &fsmInstancesBz); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal FSM instances: %w", err)
|
||||
}
|
||||
}
|
||||
fsmInstances := make(map[string]*state_machines.FSMInstance, len(fsmInstancesBz))
|
||||
for k, v := range fsmInstancesBz {
|
||||
fsmInstances[k], err = state_machines.FromDump(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to restore FSM instance from dump: %w", err)
|
||||
}
|
||||
}
|
||||
return fsmInstances, nil
|
||||
}
|
||||
|
||||
func (s *LevelDBState) LoadFSM(dkgRoundID string) (*state_machines.FSMInstance, bool, error) {
|
||||
bz, err := s.stateDb.Get([]byte(fsmStateKey), nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,11 +7,13 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/state_machines"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
|
@ -56,6 +58,8 @@ func main() {
|
|||
getHashOfStartDKGCommand(),
|
||||
getSignaturesCommand(),
|
||||
getSignatureCommand(),
|
||||
getFSMStatusCommand(),
|
||||
getFSMListCommand(),
|
||||
)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("Failed to execute root command: %v", err)
|
||||
|
@ -518,3 +522,117 @@ func proposeSignMessageCommand() *cobra.Command {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getFSMDumpRequest(host string, dkgID string) (*FSMDumpResponse, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/getFSMDump?dkgID=%s", host, dkgID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get FSM dump: %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 FSMDumpResponse
|
||||
if err = json.Unmarshal(responseBody, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func getFSMStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "show_fsm_status [dkg_id]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "shows the current status of FSM",
|
||||
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)
|
||||
}
|
||||
|
||||
fsmDumpResponse, err := getFSMDumpRequest(listenAddr, args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get FSM dump: %w", err)
|
||||
}
|
||||
if fsmDumpResponse.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to get FSM dump: %v", fsmDumpResponse.ErrorMessage)
|
||||
}
|
||||
dump := fsmDumpResponse.Result
|
||||
|
||||
fmt.Printf("FSM current status is %s\n", dump.State)
|
||||
|
||||
quorum := make(map[int]state_machines.Participant)
|
||||
if strings.HasPrefix(string(dump.State), "state_signing") {
|
||||
for k, v := range dump.Payload.SigningProposalPayload.Quorum {
|
||||
quorum[k] = v
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(string(dump.State), "state_dkg") {
|
||||
for k, v := range dump.Payload.DKGProposalPayload.Quorum {
|
||||
quorum[k] = v
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(string(dump.State), "state_sig") {
|
||||
for k, v := range dump.Payload.SignatureProposalPayload.Quorum {
|
||||
quorum[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
waiting := make([]string, 0)
|
||||
confirmed := make([]string, 0)
|
||||
failed := make([]string, 0)
|
||||
|
||||
for _, p := range quorum {
|
||||
if strings.Contains(p.GetStatus().String(), "Await") {
|
||||
waiting = append(waiting, p.GetAddr())
|
||||
}
|
||||
if strings.Contains(p.GetStatus().String(), "Error") {
|
||||
failed = append(failed, p.GetAddr())
|
||||
}
|
||||
if strings.Contains(p.GetStatus().String(), "Confirmed") {
|
||||
confirmed = append(confirmed, p.GetAddr())
|
||||
}
|
||||
}
|
||||
|
||||
if len(waiting) > 0 {
|
||||
fmt.Printf("Waiting for a data from: %s\n", strings.Join(waiting, ", "))
|
||||
}
|
||||
if len(confirmed) > 0 {
|
||||
fmt.Printf("Received a data from: %s\n", strings.Join(confirmed, ", "))
|
||||
}
|
||||
if len(failed) > 0 {
|
||||
fmt.Printf("Participants who got some error during a process: %s\n", strings.Join(waiting, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getFSMListCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get_fsm_list",
|
||||
Short: "returns a list of all FSMs served by the client",
|
||||
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)
|
||||
}
|
||||
|
||||
resp, err := rawGetRequest(fmt.Sprintf("http://%s/getFSMList", listenAddr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make HTTP request to get FSM list: %w", err)
|
||||
}
|
||||
if resp.ErrorMessage != "" {
|
||||
return fmt.Errorf("failed to make HTTP request to get FSM list: %v", resp.ErrorMessage)
|
||||
}
|
||||
fsms := resp.Result.(map[string]interface{})
|
||||
for dkgID, state := range fsms {
|
||||
fmt.Printf("DKG ID: %s - FSM state: %s\n", dkgID, state.(string))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/depools/dc4bc/client/types"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines"
|
||||
"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/state_machines/signing_proposal_fsm"
|
||||
|
@ -32,6 +33,11 @@ type OperationsResponse struct {
|
|||
Result map[string]*types.Operation `json:"result"`
|
||||
}
|
||||
|
||||
type FSMDumpResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result *state_machines.FSMDump `json:"result"`
|
||||
}
|
||||
|
||||
type SignaturesResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result map[string][]types.ReconstructedSignature `json:"result"`
|
||||
|
|
|
@ -5,6 +5,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type ParticipantStatus interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type ConfirmationParticipantStatus uint8
|
||||
|
||||
const (
|
||||
|
@ -47,6 +51,14 @@ type SignatureProposalParticipant struct {
|
|||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (sigP SignatureProposalParticipant) GetStatus() ParticipantStatus {
|
||||
return sigP.Status
|
||||
}
|
||||
|
||||
func (sigP SignatureProposalParticipant) GetAddr() string {
|
||||
return sigP.Addr
|
||||
}
|
||||
|
||||
func (c *SignatureConfirmation) IsExpired() bool {
|
||||
return c.ExpiresAt.Before(c.UpdatedAt)
|
||||
}
|
||||
|
@ -86,6 +98,14 @@ type DKGProposalParticipant struct {
|
|||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (dkgP DKGProposalParticipant) GetStatus() ParticipantStatus {
|
||||
return dkgP.Status
|
||||
}
|
||||
|
||||
func (dkgP DKGProposalParticipant) GetAddr() string {
|
||||
return dkgP.Addr
|
||||
}
|
||||
|
||||
type DKGProposalQuorum map[int]*DKGProposalParticipant
|
||||
|
||||
type DKGConfirmation struct {
|
||||
|
@ -190,3 +210,11 @@ type SigningProposalParticipant struct {
|
|||
Error error
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (signingP SigningProposalParticipant) GetStatus() ParticipantStatus {
|
||||
return signingP.Status
|
||||
}
|
||||
|
||||
func (signingP SigningProposalParticipant) GetAddr() string {
|
||||
return signingP.Addr
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ type FSMInstance struct {
|
|||
dump *FSMDump
|
||||
}
|
||||
|
||||
type Participant interface {
|
||||
GetStatus() internal.ParticipantStatus
|
||||
GetAddr() string
|
||||
}
|
||||
|
||||
// Create new fsm with unique id
|
||||
// transactionId required for unique identify dump
|
||||
func Create(dkgID string) (*FSMInstance, error) {
|
||||
|
@ -176,6 +181,10 @@ func (i *FSMInstance) Dump() ([]byte, error) {
|
|||
return i.dump.Marshal()
|
||||
}
|
||||
|
||||
func (i *FSMInstance) FSMDump() *FSMDump {
|
||||
return i.dump
|
||||
}
|
||||
|
||||
// TODO: Add encryption
|
||||
func (d *FSMDump) Marshal() ([]byte, error) {
|
||||
return json.Marshal(d)
|
||||
|
|
|
@ -93,6 +93,21 @@ func (mr *MockStateMockRecorder) LoadFSM(dkgRoundID interface{}) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadFSM", reflect.TypeOf((*MockState)(nil).LoadFSM), dkgRoundID)
|
||||
}
|
||||
|
||||
// GetAllFSM mocks base method
|
||||
func (m *MockState) GetAllFSM() (map[string]*state_machines.FSMInstance, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAllFSM")
|
||||
ret0, _ := ret[0].(map[string]*state_machines.FSMInstance)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAllFSM indicates an expected call of GetAllFSM
|
||||
func (mr *MockStateMockRecorder) GetAllFSM() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFSM", reflect.TypeOf((*MockState)(nil).GetAllFSM))
|
||||
}
|
||||
|
||||
// PutOperation mocks base method
|
||||
func (m *MockState) PutOperation(operation *types.Operation) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
Loading…
Reference in New Issue