-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial pass of resharing end to end (#9)
- added local DKG state for users - created a state directory for sidecar - refactored the key to be in relation to it - implemented resharing for the same group - updated to go 1.22 - moved keys from localhost to 127 for github runner - user passes a hash of the old key share, to avoid confusion if some DKG fails for some nodes - integration test added for multiple reshares and with new groups - threshold isn't passed around, as we default to 50%+1 - added a debug logging flag - added lots of logs - graceful shutdown of stub server to avoid port contention - fixed some PR feedback, moved some things to internal
- Loading branch information
1 parent
ab69634
commit cd8ee94
Showing
36 changed files
with
1,613 additions
and
423 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"strings" | ||
|
||
"github.com/randa-mu/ssv-dkg/cli" | ||
"github.com/randa-mu/ssv-dkg/shared" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var stateFilePath string | ||
var reshareCmd = &cobra.Command{ | ||
Use: "reshare", | ||
Short: "Reshares the key for a validator cluster you have already created", | ||
Long: "Reshares the key for a validator cluster you have already created", | ||
Run: Reshare, | ||
} | ||
|
||
func init() { | ||
reshareCmd.PersistentFlags().StringArrayVarP( | ||
&operatorFlag, | ||
"operator", | ||
"o", | ||
nil, | ||
"SSV DKG node operators you wish to sign your ETH deposit data", | ||
) | ||
reshareCmd.PersistentFlags().StringVarP(&stateFilePath, | ||
"state", | ||
"s", | ||
"", | ||
"The filepath of the initial distributed key generated validator cluster that you wish to reshare. Note: this will get rewritten during execution.", | ||
) | ||
} | ||
|
||
func Reshare(cmd *cobra.Command, _ []string) { | ||
log := shared.QuietLogger{} | ||
if stateFilePath == "" { | ||
shared.Exit("you must enter the path to the state created from the initial distributed key generation") | ||
} | ||
|
||
// if the operator flag isn't passed, we consume operator addresses from stdin | ||
operators, err := arrayOrReader(operatorFlag, cmd.InOrStdin()) | ||
if err != nil { | ||
shared.Exit("you must pass your new set of operators either via the operator flag or from stdin") | ||
} | ||
|
||
state, err := cli.LoadState(stateFilePath) | ||
if err != nil { | ||
shared.Exit(fmt.Sprintf("❌ tried to load state from %s but it failed: %v", stateFilePath, err)) | ||
} | ||
|
||
nextState, err := cli.Reshare(operators, state, log) | ||
if err != nil { | ||
shared.Exit(fmt.Sprintf("❌ resharing failed: %v", err)) | ||
} | ||
|
||
bytes, err := cli.StoreState(stateFilePath, nextState) | ||
if err != nil { | ||
fmt.Printf("⚠️ there was an error storing your state; printing it to the console so you can save it in a flat file. Err: %v\n", err) | ||
fmt.Println(string(bytes)) | ||
shared.Exit("") | ||
} | ||
|
||
log.MaybeLog(fmt.Sprintf("✅ reshare completed successfully. Encrypted shares stored in %s", stateFilePath)) | ||
} | ||
|
||
func operatorsOrStdin(cmd *cobra.Command) ([]string, error) { | ||
if len(operatorFlag) != 0 { | ||
return operatorFlag, nil | ||
} | ||
// if the operator flag isn't passed, we consume operator addresses from stdin | ||
stdin, err := io.ReadAll(cmd.InOrStdin()) | ||
if err != nil { | ||
return nil, errors.New("error reading from stdin") | ||
} | ||
|
||
operatorString := strings.Trim(string(stdin), "\n") | ||
if operatorString == "" { | ||
return nil, errors.New("you must provider either the --operator flag or operators via stdin") | ||
} | ||
|
||
return strings.Split(operatorString, " "), nil | ||
} | ||
|
||
// arrayOrReader returns the array if it's non-empty, or reads an array of strings from the provided `Reader` if it's empty | ||
func arrayOrReader(arr []string, r io.Reader) ([]string, error) { | ||
if len(arr) != 0 { | ||
return arr, nil | ||
} | ||
|
||
bytes, err := io.ReadAll(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
lines := strings.Trim(string(bytes), "\n") | ||
if lines == "" { | ||
return nil, errors.New("reader was empty") | ||
} | ||
|
||
return strings.Split(lines, " "), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package cmd | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestSignCommand(t *testing.T) { | ||
tmp := t.TempDir() | ||
filepath := path.Join(tmp, "testfile") | ||
createJunkFile(t, filepath) | ||
|
||
tests := []struct { | ||
name string | ||
args []string | ||
stdin *strings.Reader | ||
shouldError bool | ||
}{ | ||
{ | ||
name: "expected flags succeeds", | ||
shouldError: false, | ||
args: []string{ | ||
"ssv-dkg", | ||
"sign", | ||
"--input", filepath, | ||
"--output", filepath, | ||
"--operator", "1,http://127.0.0.1:8081", | ||
"--operator", "2,http://127.0.0.1:8082", | ||
"--operator", "3,http://127.0.0.1:8083", | ||
}, | ||
}, | ||
{ | ||
name: "operators from stdin works", | ||
shouldError: false, | ||
args: []string{ | ||
"ssv-dkg", | ||
"sign", | ||
"--input", filepath, | ||
"--output", filepath, | ||
"--operator", "1,http://127.0.0.1:8081", | ||
"--operator", "2,http://127.0.0.1:8082", | ||
"--operator", "3,http://127.0.0.1:8083", | ||
}, | ||
stdin: strings.NewReader("1,http://127.0.0.1:8081 2,http://127.0.0.1:8082 3,http://127.0.0.1:8083"), | ||
}, | ||
{ | ||
name: "no input returns error", | ||
shouldError: true, | ||
args: []string{ | ||
"ssv-dkg", | ||
"sign", | ||
"--output", filepath, | ||
"--operator", "1,http://127.0.0.1:8081", | ||
"--operator", "2,http://127.0.0.1:8082", | ||
"--operator", "3,http://127.0.0.1:8083", | ||
}, | ||
}, | ||
{ | ||
name: "no operators returns error", | ||
shouldError: true, | ||
args: []string{ | ||
"ssv-dkg", | ||
"sign", | ||
"--input", filepath, | ||
"--output", filepath, | ||
}, | ||
}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
if test.stdin == nil { | ||
test.stdin = strings.NewReader("") | ||
} | ||
signCmd.SetIn(test.stdin) | ||
signCmd.SetArgs(test.args) | ||
err := signCmd.ParseFlags(test.args) | ||
require.NoError(t, err) | ||
_, _, err = verifyAndGetArgs(signCmd) | ||
|
||
t.Cleanup(func() { | ||
operatorFlag = nil | ||
inputPathFlag = "" | ||
shortFlag = false | ||
stateDirectory = "" | ||
}) | ||
if test.shouldError && err == nil { | ||
t.Fatalf("expected err but got nil") | ||
} else if !test.shouldError && err != nil { | ||
t.Fatalf("expected no err but got: %v", err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func createJunkFile(t *testing.T, filepath string) { | ||
file, err := os.Create(filepath) | ||
require.NoError(t, err) | ||
_, err = file.Write([]byte("hello")) | ||
require.NoError(t, err) | ||
err = file.Close() | ||
require.NoError(t, err) | ||
} |
Oops, something went wrong.