mirror of https://github.com/certusone/dc4bc.git
feat: fixes, tests, actions
This commit is contained in:
parent
7e3121d500
commit
897c15672a
|
@ -2,34 +2,44 @@ package main
|
|||
|
||||
import (
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/depools/dc4bc/fsm/state_machines"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tm := time.Now()
|
||||
fsmMachine, err := state_machines.Create("d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257")
|
||||
log.Println(fsmMachine, err)
|
||||
resp, dump, err := fsmMachine.Do(
|
||||
"event_proposal_init",
|
||||
signature_proposal_fsm.EventInitProposal,
|
||||
requests.SignatureProposalParticipantsListRequest{
|
||||
{
|
||||
"John Doe",
|
||||
[]byte("pubkey123123"),
|
||||
},
|
||||
{
|
||||
"Crypto Billy",
|
||||
[]byte("pubkey456456"),
|
||||
},
|
||||
{
|
||||
"Matt",
|
||||
[]byte("pubkey789789"),
|
||||
Participants: []*requests.SignatureProposalParticipantsEntry{
|
||||
{
|
||||
"John Doe",
|
||||
[]byte("pubkey123123"),
|
||||
},
|
||||
{
|
||||
"Crypto Billy",
|
||||
[]byte("pubkey456456"),
|
||||
},
|
||||
{
|
||||
"Matt",
|
||||
[]byte("pubkey789789"),
|
||||
},
|
||||
},
|
||||
CreatedAt: &tm,
|
||||
},
|
||||
)
|
||||
log.Println("Err", err)
|
||||
if err != nil {
|
||||
log.Println("Err", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Dump", string(dump))
|
||||
|
||||
processResponse(resp)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
fsmName = "dkg_proposal_fsm"
|
||||
FsmName = "dkg_proposal_fsm"
|
||||
|
||||
StateDkgInitial = signature_proposal_fsm.StateValidationCompleted
|
||||
|
||||
|
@ -82,7 +82,7 @@ func New() internal.DumpedMachineProvider {
|
|||
machine := &DKGProposalFSM{}
|
||||
|
||||
machine.FSM = fsm.MustNewFSM(
|
||||
fsmName,
|
||||
FsmName,
|
||||
StateDkgInitial,
|
||||
[]fsm.EventDesc{
|
||||
|
||||
|
|
|
@ -31,7 +31,11 @@ type SignatureProposalQuorum map[string]SignatureProposalParticipant
|
|||
type SignatureProposalParticipantStatus uint8
|
||||
|
||||
const (
|
||||
PubKeyConAwaitConfirmation DKGProposalParticipantStatus = iota
|
||||
SignatureConfirmationAwaitConfirmation DKGProposalParticipantStatus = iota
|
||||
SignatureConfirmationConfirmed
|
||||
SignatureConfirmationDeclined
|
||||
SignatureConfirmationError
|
||||
PubKeyConAwaitConfirmation
|
||||
PubKeyConfirmed
|
||||
PubKeyConfirmationError
|
||||
CommitAwaitConfirmation
|
||||
|
@ -68,6 +72,14 @@ type DKGProposalParticipantStatus uint8
|
|||
func (s DKGProposalParticipantStatus) String() string {
|
||||
var str = "undefined"
|
||||
switch s {
|
||||
case SignatureConfirmationAwaitConfirmation:
|
||||
str = "SignatureConfirmationAwaitConfirmation"
|
||||
case SignatureConfirmationConfirmed:
|
||||
str = "SignatureConfirmationConfirmed"
|
||||
case SignatureConfirmationDeclined:
|
||||
str = "SignatureConfirmationDeclined"
|
||||
case SignatureConfirmationError:
|
||||
str = "SignatureConfirmationError"
|
||||
case PubKeyConAwaitConfirmation:
|
||||
str = "PubKeyConAwaitConfirmation"
|
||||
case PubKeyConfirmed:
|
||||
|
|
|
@ -36,11 +36,11 @@ func init() {
|
|||
}
|
||||
|
||||
// Create new fsm with unique id
|
||||
// Transaction id required for unique identify dump
|
||||
func Create(tid string) (*FSMInstance, error) {
|
||||
// transactionId required for unique identify dump
|
||||
func Create(transactionId string) (*FSMInstance, error) {
|
||||
var err error
|
||||
i := &FSMInstance{}
|
||||
err = i.InitDump(tid)
|
||||
err = i.InitDump(transactionId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -56,9 +56,16 @@ func Create(tid string) (*FSMInstance, error) {
|
|||
func FromDump(data []byte) (*FSMInstance, error) {
|
||||
var err error
|
||||
|
||||
i := &FSMInstance{}
|
||||
if len(data) < 2 {
|
||||
return nil, errors.New("machine dump is empty")
|
||||
}
|
||||
|
||||
i := &FSMInstance{
|
||||
dump: &FSMDump{},
|
||||
}
|
||||
err = i.dump.Unmarshal(data)
|
||||
|
||||
// TODO: Add logger
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot read machine dump")
|
||||
}
|
||||
|
@ -87,21 +94,21 @@ func (i *FSMInstance) Do(event fsm.Event, args ...interface{}) (result *fsm.Resp
|
|||
return result, dump, err
|
||||
}
|
||||
|
||||
func (i *FSMInstance) InitDump(tid string) error {
|
||||
func (i *FSMInstance) InitDump(transactionId string) error {
|
||||
if i.dump != nil {
|
||||
return errors.New("dump already initialized")
|
||||
}
|
||||
|
||||
tid = strings.TrimSpace(tid)
|
||||
transactionId = strings.TrimSpace(transactionId)
|
||||
|
||||
if tid == "" {
|
||||
if transactionId == "" {
|
||||
return errors.New("empty transaction id")
|
||||
}
|
||||
|
||||
i.dump = &FSMDump{
|
||||
State: fsm.StateGlobalIdle,
|
||||
Payload: &internal.DumpedMachineStatePayload{
|
||||
TransactionId: tid,
|
||||
TransactionId: transactionId,
|
||||
ConfirmationProposalPayload: nil,
|
||||
DKGProposalPayload: nil,
|
||||
},
|
||||
|
@ -116,5 +123,9 @@ func (d *FSMDump) Marshal() ([]byte, error) {
|
|||
|
||||
// TODO: Add decryption
|
||||
func (d *FSMDump) Unmarshal(data []byte) error {
|
||||
if d == nil {
|
||||
return errors.New("dump struct is not initialized")
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, d)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
package state_machines
|
||||
|
||||
import (
|
||||
spf "github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
testTransactionId = "d8a928b2043db77e340b523547bf16cb4aa483f0645fe0a290ed1f20aab76257"
|
||||
)
|
||||
|
||||
var (
|
||||
tm = time.Now()
|
||||
testParticipantsListRequest = requests.SignatureProposalParticipantsListRequest{
|
||||
Participants: []*requests.SignatureProposalParticipantsEntry{
|
||||
{
|
||||
"User 1",
|
||||
[]byte("pubkey123123"),
|
||||
},
|
||||
{
|
||||
"User 2",
|
||||
[]byte("pubkey456456"),
|
||||
},
|
||||
{
|
||||
"User 3",
|
||||
[]byte("pubkey789789"),
|
||||
},
|
||||
},
|
||||
CreatedAt: &tm,
|
||||
}
|
||||
)
|
||||
|
||||
func TestCreate_Positive(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error")
|
||||
}
|
||||
|
||||
if testFSMInstance == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_Negative(t *testing.T) {
|
||||
_, err := Create("")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for empty {transactionId}")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Workflow(t *testing.T) {
|
||||
testFSMInstance, err := Create(testTransactionId)
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
|
||||
if testFSMInstance == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
|
||||
if testFSMInstance.machine.Name() != spf.FsmName {
|
||||
t.Fatalf("expected machine name {%s}", spf.FsmName)
|
||||
}
|
||||
|
||||
if testFSMInstance.machine.State() != spf.StateParticipantsConfirmationsInit {
|
||||
t.Fatalf("expected inital state {%s}", spf.StateParticipantsConfirmationsInit)
|
||||
}
|
||||
|
||||
fsmResponse, dump, err := testFSMInstance.Do(spf.EventInitProposal, testParticipantsListRequest)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error")
|
||||
}
|
||||
|
||||
if len(dump) == 0 {
|
||||
t.Fatalf("expected non zero dump, when executed without error")
|
||||
}
|
||||
|
||||
if fsmResponse == nil {
|
||||
t.Fatalf("expected {*fsm.FSMResponse}")
|
||||
}
|
||||
|
||||
if fsmResponse.State != spf.StateAwaitParticipantsConfirmations {
|
||||
t.Fatalf("expected state {%s}", spf.StateAwaitParticipantsConfirmations)
|
||||
}
|
||||
|
||||
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("expected response {SignatureProposalParticipantInvitationsResponse}")
|
||||
}
|
||||
|
||||
if len(testParticipantsListResponse) != len(testParticipantsListRequest.Participants) {
|
||||
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(testParticipantsListResponse))
|
||||
}
|
||||
|
||||
participantsMap := map[int]*responses.SignatureProposalParticipantInvitationEntry{}
|
||||
|
||||
for _, participant := range testParticipantsListResponse {
|
||||
if _, ok := participantsMap[participant.ParticipantId]; ok {
|
||||
t.Fatalf("expected unique {ParticipantId}")
|
||||
}
|
||||
|
||||
if participant.Title == "" {
|
||||
t.Fatalf("expected not empty {Title}")
|
||||
}
|
||||
|
||||
if participant.EncryptedInvitation == "" {
|
||||
t.Fatalf("expected not empty {EncryptedInvitation}")
|
||||
}
|
||||
|
||||
if participant.PubKeyFingerprint == "" {
|
||||
t.Fatalf("expected not empty {PubKeyFingerprint}")
|
||||
}
|
||||
|
||||
participantsMap[participant.ParticipantId] = participant
|
||||
}
|
||||
|
||||
tm = tm.Add(10 * time.Second)
|
||||
|
||||
testFSMInstance, err = FromDump(dump)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got {%s}", err)
|
||||
}
|
||||
|
||||
if testFSMInstance == nil {
|
||||
t.Fatalf("expected {*FSMInstance}")
|
||||
}
|
||||
|
||||
for _, participant := range participantsMap {
|
||||
response, _, err := testFSMInstance.Do(spf.EventConfirmProposal, requests.SignatureProposalParticipantRequest{
|
||||
PubKeyFingerprint: participant.PubKeyFingerprint,
|
||||
EncryptedInvitation: "lll",
|
||||
CreatedAt: &tm,
|
||||
})
|
||||
log.Println(response, err)
|
||||
}
|
||||
}
|
|
@ -34,21 +34,28 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
|
||||
m.payload.ConfirmationProposalPayload = make(internal.SignatureProposalQuorum)
|
||||
|
||||
for participantIntId, participant := range request {
|
||||
for index, participant := range request.Participants {
|
||||
participantId := createFingerprint(&participant.PublicKey)
|
||||
secret, err := generateRandomString(32)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot generateRandomString")
|
||||
}
|
||||
m.payload.ConfirmationProposalPayload[participantId] = internal.SignatureProposalParticipant{
|
||||
ParticipantId: participantIntId,
|
||||
ParticipantId: index,
|
||||
Title: participant.Title,
|
||||
PublicKey: participant.PublicKey,
|
||||
InvitationSecret: secret,
|
||||
UpdatedAt: nil,
|
||||
Status: internal.SignatureAwaitConfirmation,
|
||||
UpdatedAt: request.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Checking fo quorum length
|
||||
if len(m.payload.ConfirmationProposalPayload) != len(request.Participants) {
|
||||
err = errors.New("error with creating {SignatureProposalQuorum}")
|
||||
return
|
||||
}
|
||||
|
||||
// Make response
|
||||
|
||||
responseData := make(responses.SignatureProposalParticipantInvitationsResponse, 0)
|
||||
|
@ -59,6 +66,7 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
return nil, errors.New("cannot encryptWithPubKey")
|
||||
}
|
||||
responseEntry := &responses.SignatureProposalParticipantInvitationEntry{
|
||||
ParticipantId: proposal.ParticipantId,
|
||||
Title: proposal.Title,
|
||||
PubKeyFingerprint: pubKeyFingerprint,
|
||||
EncryptedInvitation: encryptedInvitationSecret,
|
||||
|
@ -72,13 +80,8 @@ func (m *SignatureProposalFSM) actionInitProposal(event fsm.Event, args ...inter
|
|||
}
|
||||
|
||||
//
|
||||
func (m *SignatureProposalFSM) actionConfirmProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionConfirmProposalByParticipant")
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SignatureProposalFSM) actionDeclineProposalByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
log.Println("I'm actionDeclineProposalByParticipant")
|
||||
func (m *SignatureProposalFSM) actionProposalResponseByParticipant(event fsm.Event, args ...interface{}) (response interface{}, err error) {
|
||||
// SignatureProposalParticipantRequest
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package signature_proposal_fsm
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
|
||||
|
@ -27,7 +27,7 @@ func ProposalParticipantsQuorumToResponse(list *internal.SignatureProposalQuorum
|
|||
// Common functions
|
||||
|
||||
func createFingerprint(data *[]byte) string {
|
||||
hash := sha256.Sum256(*data)
|
||||
hash := sha1.Sum(*data)
|
||||
return base64.StdEncoding.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
|
|
|
@ -7,23 +7,25 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
fsmName = "signature_proposal_fsm"
|
||||
FsmName = "signature_proposal_fsm"
|
||||
signingIdLen = 32
|
||||
|
||||
StateAwaitParticipantsConfirmations = fsm.State("state_validation_await_participants_confirmations") // waiting participants
|
||||
StateParticipantsConfirmationsInit = fsm.StateGlobalIdle
|
||||
|
||||
StateValidationCanceledByParticipant = fsm.State("state_validation_canceled_by_participant")
|
||||
StateValidationCanceledByTimeout = fsm.State("state_validation_canceled_by_timeout")
|
||||
StateAwaitParticipantsConfirmations = fsm.State("state_sig_proposal_await_participants_confirmations") // waiting participants
|
||||
|
||||
StateValidationCompleted = fsm.State("state_validation_completed")
|
||||
StateValidationCanceledByParticipant = fsm.State("state_sig_proposal_canceled_by_participant")
|
||||
StateValidationCanceledByTimeout = fsm.State("state_sig_proposal_canceled_by_timeout")
|
||||
|
||||
EventInitProposal = fsm.Event("event_proposal_init")
|
||||
EventConfirmProposal = fsm.Event("event_proposal_confirm_by_participant")
|
||||
EventDeclineProposal = fsm.Event("event_proposal_decline_by_participant")
|
||||
EventValidateProposal = fsm.Event("event_proposal_validate")
|
||||
EventSetProposalValidated = fsm.Event("event_proposal_set_validated")
|
||||
StateValidationCompleted = fsm.State("state_sig_proposal_completed")
|
||||
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("proposal_canceled_timeout")
|
||||
EventInitProposal = fsm.Event("event_sig_proposal_init")
|
||||
EventConfirmProposal = fsm.Event("event_sig_proposal_confirm_by_participant")
|
||||
EventDeclineProposal = fsm.Event("event_sig_proposal_decline_by_participant")
|
||||
EventValidateProposal = fsm.Event("event_sig_proposal_validate")
|
||||
EventSetProposalValidated = fsm.Event("event_sig_proposal_set_validated")
|
||||
|
||||
eventSetValidationCanceledByTimeout = fsm.Event("event_sig_proposal_canceled_timeout")
|
||||
|
||||
// Switch to next fsm
|
||||
|
||||
|
@ -39,13 +41,13 @@ func New() internal.DumpedMachineProvider {
|
|||
machine := &SignatureProposalFSM{}
|
||||
|
||||
machine.FSM = fsm.MustNewFSM(
|
||||
fsmName,
|
||||
FsmName,
|
||||
fsm.StateGlobalIdle,
|
||||
[]fsm.EventDesc{
|
||||
// {Name: "", SrcState: []string{""}, DstState: ""},
|
||||
|
||||
// Init
|
||||
{Name: EventInitProposal, SrcState: []fsm.State{fsm.StateGlobalIdle}, DstState: StateAwaitParticipantsConfirmations},
|
||||
{Name: EventInitProposal, SrcState: []fsm.State{StateParticipantsConfirmationsInit}, DstState: StateAwaitParticipantsConfirmations},
|
||||
|
||||
// Validate by participants
|
||||
{Name: EventConfirmProposal, SrcState: []fsm.State{StateAwaitParticipantsConfirmations}, DstState: StateAwaitParticipantsConfirmations},
|
||||
|
@ -64,8 +66,8 @@ func New() internal.DumpedMachineProvider {
|
|||
},
|
||||
fsm.Callbacks{
|
||||
EventInitProposal: machine.actionInitProposal,
|
||||
EventConfirmProposal: machine.actionConfirmProposalByParticipant,
|
||||
EventDeclineProposal: machine.actionDeclineProposalByParticipant,
|
||||
EventConfirmProposal: machine.actionProposalResponseByParticipant,
|
||||
EventDeclineProposal: machine.actionProposalResponseByParticipant,
|
||||
EventValidateProposal: machine.actionValidateProposal,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
package requests
|
||||
|
||||
import "time"
|
||||
|
||||
// Requests
|
||||
|
||||
type SignatureProposalParticipantsListRequest []SignatureProposalParticipantsEntry
|
||||
// States: "__idle"
|
||||
// Events: "event_sig_proposal_init"
|
||||
type SignatureProposalParticipantsListRequest struct {
|
||||
Participants []*SignatureProposalParticipantsEntry
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
||||
type SignatureProposalParticipantsEntry struct {
|
||||
// Public title for address, such as name, nickname, organization
|
||||
|
@ -10,8 +17,12 @@ type SignatureProposalParticipantsEntry struct {
|
|||
PublicKey []byte
|
||||
}
|
||||
|
||||
// States: "__idle"
|
||||
// Events: "event_sig_proposal_confirm_by_participant"
|
||||
// "event_sig_proposal_decline_by_participant"
|
||||
type SignatureProposalParticipantRequest struct {
|
||||
// Key for link invitations to participants
|
||||
PubKeyFingerprint string
|
||||
EncryptedInvitation string
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
|
|
|
@ -6,27 +6,42 @@ import (
|
|||
)
|
||||
|
||||
func (r *SignatureProposalParticipantsListRequest) Validate() error {
|
||||
if len(*r) < config.ParticipantsMinCount {
|
||||
if len(r.Participants) < config.ParticipantsMinCount {
|
||||
return errors.New("too few participants")
|
||||
}
|
||||
|
||||
for _, participant := range *r {
|
||||
for _, participant := range r.Participants {
|
||||
if len(participant.Title) < 3 {
|
||||
return errors.New("title too short")
|
||||
return errors.New("{Title} too short")
|
||||
}
|
||||
|
||||
if len(participant.Title) > 150 {
|
||||
return errors.New("title too long")
|
||||
return errors.New("{Title} too long")
|
||||
}
|
||||
|
||||
if len(participant.PublicKey) < 10 {
|
||||
return errors.New("pub key too short")
|
||||
return errors.New("{PublicKey} too short")
|
||||
}
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SignatureProposalParticipantRequest) Validate() error {
|
||||
if len(r.PubKeyFingerprint) == 0 {
|
||||
return errors.New("{PubKeyFingerprint} cannot zero length")
|
||||
}
|
||||
|
||||
if len(r.EncryptedInvitation) == 0 {
|
||||
return errors.New("{EncryptedInvitation} cannot zero length")
|
||||
}
|
||||
|
||||
if r.CreatedAt == nil {
|
||||
return errors.New("{CreatedAt} cannot be a nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue