-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b1699b0
commit 777aa59
Showing
11 changed files
with
652 additions
and
21 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
.PHONY: help build | ||
.PHONY: help build test | ||
|
||
include .env | ||
|
||
|
@@ -7,4 +7,11 @@ help: | |
|
||
build: ## Compile the binary | ||
@mkdir -p bin | ||
@go build -o bin/$(APP_NAME) cmd/$(APP_NAME)/main.go | ||
@go build -o bin/$(APP_NAME) cmd/$(APP_NAME)/main.go | ||
|
||
mocks: ## generates mocks | ||
go install go.uber.org/mock/[email protected] | ||
go generate ./... | ||
|
||
test: ## runs all tests | ||
go test ./... -covermode=atomic |
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,20 @@ | ||
package pkg | ||
|
||
import ( | ||
"github.com/Layr-Labs/eigenlayer-cli/pkg/operator" | ||
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func OperatorCmd(p utils.Prompter) *cli.Command { | ||
var operatorCmd = &cli.Command{ | ||
Name: "operator", | ||
Usage: "Execute onchain operations for the operator", | ||
Subcommands: []*cli.Command{ | ||
operator.KeysCmd(p), | ||
}, | ||
} | ||
|
||
return operatorCmd | ||
|
||
} |
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 |
---|---|---|
@@ -1,22 +1,192 @@ | ||
package keys | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
|
||
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils" | ||
"github.com/Layr-Labs/eigensdk-go/crypto/bls" | ||
sdkEcdsa "github.com/Layr-Labs/eigensdk-go/crypto/ecdsa" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/urfave/cli/v2" | ||
passwordvalidator "github.com/wagslane/go-password-validator" | ||
) | ||
|
||
const ( | ||
OperatorKeystoreSubFolder = ".eigenlayer/operator_keys" | ||
|
||
KeyTypeECDSA = "ecdsa" | ||
KeyTypeBLS = "bls" | ||
|
||
// MinEntropyBits For password validation | ||
MinEntropyBits = 70 | ||
) | ||
|
||
func CreateCmd(p utils.Prompter) *cli.Command { | ||
|
||
createCmd := &cli.Command{ | ||
Name: "create", | ||
Action: func(context *cli.Context) error { | ||
confirm, err := p.Confirm("Would you like to populate the operator config file?") | ||
if err != nil { | ||
Name: "create", | ||
Usage: "Used to create encrypted keys in local keystore", | ||
UsageText: "create --key-type <key-type> [flags] <keyname>", | ||
Description: ` | ||
Used to create ecdsa and bls key in local keystore | ||
keyname (required) - This will be the name of the created key file. It will be saved as <keyname>.ecdsa.key.json or <keyname>.bls.key.json | ||
use --key-type ecdsa/bls to create ecdsa/bls key. | ||
It will prompt for password to encrypt the key, which is optional but highly recommended. | ||
If you want to create a key with weak/no password, use --insecure flag. Do NOT use those keys in production | ||
This command will create keys in $HOME/.eigenlayer/operator_keys/ location | ||
`, | ||
Flags: []cli.Flag{ | ||
&KeyTypeFlag, | ||
&InsecureFlag, | ||
}, | ||
|
||
Action: func(ctx *cli.Context) error { | ||
args := ctx.Args() | ||
if args.Len() != 1 { | ||
return fmt.Errorf("%w: accepts 1 arg, received %d", ErrInvalidNumberOfArgs, args.Len()) | ||
} | ||
|
||
keyName := args.Get(0) | ||
if err := validateKeyName(keyName); err != nil { | ||
return err | ||
} | ||
fmt.Println(confirm) | ||
return nil | ||
|
||
keyType := ctx.String(KeyTypeFlag.Name) | ||
insecure := ctx.Bool(InsecureFlag.Name) | ||
|
||
switch keyType { | ||
case KeyTypeECDSA: | ||
privateKey, err := crypto.GenerateKey() | ||
if err != nil { | ||
return err | ||
} | ||
return saveEcdsaKey(keyName, p, privateKey, insecure) | ||
case KeyTypeBLS: | ||
blsKeyPair, err := bls.GenRandomBlsKeys() | ||
if err != nil { | ||
return err | ||
} | ||
return saveBlsKey(keyName, p, blsKeyPair, insecure) | ||
default: | ||
return ErrInvalidKeyType | ||
} | ||
}, | ||
} | ||
return createCmd | ||
} | ||
|
||
func validateKeyName(keyName string) error { | ||
if len(keyName) == 0 { | ||
return ErrEmptyKeyName | ||
} | ||
|
||
if match, _ := regexp.MatchString("\\s", keyName); match { | ||
return ErrKeyContainsWhitespaces | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func saveBlsKey(keyName string, p utils.Prompter, keyPair *bls.KeyPair, insecure bool) error { | ||
homePath, err := os.UserHomeDir() | ||
if err != nil { | ||
return err | ||
} | ||
keyFileName := keyName + ".bls.key.json" | ||
fileLoc := filepath.Clean(filepath.Join(homePath, OperatorKeystoreSubFolder, keyFileName)) | ||
if checkIfKeyExists(fileLoc) { | ||
return errors.New("key name already exists. Please choose a different name") | ||
} | ||
password, err := p.InputHiddenString("Enter password to encrypt the bls private key:", "", | ||
func(s string) error { | ||
if insecure { | ||
return nil | ||
} | ||
return validatePassword(s) | ||
}, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = keyPair.SaveToFile(fileLoc, password) | ||
if err != nil { | ||
return err | ||
} | ||
// TODO: display it using `less` of `vi` so that it is not saved in terminal history | ||
fmt.Println("BLS Private Key: " + keyPair.PrivKey.String()) | ||
fmt.Println("Please backup the above private key in safe place.") | ||
fmt.Println() | ||
fmt.Println("BLS Pub key: " + keyPair.PubKey.String()) | ||
fmt.Println("Key location: " + fileLoc) | ||
return nil | ||
} | ||
|
||
func saveEcdsaKey(keyName string, p utils.Prompter, privateKey *ecdsa.PrivateKey, insecure bool) error { | ||
homePath, err := os.UserHomeDir() | ||
if err != nil { | ||
return err | ||
} | ||
keyFileName := keyName + ".ecdsa.key.json" | ||
fileLoc := filepath.Clean(filepath.Join(homePath, OperatorKeystoreSubFolder, keyFileName)) | ||
if checkIfKeyExists(fileLoc) { | ||
return errors.New("key name already exists. Please choose a different name") | ||
} | ||
|
||
password, err := p.InputHiddenString("Enter password to encrypt the ecdsa private key:", "", | ||
func(s string) error { | ||
if insecure { | ||
return nil | ||
} | ||
return validatePassword(s) | ||
}, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = sdkEcdsa.WriteKey(fileLoc, privateKey, password) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
privateKeyHex := hex.EncodeToString(privateKey.D.Bytes()) | ||
// TODO: display it using `less` of `vi` so that it is not saved in terminal history | ||
fmt.Println("ECDSA Private Key (Hex): ", privateKeyHex) | ||
fmt.Println("Please backup the above private key hex in safe place.") | ||
fmt.Println() | ||
fmt.Println("Key location: " + fileLoc) | ||
publicKey := privateKey.Public() | ||
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) | ||
if !ok { | ||
return err | ||
} | ||
publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) | ||
fmt.Println("Public Key hex: ", hexutil.Encode(publicKeyBytes)[4:]) | ||
address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() | ||
fmt.Println("Ethereum Address", address) | ||
return nil | ||
} | ||
|
||
func checkIfKeyExists(fileLoc string) bool { | ||
_, err := os.Stat(fileLoc) | ||
return !os.IsNotExist(err) | ||
} | ||
|
||
func validatePassword(password string) error { | ||
err := passwordvalidator.Validate(password, MinEntropyBits) | ||
if err != nil { | ||
fmt.Println("if you want to create keys for testing with weak/no password, use --insecure flag. Do NOT use those keys in production") | ||
} | ||
return err | ||
} |
Oops, something went wrong.