Merge pull request #26 from depools/feat/airgapped-bls-signing

Feat/airgapped bls signing
This commit is contained in:
Andrew Zavgorodny 2020-08-28 15:01:51 +03:00 committed by GitHub
commit 233ee27745
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 566 additions and 99 deletions

View File

@ -3,6 +3,7 @@ package airgapped
import (
"encoding/json"
"fmt"
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
"log"
"sync"
@ -190,6 +191,12 @@ func (am *AirgappedMachine) HandleOperation(operation client.Operation) (client.
err = am.handleStateDkgResponsesAwaitConfirmations(&operation)
case dkg_proposal_fsm.StateDkgMasterKeyAwaitConfirmations:
err = am.handleStateDkgMasterKeyAwaitConfirmations(&operation)
case signing_proposal_fsm.StateSigningAwaitConfirmations:
err = am.handleStateSigningAwaitConfirmations(&operation)
case signing_proposal_fsm.StateSigningAwaitPartialKeys:
err = am.handleStateSigningAwaitPartialSigns(&operation)
case signing_proposal_fsm.StateSigningPartialKeysCollected:
err = am.reconstructThresholdSignature(&operation)
default:
err = fmt.Errorf("invalid operation type: %s", operation.Type)
}

View File

@ -7,6 +7,7 @@ import (
client "github.com/depools/dc4bc/client/types"
"github.com/depools/dc4bc/fsm/fsm"
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
"github.com/depools/dc4bc/fsm/types/requests"
"github.com/depools/dc4bc/fsm/types/responses"
"github.com/depools/dc4bc/storage"
@ -29,6 +30,7 @@ type Node struct {
deals []requests.DKGProposalDealConfirmationRequest
responses []requests.DKGProposalResponseConfirmationRequest
masterKeys []requests.DKGProposalMasterKeyConfirmationRequest
partialSigns []requests.SigningProposalPartialKeyRequest
}
func (n *Node) storeOperation(t *testing.T, msg storage.Message) {
@ -57,6 +59,12 @@ func (n *Node) storeOperation(t *testing.T, msg storage.Message) {
t.Fatalf("failed to unmarshal fsm req: %v", err)
}
n.masterKeys = append(n.masterKeys, req)
case signing_proposal_fsm.EventSigningPartialKeyReceived:
var req requests.SigningProposalPartialKeyRequest
if err := json.Unmarshal(msg.Data, &req); err != nil {
t.Fatalf("failed to unmarshal fsm req: %v", err)
}
n.partialSigns = append(n.partialSigns, req)
default:
t.Fatalf("invalid event: %s", msg.Event)
}
@ -91,7 +99,7 @@ func createOperation(t *testing.T, opType string, to string, req interface{}) cl
}
func TestAirgappedAllSteps(t *testing.T) {
nodesCount := 25
nodesCount := 10
participants := make([]string, nodesCount)
for i := 0; i < nodesCount; i++ {
participants[i] = fmt.Sprintf("Participant#%d", i)
@ -220,27 +228,52 @@ func TestAirgappedAllSteps(t *testing.T) {
}
msgToSign := []byte("i am a message")
sigShares := make([][]byte, 0)
for _, n := range tr.nodes {
sigShare, err := n.Machine.createPartialSign(msgToSign, DKGIdentifier)
//partialSigns
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
defer wg.Done()
payload := responses.SigningPartialSignsParticipantInvitationsResponse{
SrcPayload: msgToSign,
}
op := createOperation(t, string(signing_proposal_fsm.StateSigningAwaitPartialKeys), "", payload)
operation, err := n.Machine.HandleOperation(op)
if err != nil {
t.Fatalf("failed to create sig share: %v", err.Error())
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
}
sigShares = append(sigShares, sigShare)
}
fullSign, err := tr.nodes[0].Machine.recoverFullSign(msgToSign, sigShares, DKGIdentifier)
if err != nil {
t.Fatalf("failed to recover full sign: %v", err.Error())
}
for _, n := range tr.nodes {
if err = n.Machine.verifySign(msgToSign, fullSign, DKGIdentifier); err != nil {
t.Fatalf("failed to verify signature: %v", err)
for _, msg := range operation.ResultMsgs {
tr.BroadcastMessage(t, msg)
}
}
})
fmt.Println("DKG succeeded, signature recovered and verified")
//recover full signature
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
defer wg.Done()
var payload responses.SigningProcessParticipantResponse
for _, req := range n.partialSigns {
p := responses.SigningProcessParticipantEntry{
ParticipantId: req.ParticipantId,
Addr: fmt.Sprintf("Participant#%d", req.ParticipantId),
PartialSign: req.PartialSign,
}
payload.Participants = append(payload.Participants, &p)
}
payload.SrcPayload = msgToSign
op := createOperation(t, string(signing_proposal_fsm.StateSigningPartialKeysCollected), "", payload)
operation, err := n.Machine.HandleOperation(op)
if err != nil {
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
}
for _, msg := range operation.ResultMsgs {
tr.BroadcastMessage(t, msg)
}
})
fmt.Println("DKG succeeded, signature recovered")
}
func runStep(transport *Transport, cb func(n *Node, wg *sync.WaitGroup)) {

View File

@ -1,41 +1,102 @@
package airgapped
import (
"encoding/json"
"fmt"
client "github.com/depools/dc4bc/client/types"
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
"github.com/depools/dc4bc/fsm/types/requests"
"github.com/depools/dc4bc/fsm/types/responses"
"go.dedis.ch/kyber/v3/sign/bls"
"go.dedis.ch/kyber/v3/sign/tbls"
)
// commented because fsm is not ready
//func (am *AirgappedMachine) handleStateSigningAwaitPartialKeys(o *client.Operation) error {
// var (
// payload responses.DKGProposalResponsesParticipantResponse
// err error
// )
//
// if err = json.Unmarshal(o.Payload, &payload); err != nil {
// return fmt.Errorf("failed to unmarshal payload: %w", err)
// }
//
// partialSign, err := am.createPartialSign(nil, o.DKGIdentifier)
// if err != nil {
// return fmt.Errorf("failed to create partialSign for msg: %w", err)
// }
//
// req := requests.SigningProposalPartialKeyRequest{
// ParticipantId: 0, // TODO: from where?
// PartialKey: partialSign,
// CreatedAt: o.CreatedAt,
// }
// reqBz, err := json.Marshal(req)
// if err != nil {
// return fmt.Errorf("failed to generate fsm request: %w", err)
// }
//
// o.Result = reqBz
// o.Event = signing_proposal_fsm.EventSigningPartialKeyReceived
// return nil
//}
func (am *AirgappedMachine) handleStateSigningAwaitConfirmations(o *client.Operation) error {
var (
payload responses.SigningProposalParticipantInvitationsResponse
err error
)
if err = json.Unmarshal(o.Payload, &payload); err != nil {
return fmt.Errorf("failed to unmarshal payload: %w", err)
}
participantID, err := am.getParticipantID(o.DKGIdentifier)
if err != nil {
return fmt.Errorf("failed to get paricipant id: %w", err)
}
req := requests.SigningProposalParticipantRequest{
SigningId: payload.SigningId,
ParticipantId: participantID,
CreatedAt: o.CreatedAt,
}
reqBz, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("failed to generate fsm request: %w", err)
}
o.Event = signing_proposal_fsm.EventConfirmSigningConfirmation
o.ResultMsgs = append(o.ResultMsgs, createMessage(*o, reqBz))
return nil
}
func (am *AirgappedMachine) handleStateSigningAwaitPartialSigns(o *client.Operation) error {
var (
payload responses.SigningPartialSignsParticipantInvitationsResponse
err error
)
if err = json.Unmarshal(o.Payload, &payload); err != nil {
return fmt.Errorf("failed to unmarshal payload: %w", err)
}
partialSign, err := am.createPartialSign(payload.SrcPayload, o.DKGIdentifier)
if err != nil {
return fmt.Errorf("failed to create partialSign for msg: %w", err)
}
participantID, err := am.getParticipantID(o.DKGIdentifier)
if err != nil {
return fmt.Errorf("failed to get paricipant id: %w", err)
}
req := requests.SigningProposalPartialKeyRequest{
SigningId: payload.SigningId,
ParticipantId: participantID,
PartialSign: partialSign,
CreatedAt: o.CreatedAt,
}
reqBz, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("failed to generate fsm request: %w", err)
}
o.Event = signing_proposal_fsm.EventSigningPartialKeyReceived
o.ResultMsgs = append(o.ResultMsgs, createMessage(*o, reqBz))
return nil
}
func (am *AirgappedMachine) reconstructThresholdSignature(o *client.Operation) error {
var (
payload responses.SigningProcessParticipantResponse
err error
)
if err = json.Unmarshal(o.Payload, &payload); err != nil {
return fmt.Errorf("failed to unmarshal payload: %w", err)
}
partialSignatures := make([][]byte, 0, len(payload.Participants))
for _, participant := range payload.Participants {
partialSignatures = append(partialSignatures, participant.PartialSign)
}
reconstructedSignature, err := am.recoverFullSign(payload.SrcPayload, partialSignatures, o.DKGIdentifier)
if err != nil {
return fmt.Errorf("failed to reconsruct full signature for msg: %w", err)
}
fmt.Println(reconstructedSignature)
return nil
}
func (am *AirgappedMachine) createPartialSign(msg []byte, dkgIdentifier string) ([]byte, error) {
blsKeyring, err := am.loadBLSKeyring(dkgIdentifier)

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
sipf "github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
"log"
"path/filepath"
"sync"
@ -182,6 +183,18 @@ func (c *Client) ProcessMessage(message storage.Message) error {
return fmt.Errorf("failed to Do operation in FSM: %w", err)
}
}
if resp.State == dpf.StateDkgMasterKeyCollected {
fsmInstance, err = state_machines.FromDump(fsmDump)
if err != nil {
return fmt.Errorf("failed get state_machines from dump: %w", err)
}
resp, fsmDump, err = fsmInstance.Do(sipf.EventSigningInit, requests.DefaultRequest{
CreatedAt: time.Now(),
})
if err != nil {
return fmt.Errorf("failed to Do operation in FSM: %w", err)
}
}
var operation *types.Operation
switch resp.State {
@ -191,7 +204,10 @@ func (c *Client) ProcessMessage(message storage.Message) error {
dpf.StateDkgCommitsAwaitConfirmations,
dpf.StateDkgDealsAwaitConfirmations,
dpf.StateDkgResponsesAwaitConfirmations,
dpf.StateDkgMasterKeyAwaitConfirmations:
dpf.StateDkgMasterKeyAwaitConfirmations,
sipf.StateSigningAwaitPartialKeys,
sipf.StateSigningPartialKeysCollected,
sipf.StateSigningAwaitConfirmations:
if resp.Data != nil {
bz, err := json.Marshal(resp.Data)
if err != nil {

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
"time"
"github.com/depools/dc4bc/fsm/fsm"
@ -85,6 +86,24 @@ func FSMRequestFromMessage(message storage.Message) (interface{}, error) {
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
}
resolvedValue = req
case signing_proposal_fsm.EventSigningPartialKeyReceived:
var req requests.SigningProposalPartialKeyRequest
if err := json.Unmarshal(message.Data, &req); err != nil {
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
}
resolvedValue = req
case signing_proposal_fsm.EventConfirmSigningConfirmation:
var req requests.SigningProposalParticipantRequest
if err := json.Unmarshal(message.Data, &req); err != nil {
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
}
resolvedValue = req
case signing_proposal_fsm.EventSigningStart:
var req requests.SigningProposalStartRequest
if err := json.Unmarshal(message.Data, &req); err != nil {
return fmt.Errorf("failed to unmarshal fsm req: %v", err), nil
}
resolvedValue = req
default:
return nil, fmt.Errorf("invalid event: %s", message.Event)
}

View File

@ -183,9 +183,9 @@ func (s SigningParticipantStatus) String() string {
}
type SigningProposalParticipant struct {
Addr string
Status SigningParticipantStatus
PartialKey []byte
Error error
UpdatedAt time.Time
Addr string
Status SigningParticipantStatus
PartialSign []byte
Error error
UpdatedAt time.Time
}

View File

@ -48,6 +48,10 @@ var (
CreatedAt: tm,
}
testSigningId string
testSigningInitiator int
testSigningPayload = []byte("message to sign")
testFSMDump = map[fsm.State][]byte{}
)
@ -173,17 +177,17 @@ func Test_SignatureProposal_EventInitProposal_Positive(t *testing.T) {
compareState(t, spf.StateAwaitParticipantsConfirmations, fsmResponse.State)
testParticipantsListResponse, ok := fsmResponse.Data.(responses.SignatureProposalParticipantInvitationsResponse)
response, 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))
if len(response) != len(testParticipantsListRequest.Participants) {
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(response))
}
for _, participant := range testParticipantsListResponse {
for _, participant := range response {
if _, ok := testIdMapParticipants[participant.ParticipantId]; ok {
t.Fatalf("expected unique {ParticipantId}")
}
@ -400,6 +404,10 @@ func Test_DkgProposal_EventDKGCommitConfirmationReceived(t *testing.T) {
t.Fatalf("expected exist {ParticipantId}")
}
if responseEntry.Addr == "" {
t.Fatalf("expected {Addr} non zero length")
}
if len(responseEntry.DkgCommit) == 0 {
t.Fatalf("expected {DkgCommit} non zero length")
}
@ -521,6 +529,10 @@ func Test_DkgProposal_EventDKGDealConfirmationReceived(t *testing.T) {
t.Fatalf("expected exist {ParticipantId}")
}
if responseEntry.Addr == "" {
t.Fatalf("expected {Addr} non zero length")
}
if len(responseEntry.DkgDeal) == 0 {
t.Fatalf("expected {DkgDeal} non zero length")
}
@ -636,6 +648,10 @@ func Test_DkgProposal_EventDKGResponseConfirmationReceived_Positive(t *testing.T
t.Fatalf("expected exist {ParticipantId}")
}
if responseEntry.Addr == "" {
t.Fatalf("expected {Addr} non zero length")
}
if len(responseEntry.DkgResponse) == 0 {
t.Fatalf("expected {DkgResponse} non zero length")
}
@ -864,7 +880,9 @@ func Test_SigningProposal_EventSigningInit(t *testing.T) {
// Start
func Test_SigningProposal_EventSigningStart(t *testing.T) {
var fsmResponse *fsm.Response
var (
fsmResponse *fsm.Response
)
testFSMInstance, err := FromDump(testFSMDump[sif.StateSigningIdle])
@ -887,22 +905,237 @@ func Test_SigningProposal_EventSigningStart(t *testing.T) {
compareState(t, sif.StateSigningAwaitConfirmations, fsmResponse.State)
compareDumpNotZero(t, testFSMDump[sif.StateSigningAwaitConfirmations])
testSigningParticipantsListResponse, ok := fsmResponse.Data.(responses.SigningProposalParticipantInvitationsResponse)
response, ok := fsmResponse.Data.(responses.SigningProposalParticipantInvitationsResponse)
if !ok {
t.Fatalf("expected response {SigningProposalParticipantInvitationsResponse}")
}
if len(testSigningParticipantsListResponse.Participants) != len(testParticipantsListRequest.Participants) {
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(testSigningParticipantsListResponse.Participants))
if len(response.Participants) != len(testParticipantsListRequest.Participants) {
t.Fatalf("expected response len {%d}, got {%d}", len(testParticipantsListRequest.Participants), len(response.Participants))
}
if testSigningParticipantsListResponse.SigningId == "" {
if response.SigningId == "" {
t.Fatalf("expected field {SigningId}")
}
if !reflect.DeepEqual(response.SrcPayload, testSigningPayload) {
t.Fatalf("expected matched {SrcPayload}")
}
testSigningId = response.SigningId
testSigningInitiator = response.InitiatorId
compareDumpNotZero(t, testFSMDump[sif.StateSigningAwaitConfirmations])
}
func Test_SigningProposal_EventConfirmSigningConfirmation_Positive(t *testing.T) {
var (
fsmResponse *fsm.Response
testFSMDumpLocal []byte
)
participantsCount := len(testIdMapParticipants)
participantCounter := participantsCount
testFSMDumpLocal = testFSMDump[sif.StateSigningAwaitConfirmations]
for participantId, _ := range testIdMapParticipants {
participantCounter--
if testSigningInitiator == participantId {
continue
}
testFSMInstance, err := FromDump(testFSMDumpLocal)
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
inState, _ := testFSMInstance.State()
compareState(t, sif.StateSigningAwaitConfirmations, inState)
fsmResponse, testFSMDumpLocal, err = testFSMInstance.Do(sif.EventConfirmSigningConfirmation, requests.SigningProposalParticipantRequest{
SigningId: testSigningId,
ParticipantId: participantId,
CreatedAt: time.Now(),
})
compareErrNil(t, err)
compareDumpNotZero(t, testFSMDumpLocal)
compareFSMResponseNotNil(t, fsmResponse)
if participantCounter-1 > 0 {
compareState(t, sif.StateSigningAwaitConfirmations, fsmResponse.State)
}
}
compareState(t, sif.StateSigningAwaitPartialKeys, fsmResponse.State)
response, ok := fsmResponse.Data.(responses.SigningPartialSignsParticipantInvitationsResponse)
if !ok {
t.Fatalf("expected response {SigningProposalParticipantInvitationsResponse}")
}
if response.SigningId == "" {
t.Fatalf("expected field {SigningId}")
}
if !reflect.DeepEqual(response.SrcPayload, testSigningPayload) {
t.Fatalf("expected matched {SrcPayload}")
}
testFSMDump[sif.StateSigningAwaitPartialKeys] = testFSMDumpLocal
compareDumpNotZero(t, testFSMDump[sif.StateSigningAwaitPartialKeys])
}
func Test_SigningProposal_EventDeclineProposal_Canceled_Participant(t *testing.T) {
testFSMInstance, err := FromDump(testFSMDump[sif.StateSigningAwaitConfirmations])
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
inState, _ := testFSMInstance.State()
compareState(t, sif.StateSigningAwaitConfirmations, inState)
fsmResponse, testFSMDumpLocal, err := testFSMInstance.Do(sif.EventDeclineSigningConfirmation, requests.SigningProposalParticipantRequest{
SigningId: testSigningId,
ParticipantId: 0,
CreatedAt: time.Now(),
})
compareErrNil(t, err)
compareDumpNotZero(t, testFSMDumpLocal)
compareFSMResponseNotNil(t, fsmResponse)
compareState(t, sif.StateSigningConfirmationsAwaitCancelledByParticipant, fsmResponse.State)
}
func Test_SigningProposal_EventConfirmSignatureProposal_Canceled_Timeout(t *testing.T) {
testFSMInstance, err := FromDump(testFSMDump[sif.StateSigningAwaitConfirmations])
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
inState, _ := testFSMInstance.State()
compareState(t, sif.StateSigningAwaitConfirmations, inState)
fsmResponse, testFSMDumpLocal, err := testFSMInstance.Do(sif.EventConfirmSigningConfirmation, requests.SigningProposalParticipantRequest{
SigningId: testSigningId,
ParticipantId: 0,
CreatedAt: time.Now().Add(36 * time.Hour),
})
compareErrNil(t, err)
compareDumpNotZero(t, testFSMDumpLocal)
compareFSMResponseNotNil(t, fsmResponse)
compareState(t, sif.StateSigningConfirmationsAwaitCancelledByTimeout, fsmResponse.State)
}
func Test_SigningProposal_EventSigningPartialKeyReceived_Positive(t *testing.T) {
var (
fsmResponse *fsm.Response
testFSMDumpLocal []byte
)
participantsCount := len(testIdMapParticipants)
participantCounter := participantsCount
testFSMDumpLocal = testFSMDump[sif.StateSigningAwaitPartialKeys]
for participantId, participant := range testIdMapParticipants {
participantCounter--
testFSMInstance, err := FromDump(testFSMDumpLocal)
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
inState, _ := testFSMInstance.State()
compareState(t, sif.StateSigningAwaitPartialKeys, inState)
fsmResponse, testFSMDumpLocal, err = testFSMInstance.Do(sif.EventSigningPartialKeyReceived, requests.SigningProposalPartialKeyRequest{
SigningId: testSigningId,
ParticipantId: participantId,
PartialSign: participant.DkgPartialKey,
CreatedAt: time.Now(),
})
compareErrNil(t, err)
compareDumpNotZero(t, testFSMDumpLocal)
compareFSMResponseNotNil(t, fsmResponse)
if participantCounter > 0 {
compareState(t, sif.StateSigningAwaitPartialKeys, fsmResponse.State)
}
}
compareState(t, sif.StateSigningPartialKeysCollected, fsmResponse.State)
response, ok := fsmResponse.Data.(responses.SigningProcessParticipantResponse)
if !ok {
t.Fatalf("expected response {SigningProcessParticipantResponse}")
}
if response.SigningId == "" {
t.Fatalf("expected field {SigningId}")
}
if !reflect.DeepEqual(response.SrcPayload, testSigningPayload) {
t.Fatalf("expected matched {SrcPayload}")
}
testFSMDump[sif.StateSigningPartialKeysCollected] = testFSMDumpLocal
compareDumpNotZero(t, testFSMDump[sif.StateSigningPartialKeysCollected])
}
func Test_DkgProposal_EventSigningRestart_Positive(t *testing.T) {
var (
fsmResponse *fsm.Response
testFSMDumpLocal []byte
)
testFSMInstance, err := FromDump(testFSMDump[sif.StateSigningPartialKeysCollected])
compareErrNil(t, err)
compareFSMInstanceNotNil(t, testFSMInstance)
inState, _ := testFSMInstance.State()
compareState(t, sif.StateSigningPartialKeysCollected, inState)
fsmResponse, testFSMDumpLocal, err = testFSMInstance.Do(sif.EventSigningRestart, requests.DefaultRequest{
CreatedAt: time.Now(),
})
compareErrNil(t, err)
compareFSMResponseNotNil(t, fsmResponse)
compareState(t, sif.StateSigningIdle, fsmResponse.State)
compareDumpNotZero(t, testFSMDumpLocal)
}
func Test_Parallel(t *testing.T) {

View File

@ -84,17 +84,18 @@ func (m *SigningProposalFSM) actionStartSigningProposal(inEvent fsm.Event, args
m.payload.SigningProposalPayload.CreatedAt = request.CreatedAt
// Make response
responseData := responses.SigningProposalParticipantInvitationsResponse{
SigningId: m.payload.SigningProposalPayload.SigningId,
InitiatorId: m.payload.SigningProposalPayload.InitiatorId,
SrcPayload: m.payload.SigningProposalPayload.SrcPayload,
Participants: make([]*responses.SigningProposalParticipantInvitationEntry, 0),
}
for participantId, proposal := range m.payload.SigningProposalPayload.Quorum {
for participantId, participant := range m.payload.SigningProposalPayload.Quorum {
responseEntry := &responses.SigningProposalParticipantInvitationEntry{
ParticipantId: participantId,
Addr: proposal.Addr,
Addr: participant.Addr,
Status: uint8(participant.Status),
}
responseData.Participants = append(responseData.Participants, responseEntry)
}
@ -130,7 +131,7 @@ func (m *SigningProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Eve
signingProposalParticipant := m.payload.SigningQuorumGet(request.ParticipantId)
if signingProposalParticipant.Status != internal.SigningAwaitConfirmation {
err = errors.New(fmt.Sprintf("cannot confirm commit with {Status} = {\"%s\"}", signingProposalParticipant.Status))
err = errors.New(fmt.Sprintf("cannot confirm participant with {Status} = {\"%s\"}", signingProposalParticipant.Status))
return
}
@ -143,7 +144,6 @@ func (m *SigningProposalFSM) actionProposalResponseByParticipant(inEvent fsm.Eve
err = errors.New(fmt.Sprintf("unsupported event for action {inEvent} = {\"%s\"}", inEvent))
return
}
signingProposalParticipant.Status = internal.SigningConfirmed
signingProposalParticipant.UpdatedAt = request.CreatedAt
m.payload.SigningProposalPayload.UpdatedAt = request.CreatedAt
@ -162,7 +162,7 @@ func (m *SigningProposalFSM) actionValidateSigningProposalConfirmations(inEvent
defer m.payloadMu.Unlock()
if m.payload.SigningProposalPayload.IsExpired() {
outEvent = eventSetSigningConfirmCanceledByParticipantInternal
outEvent = eventSetSigningConfirmCanceledByTimeoutInternal
return
}
@ -191,6 +191,15 @@ func (m *SigningProposalFSM) actionValidateSigningProposalConfirmations(inEvent
participant.Status = internal.SigningAwaitPartialKeys
}
// Make response
responseData := responses.SigningPartialSignsParticipantInvitationsResponse{
SigningId: m.payload.SigningProposalPayload.SigningId,
InitiatorId: m.payload.SigningProposalPayload.InitiatorId,
SrcPayload: m.payload.SigningProposalPayload.SrcPayload,
}
response = responseData
return
}
@ -226,8 +235,8 @@ func (m *SigningProposalFSM) actionPartialKeyConfirmationReceived(inEvent fsm.Ev
return
}
signingProposalParticipant.PartialKey = make([]byte, len(request.PartialKey))
copy(signingProposalParticipant.PartialKey, request.PartialKey)
signingProposalParticipant.PartialSign = make([]byte, len(request.PartialSign))
copy(signingProposalParticipant.PartialSign, request.PartialSign)
signingProposalParticipant.Status = internal.SigningPartialKeysConfirmed
signingProposalParticipant.UpdatedAt = request.CreatedAt
@ -276,6 +285,29 @@ func (m *SigningProposalFSM) actionValidateSigningPartialKeyAwaitConfirmations(i
participant.Status = internal.SigningProcess
}
// Response
responseData := responses.SigningProcessParticipantResponse{
SigningId: m.payload.SigningProposalPayload.SigningId,
SrcPayload: m.payload.SigningProposalPayload.SrcPayload,
Participants: make([]*responses.SigningProcessParticipantEntry, 0),
}
for participantId, participant := range m.payload.SigningProposalPayload.Quorum {
responseEntry := &responses.SigningProcessParticipantEntry{
ParticipantId: participantId,
Addr: participant.Addr,
PartialSign: participant.PartialSign,
}
responseData.Participants = append(responseData.Participants, responseEntry)
}
response = responseData
return
}
func (m *SigningProposalFSM) actionSigningRestart(inEvent fsm.Event, args ...interface{}) (outEvent fsm.Event, response interface{}, err error) {
return
}

View File

@ -6,7 +6,7 @@ import (
)
const (
dkgTransactionIdLength = 128
dkgTransactionIdLength = 512
)
func generateSigningId() (string, error) {

View File

@ -23,8 +23,8 @@ const (
StateSigningAwaitPartialKeys = fsm.State("state_signing_await_partial_keys")
// Cancelled
StateSigningPartialKeysAwaitCancelledByTimeout = fsm.State("state_signing_partial_signatures_await_cancelled_by_timeout")
StateSigningPartialKeysAwaitCancelledByParticipant = fsm.State("state_signing_partial_signatures_await_cancelled_by_participant")
StateSigningPartialKeysAwaitCancelledByTimeout = fsm.State("state_signing_partial_signatures_await_cancelled_by_timeout")
StateSigningPartialKeysAwaitCancelledByError = fsm.State("state_signing_partial_signatures_await_cancelled_by_error")
StateSigningPartialKeysCollected = fsm.State("state_signing_partial_signatures_collected")
@ -37,15 +37,18 @@ const (
eventSetSigningConfirmCanceledByParticipantInternal = fsm.Event("event_signing_proposal_canceled_by_participant")
eventSetSigningConfirmCanceledByTimeoutInternal = fsm.Event("event_signing_proposal_canceled_by_timeout")
eventAutoValidateProposalInternal = fsm.Event("event_signing_proposal_validate")
eventSetProposalValidatedInternal = fsm.Event("event_signing_proposal_set_validated")
eventAutoSigningValidateProposalInternal = fsm.Event("event_signing_proposal_await_validate")
eventSetProposalValidatedInternal = fsm.Event("event_signing_proposal_set_validated")
EventSigningPartialKeyReceived = fsm.Event("event_signing_partial_key_received")
EventSigningPartialKeyError = fsm.Event("event_signing_partial_key_error_received")
eventSigningPartialKeyCancelByTimeoutInternal = fsm.Event("event_signing_partial_key_canceled_by_timeout_internal")
eventSigningPartialKeyCancelByErrorInternal = fsm.Event("event_signing_partial_key_canceled_by_error_internal")
eventSigningPartialKeysConfirmedInternal = fsm.Event("event_signing_partial_keys_confirmed_internal")
EventSigningRestart = fsm.Event("event_signing_restart")
eventAutoSigningValidatePartialKeyInternal = fsm.Event("event_signing_partial_keys_await_validate")
eventSigningPartialKeysConfirmedInternal = fsm.Event("event_signing_partial_keys_confirmed_internal")
EventSigningRestart = fsm.Event("event_signing_restart")
)
type SigningProposalFSM struct {
@ -76,29 +79,33 @@ func New() internal.DumpedMachineProvider {
{Name: eventSetSigningConfirmCanceledByTimeoutInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningConfirmationsAwaitCancelledByTimeout, IsInternal: true},
// Validate
{Name: eventAutoValidateProposalInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations, IsInternal: true, IsAuto: true},
{Name: eventAutoSigningValidateProposalInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitConfirmations, IsInternal: true, IsAuto: true},
{Name: eventSetProposalValidatedInternal, SrcState: []fsm.State{StateSigningAwaitConfirmations}, DstState: StateSigningAwaitPartialKeys, IsInternal: true},
// Canceled
{Name: EventSigningPartialKeyReceived, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningAwaitPartialKeys},
{Name: EventSigningPartialKeyError, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByParticipant},
{Name: EventSigningPartialKeyError, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByError},
{Name: eventSigningPartialKeyCancelByTimeoutInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByTimeout, IsInternal: true},
{Name: eventSigningPartialKeyCancelByErrorInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByParticipant, IsInternal: true, IsAuto: true},
{Name: eventSigningPartialKeyCancelByErrorInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysAwaitCancelledByError, IsInternal: true},
// Validate
{Name: eventAutoSigningValidatePartialKeyInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningAwaitPartialKeys, IsInternal: true, IsAuto: true},
{Name: eventSigningPartialKeysConfirmedInternal, SrcState: []fsm.State{StateSigningAwaitPartialKeys}, DstState: StateSigningPartialKeysCollected, IsInternal: true},
{Name: EventSigningRestart, SrcState: []fsm.State{StateSigningPartialKeysCollected}, DstState: StateSigningIdle, IsInternal: true},
{Name: EventSigningRestart, SrcState: []fsm.State{StateSigningPartialKeysCollected}, DstState: StateSigningIdle},
},
fsm.Callbacks{
EventSigningInit: machine.actionInitSigningProposal,
EventSigningStart: machine.actionStartSigningProposal,
EventConfirmSigningConfirmation: machine.actionProposalResponseByParticipant,
EventDeclineSigningConfirmation: machine.actionProposalResponseByParticipant,
eventAutoValidateProposalInternal: machine.actionValidateSigningProposalConfirmations,
EventSigningPartialKeyReceived: machine.actionPartialKeyConfirmationReceived,
EventSigningPartialKeyError: machine.actionValidateSigningPartialKeyAwaitConfirmations,
// actionConfirmationError
EventSigningInit: machine.actionInitSigningProposal,
EventSigningStart: machine.actionStartSigningProposal,
EventConfirmSigningConfirmation: machine.actionProposalResponseByParticipant,
EventDeclineSigningConfirmation: machine.actionProposalResponseByParticipant,
eventAutoSigningValidateProposalInternal: machine.actionValidateSigningProposalConfirmations,
EventSigningPartialKeyReceived: machine.actionPartialKeyConfirmationReceived,
eventAutoSigningValidatePartialKeyInternal: machine.actionValidateSigningPartialKeyAwaitConfirmations,
EventSigningPartialKeyError: machine.actionConfirmationError,
EventSigningRestart: machine.actionSigningRestart,
},
)

View File

@ -24,6 +24,6 @@ type SigningProposalParticipantRequest struct {
type SigningProposalPartialKeyRequest struct {
SigningId string
ParticipantId int
PartialKey []byte
PartialSign []byte
CreatedAt time.Time
}

View File

@ -43,8 +43,8 @@ func (r *SigningProposalPartialKeyRequest) Validate() error {
return errors.New("{ParticipantId} cannot be a negative number")
}
if len(r.PartialKey) == 0 {
return errors.New("{PartialKey} cannot zero length")
if len(r.PartialSign) == 0 {
return errors.New("{PartialSign} cannot zero length")
}
if r.CreatedAt.IsZero() {

View File

@ -4,7 +4,6 @@ package responses
// Event: "event_sig_proposal_init"
// States: "__idle"
type SignatureProposalParticipantInvitationsResponse []*SignatureProposalParticipantInvitationEntry
type SignatureProposalParticipantInvitationEntry struct {

View File

@ -1,20 +1,52 @@
package responses
// Event: "event_signing_start"
// States: "state_signing_await_confirmations"
type SigningProposalParticipantInvitationsResponse struct {
SigningId string
InitiatorId int
Participants []*SigningProposalParticipantInvitationEntry
SigningId string
// Source message for signing
SrcPayload []byte
}
type SigningProposalParticipantInvitationEntry struct {
ParticipantId int
Addr string
Status uint8
}
type SigningProposalParticipantStatusResponse []*SignatureProposalParticipantStatusEntry
// Event: "event_signing_proposal_confirm_by_participant"
// States: "state_signing_await_partial_keys"
type SigningPartialSignsParticipantInvitationsResponse struct {
SigningId string
InitiatorId int
SrcPayload []byte
}
// Event: ""
// States: ""
type SigningProposalParticipantStatusResponse struct {
SigningId string
Participants []*SignatureProposalParticipantStatusEntry
}
type SigningProposalParticipantStatusEntry struct {
ParticipantId int
Addr string
Status uint8
}
// Event: ""
// States: ""
type SigningProcessParticipantResponse struct {
SigningId string
SrcPayload []byte
Participants []*SigningProcessParticipantEntry
}
type SigningProcessParticipantEntry struct {
ParticipantId int
Addr string
PartialSign []byte
}

30
main.go
View File

@ -7,6 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
sif "github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
_ "image/jpeg"
"log"
"sync"
@ -119,7 +120,7 @@ func main() {
participants = append(participants, &requests.SignatureProposalParticipantsEntry{
Addr: node.client.GetAddr(),
PubKey: node.client.GetPubKey(),
DkgPubKey: dkgPubKey, // TODO: Use a real one.
DkgPubKey: dkgPubKey,
})
}
messageData := requests.SignatureProposalParticipantsListRequest{
@ -146,6 +147,33 @@ func main() {
log.Fatalf("Failed to send %+v to storage: %v\n", message, err)
}
// i haven't a better idea to test signing without big changes in the client code
time.Sleep(10 * time.Second)
log.Println("Propose message to sign")
messageDataSign := requests.SigningProposalStartRequest{
ParticipantId: 0,
SrcPayload: []byte("message to sign"),
CreatedAt: time.Now(),
}
messageDataBz, err = json.Marshal(messageDataSign)
if err != nil {
log.Fatalf("failed to marshal SignatureProposalParticipantsListRequest: %v\n", err)
}
message = storage.Message{
ID: uuid.New().String(),
DkgRoundID: hex.EncodeToString(dkgRoundID[:]),
Event: string(sif.EventSigningStart),
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()