mirror of https://github.com/certusone/dc4bc.git
Merge branch 'master' into feat/remove-participant-address
This commit is contained in:
commit
63543446c0
43
HowTo.md
43
HowTo.md
|
@ -96,17 +96,27 @@ EcVs+nTi4iFERVeBHUPePDmvknBx95co7csKj0sZNuo=
|
|||
sN7XbnvZCRtg650dVCCpPK/hQ/rMTSlxrdnvzJ75zV4W/Uzk9suvjNPtyRt7PDXLDTGNimn+4X/FcJj2K6vDdgqOrr9BHwMqJXnQykcv3IV0ggIUjpMMgdbQ+0iSseyq
|
||||
```
|
||||
|
||||
Now you want to start the DKG procedure. This tells the node to send an InitDKG message that proposes to run DKG for 2 participants with `threshold=2`. You will be prompted to enter some required information about the suggested participants:
|
||||
Now you want to start the DKG procedure. This tells the node to send an InitDKG message that proposes to run DKG with parameters which locate in a start_dkg_propose.json file.
|
||||
```
|
||||
$ ./dc4bc_cli start_dkg 2 2 --listen_addr localhost:8080
|
||||
Enter a necessary data for participant 0:
|
||||
Enter address: e0d8083f8a2d18f310bfbdc9649a83664470f46053ab53c105a054b08f9eff85
|
||||
Enter pubkey (base64): 4NgIP4otGPMQv73JZJqDZkRw9GBTq1PBBaBUsI+e/4U=
|
||||
Enter DKGPubKey (base64): sN7XbnvZCRtg650dVCCpPK/hQ/rMTSlxrdnvzJ75zV4W/Uzk9suvjNPtyRt7PDXLDTGNimn+4X/FcJj2K6vDdgqOrr9BHwMqJXnQykcv3IV0ggIUjpMMgdbQ+0iSseyq
|
||||
Enter a necessary data for participant 1:
|
||||
Enter address: 11c56cfa74e2e221444557811d43de3c39af927071f79728edcb0a8f4b1936ea
|
||||
Enter pubkey (base64): EcVs+nTi4iFERVeBHUPePDmvknBx95co7csKj0sZNuo=
|
||||
Enter DKGPubKey (base64): kJbOTZSwOKWYfg1KD/VxfRDEfk7kSgMzYiALJaLn2HJ08x5kIJWqkzFi/Z0B3ZEgBJROOybWPMVnQOpQ/DQwxYbxa6kgOPPBnY5WshX14vkgAtv+gE062rWLtFVBqZI+
|
||||
$ ./dc4bc_cli start_dkg /path/to/start_dkg_propose.json --listen_addr localhost:8080
|
||||
```
|
||||
Example of start_dkg_propose.json file structure:
|
||||
```
|
||||
{
|
||||
"SigningThreshold": 2,
|
||||
"Participants": [
|
||||
{
|
||||
"Addr": "e0d8083f8a2d18f310bfbdc9649a83664470f46053ab53c105a054b08f9eff85",
|
||||
"PubKey": "EcVs+nTi4iFERVeBHUPePDmvknBx95co7csKj0sZNuo=",
|
||||
"DkgPubKey": "sN7XbnvZCRtg650dVCCpPK/hQ/rMTSlxrdnvzJ75zV4W/Uzk9suvjNPtyRt7PDXLDTGNimn+4X/FcJj2K6vDdgqOrr9BHwMqJXnQykcv3IV0ggIUjpMMgdbQ+0iSseyq"
|
||||
},
|
||||
{
|
||||
"Addr": "addr2",
|
||||
"PubKey": "cHVia2V5Mg==",
|
||||
"DkgPubKey": "ZGtnX3B1YmtleV8y"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The message will be consumed by your node:
|
||||
|
@ -121,11 +131,20 @@ The message will be consumed by your node:
|
|||
Now you have a pending operation in your operation pool. Get the list of pending operations:
|
||||
```
|
||||
$ ./dc4bc_cli get_operations --listen_addr localhost:8080
|
||||
Operation ID: 6d98f39d-1b24-49ce-8473-4f5d934ab2dc
|
||||
Operation: {"ID":"6d98f39d-1b24-49ce-8473-4f5d934ab2dc","Type":"state_sig_proposal_await_participants_confirmations","Payload":"W3siUGFydGljaXBhbnRJZCI6MCwiQWRkciI6ImUwZDgwODNmOGEyZDE4ZjMxMGJmYmRjOTY0OWE4MzY2NDQ3MGY0NjA1M2FiNTNjMTA1YTA1NGIwOGY5ZWZmODVcbiIsIlRocmVzaG9sZCI6Mn0seyJQYXJ0aWNpcGFudElkIjoxLCJBZGRyIjoiMTFjNTZjZmE3NGUyZTIyMTQ0NDU1NzgxMWQ0M2RlM2MzOWFmOTI3MDcxZjc5NzI4ZWRjYjBhOGY0YjE5MzZlYVxuIiwiVGhyZXNob2xkIjoyfV0=","ResultMsgs":null,"CreatedAt":"2020-09-11T14:28:54.343122+03:00","DKGIdentifier":"191fb020fd30edd891b066f72e5a5e3a","To":"","Event":""}
|
||||
DKG round ID: 3086f09822d7ba4bfb9af14c12d2c8ef
|
||||
Operation ID: 30fa9c21-b79f-4a53-a84b-e7ad574c1a51
|
||||
Description: confirm participation in the new DKG round
|
||||
Hash of the proposing DKG message - a60bd47a831cd58a96bdd4381ee15afc
|
||||
-----------------------------------------------------
|
||||
```
|
||||
|
||||
You can check the hash of the proposing DKG message:
|
||||
```
|
||||
./dc4bc_cli get_start_dkg_file_hash start_dkg_propose.json
|
||||
a60bd47a831cd58a96bdd4381ee15afc
|
||||
```
|
||||
The command returns a hash of the proposing message. If it is not equal to the hash from the list of pending operations, that means the person who proposed to start the DKG round changed the parameters that you agreed on the Conferce Call.
|
||||
|
||||
Copy the Operation ID and make the node produce a QR-code for it:
|
||||
```
|
||||
$ ./dc4bc_cli get_operation_qr 6d98f39d-1b24-49ce-8473-4f5d934ab2dc --listen_addr localhost:8080
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/depools/dc4bc/client"
|
||||
"github.com/depools/dc4bc/client/types"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/qr"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -43,17 +45,13 @@ func main() {
|
|||
proposeSignMessageCommand(),
|
||||
getUsernameCommand(),
|
||||
getPubKeyCommand(),
|
||||
getHashOfStartDKGCommand(),
|
||||
)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("Failed to execute root command: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -89,12 +87,24 @@ func getOperationsCommand() *cobra.Command {
|
|||
return fmt.Errorf("failed to get operations: %s", operations.ErrorMessage)
|
||||
}
|
||||
for _, operation := range operations.Result {
|
||||
fmt.Printf("DKG round ID: %s\n", operation.DKGIdentifier)
|
||||
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("Description: %s\n", getShortOperationDescription(operation.Type))
|
||||
if fsm.State(operation.Type) == signature_proposal_fsm.StateAwaitParticipantsConfirmations {
|
||||
payloadHash, err := calcStartDKGMessageHash(operation.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get hash of start DKG message: %w", err)
|
||||
}
|
||||
fmt.Printf("Hash of the proposing DKG message - %s\n", hex.EncodeToString(payloadHash))
|
||||
}
|
||||
if fsm.State(operation.Type) == signing_proposal_fsm.StateSigningAwaitConfirmations {
|
||||
var payload responses.SigningProposalParticipantInvitationsResponse
|
||||
if err := json.Unmarshal(operation.Payload, &payload); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal operation payload")
|
||||
}
|
||||
msgHash := md5.Sum(payload.SrcPayload)
|
||||
fmt.Printf("Hash of the message to sign - %s\n", hex.EncodeToString(msgHash[:]))
|
||||
}
|
||||
fmt.Printf("Operation: %s\n", string(operationBz))
|
||||
fmt.Println("-----------------------------------------------------")
|
||||
}
|
||||
return nil
|
||||
|
@ -102,11 +112,6 @@ func getOperationsCommand() *cobra.Command {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -265,8 +270,8 @@ func readOperationFromCameraCommand() *cobra.Command {
|
|||
|
||||
func startDKGCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "start_dkg [participants count] [threshold]",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Use: "start_dkg [proposing_file]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "sends a propose message to start a DKG process",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
|
||||
|
@ -274,61 +279,21 @@ func startDKGCommand() *cobra.Command {
|
|||
return fmt.Errorf("failed to read configuration: %v", err)
|
||||
}
|
||||
|
||||
participantsCount, err := strconv.Atoi(args[0])
|
||||
dkgProposeFileData, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get participants count: %w", err)
|
||||
return fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
if participantsCount < 0 {
|
||||
return fmt.Errorf("invalid number of participants: %d", participantsCount)
|
||||
var req requests.SignatureProposalParticipantsListRequest
|
||||
if err = json.Unmarshal(dkgProposeFileData, &req); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal dkg proposing file: %w", err)
|
||||
}
|
||||
|
||||
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)
|
||||
if len(req.Participants) == 0 || req.SigningThreshold > len(req.Participants) {
|
||||
return fmt.Errorf("invalid threshold: %d", req.SigningThreshold)
|
||||
}
|
||||
req.CreatedAt = time.Now()
|
||||
|
||||
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: ")
|
||||
addr, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read addr: %w", err)
|
||||
}
|
||||
p.Addr = string(addr)
|
||||
|
||||
fmt.Printf("Enter pubkey (base64): ")
|
||||
pubKey, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read pubKey: %w", err)
|
||||
}
|
||||
p.PubKey, err = base64.StdEncoding.DecodeString(string(pubKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode pubKey: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Enter DKGPubKey (base64): ")
|
||||
DKGPubKey, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read DKGPubKey: %w", err)
|
||||
}
|
||||
p.DkgPubKey, err = base64.StdEncoding.DecodeString(string(DKGPubKey))
|
||||
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(),
|
||||
}
|
||||
messageData := req
|
||||
messageDataBz, err := json.Marshal(messageData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal SignatureProposalParticipantsListRequest: %v\n", err)
|
||||
|
@ -346,6 +311,47 @@ func startDKGCommand() *cobra.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func getHashOfStartDKGCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get_start_dkg_file_hash [proposing_file]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "returns hash of proposing message for DKG start to verify correctness",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
dkgProposeFileData, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
var req requests.SignatureProposalParticipantsListRequest
|
||||
if err = json.Unmarshal(dkgProposeFileData, &req); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal dkg proposing file: %w", err)
|
||||
}
|
||||
|
||||
participants := DKGParticipants(req.Participants)
|
||||
sort.Sort(participants)
|
||||
|
||||
hashPayload := bytes.NewBuffer(nil)
|
||||
if _, err := hashPayload.Write([]byte(fmt.Sprintf("%d", req.SigningThreshold))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range participants {
|
||||
if _, err := hashPayload.Write(p.PubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := hashPayload.Write(p.DkgPubKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := hashPayload.Write([]byte(p.Addr)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hash := md5.Sum(hashPayload.Bytes())
|
||||
fmt.Println(hex.EncodeToString(hash[:]))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func proposeSignMessageCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "sign_data [dkg_id] [data]",
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"SigningThreshold": 3,
|
||||
"Participants": [
|
||||
{
|
||||
"Addr": "addr1",
|
||||
"PubKey": "cHVia2V5MQ==",
|
||||
"DkgPubKey": "ZGtnX3B1YmtleV8x"
|
||||
},
|
||||
{
|
||||
"Addr": "addr2",
|
||||
"PubKey": "cHVia2V5Mg==",
|
||||
"DkgPubKey": "ZGtnX3B1YmtleV8y"
|
||||
},
|
||||
{
|
||||
"Addr": "addr3",
|
||||
"PubKey": "cHVia2V5Mw==",
|
||||
"DkgPubKey": "ZGtnX3B1YmtleV8z"
|
||||
},
|
||||
{
|
||||
"Addr": "addr4",
|
||||
"PubKey": "cHVia2V5NA==",
|
||||
"DkgPubKey": "ZGtnX3B1YmtleV80"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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/signature_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"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type DKGInvitationResponse responses.SignatureProposalParticipantInvitationsResponse
|
||||
|
||||
func (d DKGInvitationResponse) Len() int { return len(d) }
|
||||
func (d DKGInvitationResponse) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d DKGInvitationResponse) Less(i, j int) bool { return d[i].Addr < d[j].Addr }
|
||||
|
||||
type DKGParticipants []*requests.SignatureProposalParticipantsEntry
|
||||
|
||||
func (d DKGParticipants) Len() int { return len(d) }
|
||||
func (d DKGParticipants) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d DKGParticipants) Less(i, j int) bool { return d[i].Addr < d[j].Addr }
|
||||
|
||||
type OperationsResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result map[string]*types.Operation `json:"result"`
|
||||
}
|
||||
|
||||
type OperationQRPathsResponse struct {
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result []string `json:"result"`
|
||||
}
|
||||
|
||||
func calcStartDKGMessageHash(payload []byte) ([]byte, error) {
|
||||
var msg DKGInvitationResponse
|
||||
if err := json.Unmarshal(payload, &msg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal payload: %w", err)
|
||||
}
|
||||
|
||||
sort.Sort(msg)
|
||||
|
||||
hashPayload := bytes.NewBuffer(nil)
|
||||
// threshold is the same for everyone
|
||||
if _, err := hashPayload.Write([]byte(fmt.Sprintf("%d", msg[0].Threshold))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range msg {
|
||||
if _, err := hashPayload.Write(p.PubKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := hashPayload.Write(p.DkgPubKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := hashPayload.Write([]byte(p.Addr)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
hash := md5.Sum(hashPayload.Bytes())
|
||||
return hash[:], nil
|
||||
}
|
||||
|
||||
func getShortOperationDescription(operationType types.OperationType) string {
|
||||
switch fsm.State(operationType) {
|
||||
case signature_proposal_fsm.StateAwaitParticipantsConfirmations:
|
||||
return "confirm participation in the new DKG round"
|
||||
case dkg_proposal_fsm.StateDkgCommitsAwaitConfirmations:
|
||||
return "send commits for the DKG round"
|
||||
case dkg_proposal_fsm.StateDkgDealsAwaitConfirmations:
|
||||
return "send deals for the DKG round"
|
||||
case dkg_proposal_fsm.StateDkgResponsesAwaitConfirmations:
|
||||
return "send responses for the DKG round"
|
||||
case dkg_proposal_fsm.StateDkgMasterKeyAwaitConfirmations:
|
||||
return "reconstruct the public key and broadcast it"
|
||||
case signing_proposal_fsm.StateSigningAwaitConfirmations:
|
||||
return "confirm participation in a new message signing"
|
||||
case signing_proposal_fsm.StateSigningAwaitPartialSigns:
|
||||
return "send your partial sign for the message"
|
||||
case signing_proposal_fsm.StateSigningPartialSignsCollected:
|
||||
return "recover full signature for the message"
|
||||
default:
|
||||
return "unknown operation"
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@ func (m *SignatureProposalFSM) actionInitSignatureProposal(inEvent fsm.Event, ar
|
|||
Addr: participant.Addr,
|
||||
Threshold: participant.Threshold,
|
||||
DkgPubKey: participant.DkgPubKey,
|
||||
PubKey: participant.PubKey,
|
||||
}
|
||||
responseData = append(responseData, responseEntry)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ type SignatureProposalParticipantInvitationEntry struct {
|
|||
Addr string
|
||||
Threshold int
|
||||
DkgPubKey []byte
|
||||
PubKey []byte
|
||||
}
|
||||
|
||||
// Public lists for proposal confirmation process
|
||||
|
|
8
qr/qr.go
8
qr/qr.go
|
@ -61,10 +61,7 @@ func (p *CameraProcessor) ReadQR() ([]byte, error) {
|
|||
}
|
||||
data, err := ReadDataFromQR(imgObject)
|
||||
if err != nil {
|
||||
if _, ok := err.(gozxing.NotFoundException); ok {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
@ -88,9 +85,6 @@ func ReadDataFromQR(img image.Image) ([]byte, error) {
|
|||
qrReader := qrcode.NewQRCodeReader()
|
||||
result, err := qrReader.Decode(bmp, nil)
|
||||
if err != nil {
|
||||
if _, ok := err.(gozxing.NotFoundException); ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("failed to decode the QR-code contents: %w", err)
|
||||
}
|
||||
return []byte(result.String()), nil
|
||||
|
|
Loading…
Reference in New Issue