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

implemented keystore support #29

Merged
merged 5 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o mev-commit-oracle ./cmd/main.go
FROM alpine:latest

COPY --from=builder /app/mev-commit-oracle /usr/local/bin/mev-commit-oracle
COPY --from=builder /app/keystore /keystore
COPY --from=builder /app/config.yaml /config.yaml
COPY --from=builder /app/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
Expand Down
104 changes: 91 additions & 13 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import (
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
contracts "github.com/primevprotocol/contracts-abi/config"
"github.com/primevprotocol/mev-oracle/pkg/keysigner"
"github.com/primevprotocol/mev-oracle/pkg/node"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
Expand All @@ -23,6 +26,7 @@ const (
defaultHTTPPort = 8080
defaultConfigDir = "~/.mev-commit-oracle"
defaultKeyFile = "key"
defaultKeystore = "keystore"
)

var (
Expand Down Expand Up @@ -138,6 +142,19 @@ var (
Usage: "Override winners for testing",
EnvVars: []string{"MEV_ORACLE_OVERRIDE_WINNERS"},
})

optionKeystorePassword = altsrc.NewStringFlag(&cli.StringFlag{
Name: "keystore-password",
Usage: "use to access keystore",
EnvVars: []string{"MEV_COMMIT_KEYSTORE_PASSWORD"},
})

optionKeystorePath = altsrc.NewStringFlag(&cli.StringFlag{
Name: "keystore-path",
Usage: "path to keystore location",
EnvVars: []string{"MEV_COMMIT_KEYSTORE_PATH"},
Value: filepath.Join(defaultConfigDir, defaultKeystore),
})
)

func main() {
Expand All @@ -157,6 +174,8 @@ func main() {
optionPgDbname,
optionLaggerdMode,
optionOverrideWinners,
optionKeystorePath,
optionKeystorePassword,
}
app := &cli.App{
Name: "mev-oracle",
Expand All @@ -168,7 +187,7 @@ func main() {
Flags: flags,
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc(optionConfig.Name)),
Action: func(c *cli.Context) error {
return start(c)
return initializeApplication(c)
},
},
}}
Expand Down Expand Up @@ -244,14 +263,30 @@ func getEthAddressFromPubKey(key *ecdsa.PublicKey) common.Address {
return common.BytesToAddress(address)
}

func start(c *cli.Context) error {
privKeyFile, err := resolveFilePath(c.String(optionPrivKeyFile.Name))
if err != nil {
return fmt.Errorf("failed to get private key file path: %w", err)
func initializeApplication(c *cli.Context) error {
if err := verifyKeystorePasswordPresence(c); err != nil {
return err
}
if err := launchOracleWithConfig(c); err != nil {
return err
}
return nil
}

if err := createKeyIfNotExists(c, privKeyFile); err != nil {
return fmt.Errorf("failed to create private key: %w", err)
// verifyKeystorePasswordPresence checks for the presence of a keystore password.
// it returns error, if keystore path is set and keystore password is not
func verifyKeystorePasswordPresence(c *cli.Context) error {
if c.IsSet(optionKeystorePath.Name) && !c.IsSet(optionKeystorePassword.Name) {
return cli.Exit("Password for encrypted keystore is missing", 1)
}
return nil
}

// launchOracleWithConfig configures and starts the oracle based on the CLI context or config.yaml file.
func launchOracleWithConfig(c *cli.Context) error {
keySigner, err := setupKeySigner(c)
if err != nil {
return fmt.Errorf("failed to setup key signer: %w", err)
}

lvl, _ := zerolog.ParseLevel(c.String(optionLogLevel.Name))
Expand All @@ -260,13 +295,8 @@ func start(c *cli.Context) error {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = log.Output(os.Stdout).With().Caller().Logger()

privKey, err := crypto.LoadECDSA(privKeyFile)
if err != nil {
return fmt.Errorf("failed to load private key from file '%s': %w", privKeyFile, err)
}

nd, err := node.NewNode(&node.Options{
PrivateKey: privKey,
KeySigner: keySigner,
HTTPPort: c.Int(optionHTTPPort.Name),
L1RPCUrl: c.String(optionL1RPCUrl.Name),
SettlementRPCUrl: c.String(optionSettlementRPCUrl.Name),
Expand Down Expand Up @@ -305,3 +335,51 @@ func start(c *cli.Context) error {

return nil
}

func setupKeySigner(c *cli.Context) (keysigner.KeySigner, error) {
if c.IsSet(optionKeystorePath.Name) {
return setupKeystoreSigner(c)
}
return setupPrivateKeySigner(c)
}

func setupKeystoreSigner(c *cli.Context) (keysigner.KeySigner, error) {
// Load the keystore file
ks := keystore.NewKeyStore(c.String(optionKeystorePath.Name), keystore.LightScryptN, keystore.LightScryptP)
password := c.String(optionKeystorePassword.Name)
ksAccounts := ks.Accounts()

var account accounts.Account
if len(ksAccounts) == 0 {
var err error
account, err = ks.NewAccount(password)
if err != nil {
return nil, fmt.Errorf("failed to create account: %w", err)
}
} else {
account = ksAccounts[0]
}

fmt.Fprintf(c.App.Writer, "Public address of the key: %s\n", account.Address.Hex())
fmt.Fprintf(c.App.Writer, "Path of the secret key file: %s\n", account.URL.Path)

return keysigner.NewKeystoreSigner(ks, password, account), nil
}

func setupPrivateKeySigner(c *cli.Context) (keysigner.KeySigner, error) {
privKeyFile, err := resolveFilePath(c.String(optionPrivKeyFile.Name))
if err != nil {
return nil, fmt.Errorf("failed to get private key file path: %w", err)
}

if err := createKeyIfNotExists(c, privKeyFile); err != nil {
return nil, fmt.Errorf("failed to create private key: %w", err)
}

privKey, err := crypto.LoadECDSA(privKeyFile)
if err != nil {
return nil, fmt.Errorf("failed to load private key from file '%s': %w", privKeyFile, err)
}

return keysigner.NewPrivateKeySigner(privKey), nil
}
1 change: 1 addition & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
keystore_path: /keystore
log-level: debug
l1-rpc-url: <L1_URL>
settlement-rpc-url: http://sl-bootnode:8545
Expand Down
1 change: 1 addition & 0 deletions integrationtest/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FROM alpine:latest

COPY --from=builder /app/mev-commit-oracle /usr/local/bin/mev-commit-oracle
COPY --from=builder /app/integrationtest/key /key
COPY --from=builder /app/keystore /keystore
COPY --from=builder /app/integrationtest/config.yaml /config.yaml
COPY --from=builder /app/integrationtest/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
Expand Down
2 changes: 2 additions & 0 deletions integrationtest/config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
priv_key_file: /key
keystore_path: /keystore
keystore_password: primev
log_level: debug
l1_rpc_url: <L1_URL>
settlement_rpc_url: http://sl-bootnode:8545
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"address":"f39fd6e51aad88f6f4ce6ab8827279cfffb92266","crypto":{"cipher":"aes-128-ctr","ciphertext":"76c45c365f221cc9974ac51045cb947d053ace020d9d1343ddc8100b6d5ad5e4","cipherparams":{"iv":"751c74fd7dbce2e9c17cddff2ed36724"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"852baf4e7508c9da75956846da3b5a47e4f978f9068adb91c4e4ef18ce7b30ba"},"mac":"dce583625ec4e9821153bfec9d923752bc8d5161f7622c01afc227d537cceff5"},"id":"589bc600-ce98-4051-9d97-6aba0d6fc2fb","version":3}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"address":"f39fd6e51aad88f6f4ce6ab8827279cfffb92266","crypto":{"cipher":"aes-128-ctr","ciphertext":"76c45c365f221cc9974ac51045cb947d053ace020d9d1343ddc8100b6d5ad5e4","cipherparams":{"iv":"751c74fd7dbce2e9c17cddff2ed36724"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"852baf4e7508c9da75956846da3b5a47e4f978f9068adb91c4e4ef18ce7b30ba"},"mac":"dce583625ec4e9821153bfec9d923752bc8d5161f7622c01afc227d537cceff5"},"id":"589bc600-ce98-4051-9d97-6aba0d6fc2fb","version":3}
61 changes: 61 additions & 0 deletions pkg/keysigner/keysigner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package keysigner

import (
"crypto/ecdsa"
"math/big"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

type KeySigner interface {
GetAddress() common.Address
GetAuth(chainID *big.Int) (*bind.TransactOpts, error)
}

type privateKeySigner struct {
privKey *ecdsa.PrivateKey
}

func NewPrivateKeySigner(privKey *ecdsa.PrivateKey) *privateKeySigner {
return &privateKeySigner{
privKey: privKey,
}
}

func (pks *privateKeySigner) GetAddress() common.Address {
return crypto.PubkeyToAddress(pks.privKey.PublicKey)
}

func (pks *privateKeySigner) GetAuth(chainID *big.Int) (*bind.TransactOpts, error) {
return bind.NewKeyedTransactorWithChainID(pks.privKey, chainID)
}

type keystoreSigner struct {
keystore *keystore.KeyStore
password string
account accounts.Account
}

func NewKeystoreSigner(keystore *keystore.KeyStore, password string, account accounts.Account) *keystoreSigner {
return &keystoreSigner{
keystore: keystore,
password: password,
account: account,
}
}

func (kss *keystoreSigner) GetAddress() common.Address {
return kss.account.Address
}

func (kss *keystoreSigner) GetAuth(chainID *big.Int) (*bind.TransactOpts, error) {
if err := kss.keystore.Unlock(kss.account, kss.password); err != nil {
return nil, err
}

return bind.NewKeyStoreTransactorWithChainID(kss.keystore, kss.account, chainID)
}
25 changes: 7 additions & 18 deletions pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package node

import (
"context"
"crypto/ecdsa"
"database/sql"
"errors"
"fmt"
Expand All @@ -13,21 +12,20 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
rollupclient "github.com/primevprotocol/contracts-abi/clients/Oracle"
preconf "github.com/primevprotocol/contracts-abi/clients/PreConfCommitmentStore"
"github.com/primevprotocol/mev-oracle/pkg/apiserver"
"github.com/primevprotocol/mev-oracle/pkg/keysigner"
"github.com/primevprotocol/mev-oracle/pkg/l1Listener"
"github.com/primevprotocol/mev-oracle/pkg/settler"
"github.com/primevprotocol/mev-oracle/pkg/store"
"github.com/primevprotocol/mev-oracle/pkg/updater"
"github.com/rs/zerolog/log"
"golang.org/x/crypto/sha3"
)

type Options struct {
PrivateKey *ecdsa.PrivateKey
KeySigner keysigner.KeySigner
HTTPPort int
SettlementRPCUrl string
L1RPCUrl string
Expand Down Expand Up @@ -63,7 +61,7 @@ func NewNode(opts *Options) (*Node, error) {
return nil, err
}

owner := getEthAddressFromPubKey(opts.PrivateKey.Public().(*ecdsa.PublicKey))
owner := opts.KeySigner.GetAddress()

settlementClient, err := ethclient.Dial(opts.SettlementRPCUrl)
if err != nil {
Expand Down Expand Up @@ -114,7 +112,7 @@ func NewNode(opts *Options) (*Node, error) {
for _, winner := range opts.OverrideWinners {
err := setBuilderMapping(
ctx,
opts.PrivateKey,
opts.KeySigner,
chainID,
settlementClient,
oracleContract,
Expand Down Expand Up @@ -148,7 +146,7 @@ func NewNode(opts *Options) (*Node, error) {
updtrClosed := updtr.Start(ctx)

settlr := settler.NewSettler(
opts.PrivateKey,
opts.KeySigner,
chainID,
owner,
oracleContract,
Expand Down Expand Up @@ -234,15 +232,6 @@ func initDB(opts *Options) (db *sql.DB, err error) {
return db, err
}

func getEthAddressFromPubKey(key *ecdsa.PublicKey) common.Address {
pbBytes := crypto.FromECDSAPub(key)
hash := sha3.NewLegacyKeccak256()
hash.Write(pbBytes[1:])
address := hash.Sum(nil)[12:]

return common.BytesToAddress(address)
}

type laggerdL1Client struct {
l1Listener.EthClient
amount int
Expand Down Expand Up @@ -276,14 +265,14 @@ func (w *winnerOverrideL1Client) HeaderByNumber(ctx context.Context, number *big

func setBuilderMapping(
ctx context.Context,
privateKey *ecdsa.PrivateKey,
keySigner keysigner.KeySigner,
chainID *big.Int,
client *ethclient.Client,
rc *rollupclient.Oracle,
builderName string,
builderAddress string,
) error {
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
auth, err := keySigner.GetAuth(chainID)
if err != nil {
return err
}
Expand Down
Loading
Loading