diff --git a/cli/internal/cmd/sign.go b/cli/internal/cmd/sign.go index 5b045e0..f9d0d43 100644 --- a/cli/internal/cmd/sign.go +++ b/cli/internal/cmd/sign.go @@ -2,13 +2,14 @@ package cmd import ( "encoding/base64" - "encoding/hex" + "encoding/json" "errors" "fmt" "os" "github.com/randa-mu/ssv-dkg/cli" "github.com/randa-mu/ssv-dkg/shared" + "github.com/randa-mu/ssv-dkg/shared/api" "github.com/spf13/cobra" ) @@ -44,15 +45,17 @@ func Sign(cmd *cobra.Command, _ []string) { } log := shared.QuietLogger{Quiet: shortFlag} - signingOutput, err := cli.Sign(shared.Uniq(append(args, operatorFlag...)), depositData, log) + // TODO: this should probably sign something more than just the deposit data root + signingOutput, err := cli.Sign(shared.Uniq(append(args, operatorFlag...)), depositData.DepositDataRoot, log) if err != nil { shared.Exit(fmt.Sprintf("%v", err)) } - log.MaybeLog(fmt.Sprintf("✅ received signed deposit data! sessionID: %s", hex.EncodeToString(signingOutput.SessionID))) + path := cli.CreateFilename(stateDirectory, signingOutput) + + log.MaybeLog(fmt.Sprintf("✅ received signed deposit data! stored state in %s", path)) log.Log(base64.StdEncoding.EncodeToString(signingOutput.GroupSignature)) - path := cli.CreateFilename(stateDirectory, signingOutput) bytes, err := cli.StoreStateIfNotExists(path, signingOutput) if err != nil { log.Log(fmt.Sprintf("⚠️ there was an error storing the state; you should store it somewhere for resharing. Error: %v", err)) @@ -60,25 +63,31 @@ func Sign(cmd *cobra.Command, _ []string) { } } -func verifyAndGetArgs(cmd *cobra.Command) ([]string, []byte, error) { +func verifyAndGetArgs(cmd *cobra.Command) ([]string, api.UnsignedDepositData, error) { // if the operator flag isn't passed, we consume operator addresses from stdin operators, err := arrayOrReader(operatorFlag, cmd.InOrStdin()) if err != nil { - return nil, nil, errors.New("you must provider either the --operator flag or operators via stdin") + return nil, api.UnsignedDepositData{}, errors.New("you must provider either the --operator flag or operators via stdin") } if inputPathFlag == "" { - return nil, nil, errors.New("input path cannot be empty") + return nil, api.UnsignedDepositData{}, errors.New("input path cannot be empty") } // there is a default value, so this shouldn't really happen if stateDirectory == "" { - return nil, nil, errors.New("you must provide a state directory") + return nil, api.UnsignedDepositData{}, errors.New("you must provide a state directory") + } + + depositBytes, err := os.ReadFile(inputPathFlag) + if err != nil { + return nil, api.UnsignedDepositData{}, fmt.Errorf("error reading the deposit data file: %v", err) } - depositData, err := os.ReadFile(inputPathFlag) + var depositData api.UnsignedDepositData + err = json.Unmarshal(depositBytes, &depositData) if err != nil { - return nil, nil, fmt.Errorf("error reading the deposit data file: %v", err) + return nil, api.UnsignedDepositData{}, err } return operators, depositData, nil diff --git a/cli/internal/cmd/sign_test.go b/cli/internal/cmd/sign_test.go index 56a7720..a5eba13 100644 --- a/cli/internal/cmd/sign_test.go +++ b/cli/internal/cmd/sign_test.go @@ -1,18 +1,21 @@ package cmd import ( + "encoding/json" + "math/big" "os" "path" "strings" "testing" + "github.com/randa-mu/ssv-dkg/shared/api" "github.com/stretchr/testify/require" ) func TestSignCommand(t *testing.T) { tmp := t.TempDir() filepath := path.Join(tmp, "testfile") - createJunkFile(t, filepath) + createdUnsignedDepositData(t, filepath) tests := []struct { name string @@ -96,10 +99,22 @@ func TestSignCommand(t *testing.T) { } } -func createJunkFile(t *testing.T, filepath string) { +func createdUnsignedDepositData(t *testing.T, filepath string) { + data := api.UnsignedDepositData{ + WithdrawalCredentials: []byte("hello world"), + DepositDataRoot: []byte("hello world"), + DepositMessageRoot: []byte("hello world"), + Amount: api.BigInt{Int: *big.NewInt(1)}, + ForkVersion: "somefork", + NetworkName: "somenetwork", + DepositCLIVersion: "somecli", + } + + bytes, err := json.Marshal(data) + require.NoError(t, err) file, err := os.Create(filepath) require.NoError(t, err) - _, err = file.Write([]byte("hello")) + _, err = file.Write(bytes) require.NoError(t, err) err = file.Close() require.NoError(t, err) diff --git a/shared/api/cli.go b/shared/api/cli.go index df0b112..b55c33b 100644 --- a/shared/api/cli.go +++ b/shared/api/cli.go @@ -1,6 +1,27 @@ package api -import "github.com/randa-mu/ssv-dkg/shared/crypto" +import ( + "fmt" + "math/big" + + "github.com/randa-mu/ssv-dkg/shared/crypto" +) + +type UnsignedDepositData struct { + WithdrawalCredentials []byte `json:"withdrawal_credentials"` + DepositDataRoot []byte `json:"deposit_data_root"` + DepositMessageRoot []byte `json:"deposit_message_root,omitempty"` + Amount BigInt `json:"amount,omitempty"` + ForkVersion string `json:"fork_version,omitempty"` + NetworkName string `json:"network_name,omitempty"` + DepositCLIVersion string `json:"deposit_cli_version,omitempty"` +} + +type SignedDepositData struct { + UnsignedDepositData + PubKey []byte `json:"pubkey"` + Signature []byte `json:"signature"` +} type SigningOutput struct { SessionID []byte `json:"session_id"` @@ -18,3 +39,25 @@ type OperatorResponse struct { Identity crypto.Identity Response SignResponse } + +type BigInt struct { + big.Int +} + +// MarshalJSON intentionally uses a value pointer or things go wrong +func (b BigInt) MarshalJSON() ([]byte, error) { + return []byte(b.String()), nil +} + +func (b *BigInt) UnmarshalJSON(p []byte) error { + if string(p) == "null" { + return nil + } + var z big.Int + _, ok := z.SetString(string(p), 10) + if !ok { + return fmt.Errorf("not a valid big integer: %s", p) + } + b.Int = z + return nil +}