Skip to content

Commit

Permalink
add keys create (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
shrimalmadhur committed Nov 30, 2023
1 parent b1699b0 commit 777aa59
Show file tree
Hide file tree
Showing 11 changed files with 652 additions and 21 deletions.
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help build
.PHONY: help build test

include .env

Expand All @@ -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
7 changes: 4 additions & 3 deletions cmd/eigenlayer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package main

import (
"fmt"
"github.com/Layr-Labs/eigenlayer-cli/pkg/operator"
"os"

"github.com/Layr-Labs/eigenlayer-cli/pkg"
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils"
"github.com/urfave/cli/v2"
"os"
)

func main() {
Expand All @@ -17,7 +18,7 @@ func main() {

// Initialize the dependencies
prompter := utils.NewPrompter()
app.Commands = append(app.Commands, operator.KeysCmd(prompter))
app.Commands = append(app.Commands, pkg.OperatorCmd(prompter))

if err := app.Run(os.Args); err != nil {
_, err := fmt.Fprintln(os.Stderr, err)
Expand Down
36 changes: 32 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,46 @@ go 1.21.0

require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/Layr-Labs/eigensdk-go v0.0.8
github.com/ethereum/go-ethereum v1.13.5
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
github.com/wagslane/go-password-validator v0.3.0
go.uber.org/mock v0.3.0
)

require (
github.com/bits-and-blooms/bitset v1.7.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/holiman/uint256 v1.2.3 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
153 changes: 147 additions & 6 deletions go.sum

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions pkg/operator.go
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

}
182 changes: 176 additions & 6 deletions pkg/operator/keys/create.go
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
}
Loading

0 comments on commit 777aa59

Please sign in to comment.