Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Multisig Wallets #120

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3ede08f
add gnosis js examples
pavelkrolevets Sep 11, 2024
df2b860
add multisig support
pavelkrolevets Sep 12, 2024
4723ca2
update on-chain gnosis example
pavelkrolevets Sep 12, 2024
5ac76cb
add off-chain multisig gnosis example
pavelkrolevets Sep 12, 2024
896508c
add support to multisig wallets by seperating the stage of submitting…
alan-ssvlabs Sep 13, 2024
63e8b53
update reshare bulk marshaling
pavelkrolevets Sep 13, 2024
538db76
update operator handlers
alan-ssvlabs Sep 16, 2024
5312d79
update integration test
alan-ssvlabs Sep 16, 2024
9cf8c88
update Reshare and Resign hash from hash of tree root to hash of mars…
alan-ssvlabs Sep 16, 2024
5bf3667
working json bulk resign/reshare decoding
pavelkrolevets Sep 16, 2024
f06eea0
Merge remote-tracking branch 'origin/multisig' into gnosis_multisig
pavelkrolevets Sep 16, 2024
e0be332
Merge pull request #121 from ssvlabs/gnosis_multisig
alan-ssvlabs Sep 16, 2024
b73a2bf
fix bugs in hashing messages and initiating operator resign/reshare m…
alan-ssvlabs Sep 16, 2024
d5da809
fix operators marshalling
pavelkrolevets Sep 16, 2024
f83dfcf
Update test
alan-ssvlabs Sep 16, 2024
f0cb6e9
simplify code
alan-ssvlabs Sep 16, 2024
cd0759c
add error handling
alan-ssvlabs Sep 16, 2024
d80df54
revert changes on operator side to process resign/reshare message and…
alan-ssvlabs Sep 22, 2024
b64eb84
udpate initiator inputs to start resign/reshare to pass in signatures…
alan-ssvlabs Sep 22, 2024
f566ab1
update test
alan-ssvlabs Sep 22, 2024
fa7aa71
update variable naming in multisig tests
alan-ssvlabs Sep 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 3 additions & 17 deletions cli/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ const (
serverTLSCertPath = "serverTLSCertPath"
serverTLSKeyPath = "serverTLSKeyPath"
proofsFilePath = "proofsFilePath"
ethKeystorePath = "ethKeystorePath"
ethKeystorePass = "ethKeystorePass"
ethEndpointURL = "ethEndpointURL"
signedReshare = "signedReshare"
signatures = "signatures"
)

// WithdrawAddressFlag adds withdraw address flag to the command
Expand Down Expand Up @@ -151,27 +149,15 @@ func OperatorIDFlag(c *cobra.Command) {
func ProofsFilePath(c *cobra.Command) {
AddPersistentStringFlag(c, proofsFilePath, "proofs.json", "Path to proofs file", false)
}

// KeystoreFilePath
func KeystoreFilePath(c *cobra.Command) {
AddPersistentStringFlag(c, ethKeystorePath, "keystore.json", "Path to ethereum keystore json file", false)
}

// KeystoreFilePass
func KeystoreFilePass(c *cobra.Command) {
AddPersistentStringFlag(c, ethKeystorePass, "", "Password to decrypt ethereum keystore json file", false)
func SignaturesFlag(c *cobra.Command) {
AddPersistentStringFlag(c, signatures, "", "Stringified signature(s) for the resign/reshare message", false)
}

// EthEndpointURL
func EthEndpointURL(c *cobra.Command) {
AddPersistentStringFlag(c, ethEndpointURL, "http://127.0.0.1:8545", "Ethereum node endpoint URL", false)
}

// KeystoreFilePath
func SignedReshareFilePath(c *cobra.Command) {
AddPersistentStringFlag(c, ethKeystorePath, "signed_reshare.json", "Path to signed reshare message json file", false)
}

// AddPersistentStringFlag adds a string flag to the command
func AddPersistentStringFlag(c *cobra.Command, flag, value, description string, isRequired bool) {
req := ""
Expand Down
2 changes: 1 addition & 1 deletion cli/initiator/initiator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var StartDKG = &cobra.Command{
}()
logger.Info("🪛 Initiator`s", zap.String("Version", cmd.Version))
// Load operators TODO: add more sources.
operatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.OperatorIDs)
operatorIDs, err := cli_utils.StringSliceToUintArray(cli_utils.OperatorIDs)
if err != nil {
logger.Fatal("😥 Failed to load participants: ", zap.Error(err))
}
Expand Down
35 changes: 21 additions & 14 deletions cli/initiator/reshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ package initiator
import (
"fmt"
"log"
"os"
"path/filepath"

e2m_core "github.com/bloxapp/eth2-key-manager/core"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/spf13/cobra"
cli_utils "github.com/ssvlabs/ssv-dkg/cli/utils"
"github.com/ssvlabs/ssv-dkg/pkgs/initiator"
Expand Down Expand Up @@ -56,11 +53,11 @@ var StartReshare = &cobra.Command{
if err != nil {
logger.Fatal("😥 Failed to load operators: ", zap.Error(err))
}
oldOperatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.OperatorIDs)
oldOperatorIDs, err := cli_utils.StringSliceToUintArray(cli_utils.OperatorIDs)
if err != nil {
logger.Fatal("😥 Failed to load participants: ", zap.Error(err))
}
newOperatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.NewOperatorIDs)
newOperatorIDs, err := cli_utils.StringSliceToUintArray(cli_utils.NewOperatorIDs)
if err != nil {
logger.Fatal("😥 Failed to load new participants: ", zap.Error(err))
}
Expand All @@ -79,21 +76,31 @@ var StartReshare = &cobra.Command{
if ethNetwork == "" {
logger.Fatal("😥 Cant recognize eth network")
}
// Open ethereum keystore
jsonBytes, err := os.ReadFile(cli_utils.KeystorePath)
signatures, err := cli_utils.SignaturesStringToBytes(cli_utils.Signatures)
if err != nil {
return err
logger.Fatal("😥 Failed to load signatures: ", zap.Error(err))
}
keyStorePassword, err := os.ReadFile(filepath.Clean(cli_utils.KeystorePass))
// Contruct the resign message
rMsg, err := dkgInitiator.ConstructReshareMessage(
oldOperatorIDs,
newOperatorIDs,
signedProofs[0][0].Proof.ValidatorPubKey,
ethNetwork,
cli_utils.WithdrawAddress[:],
cli_utils.OwnerAddress,
cli_utils.Nonce,
signedProofs[0],
)
Comment on lines +83 to +93

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the issue I had the requirement:

In the cli pass the resign/reshare messages along with their signatures as a string in serialized form (Talk with product about the serialization).

But maybe you are doing this in bulk?

Anyhow, the current way is good enough for me and if @RaekwonIII wants to change this he can talk to @MatusKysel

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed in this cli users pass the data for generating the reshare/resign messages instead of the messages themselves. Everything can be a string now including the signedProofs (updated in the bulk PR), so from the users perspective it would be pretty much the same. But of course we can update this if required.

if err != nil {
return fmt.Errorf("😥 Error reading password file: %s", err)
logger.Fatal("😥 Failed to construct resign message: ", zap.Error(err))
}
sk, err := keystore.DecryptKey(jsonBytes, string(keyStorePassword))
if err != nil {
return err
// Append the signatures
signedReshare := &wire.SignedReshare{
Message: rMsg,
Signature: signatures,
}
// Start the ceremony
depositData, keyShares, proof, err := dkgInitiator.StartResharing(id, oldOperatorIDs, newOperatorIDs, signedProofs[0], sk.PrivateKey, ethNetwork, cli_utils.WithdrawAddress[:], cli_utils.OwnerAddress, cli_utils.Nonce)
depositData, keyShares, proof, err := dkgInitiator.StartResharing(id, signedReshare)
if err != nil {
logger.Fatal("😥 Failed to initiate DKG ceremony: ", zap.Error(err))
}
Expand Down
38 changes: 22 additions & 16 deletions cli/initiator/resigning.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import (
"encoding/hex"
"fmt"
"log"
"os"
"path/filepath"

e2m_core "github.com/bloxapp/eth2-key-manager/core"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/sourcegraph/conc/pool"
"github.com/spf13/cobra"
cli_utils "github.com/ssvlabs/ssv-dkg/cli/utils"
Expand Down Expand Up @@ -58,7 +55,7 @@ var StartResigning = &cobra.Command{
if err != nil {
logger.Fatal("😥 Failed to load operators: ", zap.Error(err))
}
operatorIDs, err := cli_utils.StingSliceToUintArray(cli_utils.OperatorIDs)
operatorIDs, err := cli_utils.StringSliceToUintArray(cli_utils.OperatorIDs)
if err != nil {
logger.Fatal("😥 Failed to load participants: ", zap.Error(err))
}
Expand All @@ -70,18 +67,9 @@ var StartResigning = &cobra.Command{
if err != nil {
logger.Fatal("😥 Failed to read proofs json file:", zap.Error(err))
}
// Open ethereum keystore
jsonBytes, err := os.ReadFile(cli_utils.KeystorePath)
signatures, err := cli_utils.SignaturesStringToBytes(cli_utils.Signatures)
if err != nil {
return err
}
keyStorePassword, err := os.ReadFile(filepath.Clean(cli_utils.KeystorePass))
if err != nil {
return fmt.Errorf("😥 Error reading password file: %s", err)
}
sk, err := keystore.DecryptKey(jsonBytes, string(keyStorePassword))
if err != nil {
return err
logger.Fatal("😥 Failed to load signatures: ", zap.Error(err))
}
// start the ceremony
ctx := context.Background()
Expand All @@ -97,8 +85,26 @@ var StartResigning = &cobra.Command{
// Create a new ID.
id := spec.NewID()
nonce := cli_utils.Nonce + uint64(i)
// Reconstruct the resign message
rMsg, err := dkgInitiator.ConstructResignMessage(
operatorIDs,
arrayOfSignedProofs[i][0].Proof.ValidatorPubKey,
ethNetwork,
cli_utils.WithdrawAddress[:],
cli_utils.OwnerAddress,
nonce,
arrayOfSignedProofs[i],
)
if err != nil {
return nil, err
}
// Append the signatures
signedResign := &wire.SignedResign{
Message: rMsg,
Signature: signatures,
}
// Perform the resigning ceremony
depositData, keyShares, proofs, err := dkgInitiator.StartResigning(id, operatorIDs, arrayOfSignedProofs[i], sk.PrivateKey, ethNetwork, cli_utils.WithdrawAddress.Bytes(), cli_utils.OwnerAddress, nonce)
depositData, keyShares, proofs, err := dkgInitiator.StartResigning(id, signedResign)
if err != nil {
return nil, err
}
Expand Down
97 changes: 67 additions & 30 deletions cli/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ var (
var (
ProofsFilePath string
NewOperatorIDs []string
KeystorePath string
KeystorePass string
Signatures string
)

// SetViperConfig reads a yaml config file if provided
Expand Down Expand Up @@ -200,6 +199,35 @@ func SetVerifyFlags(cmd *cobra.Command) {
flags.AddPersistentStringFlag(cmd, "owner", "", "Owner address", true)
}

func SetGenerateResignMsgFlags(cmd *cobra.Command) {
SetBaseFlags(cmd)
flags.OperatorsInfoFlag(cmd)
flags.OperatorsInfoPathFlag(cmd)
flags.OperatorIDsFlag(cmd)
flags.OwnerAddressFlag(cmd)
flags.NonceFlag(cmd)
flags.NetworkFlag(cmd)
flags.WithdrawAddressFlag(cmd)
flags.ProofsFilePath(cmd)
flags.ClientCACertPathFlag(cmd)
flags.EthEndpointURL(cmd)
}

func SetGenerateReshareMsgFlags(cmd *cobra.Command) {
SetBaseFlags(cmd)
flags.OperatorsInfoFlag(cmd)
flags.OperatorsInfoPathFlag(cmd)
flags.OperatorIDsFlag(cmd)
flags.NewOperatorIDsFlag(cmd)
flags.WithdrawAddressFlag(cmd)
flags.OwnerAddressFlag(cmd)
flags.NonceFlag(cmd)
flags.NetworkFlag(cmd)
flags.ProofsFilePath(cmd)
flags.ClientCACertPathFlag(cmd)
flags.EthEndpointURL(cmd)
}

func SetResigningFlags(cmd *cobra.Command) {
SetBaseFlags(cmd)
flags.OperatorsInfoFlag(cmd)
Expand All @@ -211,8 +239,7 @@ func SetResigningFlags(cmd *cobra.Command) {
flags.WithdrawAddressFlag(cmd)
flags.ProofsFilePath(cmd)
flags.ClientCACertPathFlag(cmd)
flags.KeystoreFilePath(cmd)
flags.KeystoreFilePass(cmd)
flags.SignaturesFlag(cmd)
flags.EthEndpointURL(cmd)
}

Expand All @@ -228,8 +255,7 @@ func SetReshareFlags(cmd *cobra.Command) {
flags.NetworkFlag(cmd)
flags.ProofsFilePath(cmd)
flags.ClientCACertPathFlag(cmd)
flags.KeystoreFilePath(cmd)
flags.KeystoreFilePass(cmd)
flags.SignaturesFlag(cmd)
flags.EthEndpointURL(cmd)
}

Expand Down Expand Up @@ -397,10 +423,7 @@ func BindResigningFlags(cmd *cobra.Command) error {
if err := viper.BindPFlag("network", cmd.Flags().Lookup("network")); err != nil {
return err
}
if err := viper.BindPFlag("ethKeystorePath", cmd.PersistentFlags().Lookup("ethKeystorePath")); err != nil {
return err
}
if err := viper.BindPFlag("ethKeystorePass", cmd.PersistentFlags().Lookup("ethKeystorePass")); err != nil {
if err := viper.BindPFlag("signatures", cmd.PersistentFlags().Lookup("signatures")); err != nil {
return err
}
OperatorIDs = viper.GetStringSlice("operatorIDs")
Expand Down Expand Up @@ -453,13 +476,9 @@ func BindResigningFlags(cmd *cobra.Command) error {
if err != nil {
return fmt.Errorf("😥 Failed to parse owner address: %s", err)
}
KeystorePath = viper.GetString("ethKeystorePath")
if strings.Contains(KeystorePath, "../") {
return fmt.Errorf("😥 ethKeystorePath should not contain traversal")
}
KeystorePass = viper.GetString("ethKeystorePass")
if strings.Contains(KeystorePath, "../") {
return fmt.Errorf("😥 ethKeystorePass should not contain traversal")
Signatures = viper.GetString("signatures")
if Signatures == "" {
return fmt.Errorf("😥 Failed to get signature flag value")
}
return nil
}
Expand Down Expand Up @@ -499,10 +518,7 @@ func BindReshareFlags(cmd *cobra.Command) error {
if err := viper.BindPFlag("proofsFilePath", cmd.PersistentFlags().Lookup("proofsFilePath")); err != nil {
return err
}
if err := viper.BindPFlag("ethKeystorePath", cmd.PersistentFlags().Lookup("ethKeystorePath")); err != nil {
return err
}
if err := viper.BindPFlag("ethKeystorePass", cmd.PersistentFlags().Lookup("ethKeystorePass")); err != nil {
if err := viper.BindPFlag("signatures", cmd.PersistentFlags().Lookup("signatures")); err != nil {
return err
}
OperatorsInfoPath = viper.GetString("operatorsInfoPath")
Expand Down Expand Up @@ -559,13 +575,9 @@ func BindReshareFlags(cmd *cobra.Command) error {
return fmt.Errorf("😥 clientCACertPath flag should not contain traversal")
}
}
KeystorePath = viper.GetString("ethKeystorePath")
if strings.Contains(KeystorePath, "../") {
return fmt.Errorf("😥 ethKeystorePath should not contain traversal")
}
KeystorePass = viper.GetString("ethKeystorePass")
if strings.Contains(KeystorePath, "../") {
return fmt.Errorf("😥 ethKeystorePass should not contain traversal")
Signatures = viper.GetString("signature")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Signatures = viper.GetString("signature")
Signatures = viper.GetString("signatures")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in Bulk PR

if Signatures == "" {
return fmt.Errorf("😥 Failed to get signature flag value")
MatusKysel marked this conversation as resolved.
Show resolved Hide resolved
}
return nil
}
Expand Down Expand Up @@ -678,8 +690,8 @@ func BindVerifyFlags(cmd *cobra.Command) error {
return nil
}

// StingSliceToUintArray converts the string slice to uint64 slice
func StingSliceToUintArray(flagdata []string) ([]uint64, error) {
// StringSliceToUintArray converts the string slice to uint64 slice
func StringSliceToUintArray(flagdata []string) ([]uint64, error) {
partsarr := make([]uint64, 0, len(flagdata))
for i := 0; i < len(flagdata); i++ {
opid, err := strconv.ParseUint(flagdata[i], 10, strconv.IntSize)
Expand Down Expand Up @@ -726,6 +738,14 @@ func LoadOperators(logger *zap.Logger) (wire.OperatorsCLI, error) {
return operators, nil
}

func SignaturesStringToBytes(signatures string) ([]byte, error) {
sig, err := hex.DecodeString(signatures)
if err != nil {
return nil, fmt.Errorf("😥 Failed to parse signatures: %s", err)
}
return sig, nil
}

func WriteResults(
logger *zap.Logger,
depositDataArr []*wire.DepositDataCLI,
Expand Down Expand Up @@ -937,6 +957,23 @@ func WriteProofs(proofs []*wire.SignedProof, dir string) error {
return nil
}

func WriteMessage(msg interface{}, outputPath string, msgType string) (err error) {
switch msgType {
case "resign":
finalPath := fmt.Sprintf("%s/resign.json", outputPath)
err = utils.WriteJSON(finalPath, msg)
case "reshare":
finalPath := fmt.Sprintf("%s/reshare.json", outputPath)
err = utils.WriteJSON(finalPath, msg)
default:
return fmt.Errorf("unknown message type: %s", msgType)
}
if err != nil {
return fmt.Errorf("failed writing data file: %w, %v", err, msg)
}
return nil
}

func createDirIfNotExist(path string) error {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
Expand Down
Loading
Loading