From 56aa1b442b12a396f4e97f4850730bb18864e203 Mon Sep 17 00:00:00 2001 From: Mikelle Date: Sat, 3 Feb 2024 20:42:58 +0800 Subject: [PATCH 1/4] implemented keystore support --- Dockerfile | 1 + cmd/main.go | 61 ++++++++++++++----- config.yaml | 2 + integrationtest/Dockerfile | 1 + integrationtest/config.yaml | 2 + ...--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 | 1 + ...--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 | 1 + pkg/keysigner/keysigner.go | 61 +++++++++++++++++++ pkg/node/node.go | 13 ++-- pkg/settler/settler.go | 10 +-- 10 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 integrationtest/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 create mode 100644 keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 create mode 100644 pkg/keysigner/keysigner.go diff --git a/Dockerfile b/Dockerfile index bcf872e..4a3f228 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ FROM alpine:latest COPY --from=builder /app/mev-commit-oracle /usr/local/bin/mev-commit-oracle COPY --from=builder /app/key /key +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 diff --git a/cmd/main.go b/cmd/main.go index fe2c8c5..11d5727 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,8 +7,10 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/primevprotocol/mev-oracle/pkg/keysigner" "github.com/primevprotocol/mev-oracle/pkg/node" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -53,6 +55,8 @@ func main() { type config struct { PrivKeyFile string `yaml:"priv_key_file" json:"priv_key_file"` + KeystorePath string `yaml:"keystore_path" json:"keystore_path"` + KeystorePassword string `yaml:"keystore_password" json:"keystore_password"` HTTPPort int `yaml:"http_port" json:"http_port"` LogLevel string `yaml:"log_level" json:"log_level"` L1RPCUrl string `yaml:"l1_rpc_url" json:"l1_rpc_url"` @@ -69,8 +73,8 @@ type config struct { } func checkConfig(cfg *config) error { - if cfg.PrivKeyFile == "" { - return fmt.Errorf("priv_key_file is required") + if cfg.PrivKeyFile == "" && (cfg.KeystorePath == "" || cfg.KeystorePassword == "") { + return fmt.Errorf("priv_key_file or keystore_path and keystore_password are required") } if cfg.HTTPPort == 0 { @@ -131,25 +135,15 @@ func start(c *cli.Context) error { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix log.Logger = log.Output(os.Stdout).With().Caller().Logger() - privKeyFile := cfg.PrivKeyFile - if strings.HasPrefix(privKeyFile, "~/") { - homeDir, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("failed to get user home directory: %w", err) - } - - privKeyFile = filepath.Join(homeDir, privKeyFile[2:]) - } - - privKey, err := crypto.LoadECDSA(privKeyFile) + keySigner, err := setupKeySigner(&cfg) if err != nil { - return fmt.Errorf("failed to load private key from file '%s': %w", cfg.PrivKeyFile, err) + return fmt.Errorf("failed to setup key signer: %w", err) } common.HexToAddress(cfg.OracleContractAddr) nd, err := node.NewNode(&node.Options{ - PrivateKey: privKey, + KeySigner: keySigner, HTTPPort: cfg.HTTPPort, L1RPCUrl: cfg.L1RPCUrl, SettlementRPCUrl: cfg.SettlementRPCUrl, @@ -188,3 +182,40 @@ func start(c *cli.Context) error { return nil } + +func setupKeySigner(cfg *config) (keysigner.KeySigner, error) { + if cfg.PrivKeyFile == "" { + return setupKeystoreSigner(cfg) + } + return setupPrivateKeySigner(cfg) +} + +func setupKeystoreSigner(cfg *config) (keysigner.KeySigner, error) { + // Load the keystore file + ks := keystore.NewKeyStore(cfg.KeystorePath, keystore.LightScryptN, keystore.LightScryptP) + accounts := ks.Accounts() + if len(accounts) == 0 { + return nil, fmt.Errorf("no accounts found in keystore, path: %s", cfg.KeystorePath) + } + + account := accounts[0] + return keysigner.NewKeystoreSigner(ks, cfg.KeystorePassword, account), nil +} + +func setupPrivateKeySigner(cfg *config) (keysigner.KeySigner, error) { + privKeyFile := cfg.PrivKeyFile + if strings.HasPrefix(privKeyFile, "~/") { + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("failed to get user home directory: %w", err) + } + + privKeyFile = filepath.Join(homeDir, privKeyFile[2:]) + } + + privKey, err := crypto.LoadECDSA(privKeyFile) + if err != nil { + return nil, fmt.Errorf("failed to load private key from file '%s': %w", cfg.PrivKeyFile, err) + } + return keysigner.NewPrivateKeySigner(privKey), nil +} diff --git a/config.yaml b/config.yaml index 33508a4..9191e05 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,6 @@ priv_key_file: /key +keystore_path: /keystore +keystore_password: primev log_level: debug l1_rpc_url: settlement_rpc_url: http://sl-bootnode:8545 diff --git a/integrationtest/Dockerfile b/integrationtest/Dockerfile index 4392a15..4f81c5a 100644 --- a/integrationtest/Dockerfile +++ b/integrationtest/Dockerfile @@ -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 diff --git a/integrationtest/config.yaml b/integrationtest/config.yaml index 96cf2f6..572d263 100644 --- a/integrationtest/config.yaml +++ b/integrationtest/config.yaml @@ -1,4 +1,6 @@ priv_key_file: /key +keystore_path: /keystore +keystore_password: primev log_level: debug l1_rpc_url: settlement_rpc_url: http://sl-bootnode:8545 diff --git a/integrationtest/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 b/integrationtest/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 new file mode 100644 index 0000000..9ca49c3 --- /dev/null +++ b/integrationtest/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 @@ -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} \ No newline at end of file diff --git a/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 b/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 new file mode 100644 index 0000000..9ca49c3 --- /dev/null +++ b/keystore/UTC--2024-02-03T12-33-51.739458000Z--f39fd6e51aad88f6f4ce6ab8827279cfffb92266 @@ -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} \ No newline at end of file diff --git a/pkg/keysigner/keysigner.go b/pkg/keysigner/keysigner.go new file mode 100644 index 0000000..3b9fd7e --- /dev/null +++ b/pkg/keysigner/keysigner.go @@ -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) +} diff --git a/pkg/node/node.go b/pkg/node/node.go index deba334..7a74c7a 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -18,6 +18,7 @@ import ( 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" @@ -27,7 +28,7 @@ import ( ) type Options struct { - PrivateKey *ecdsa.PrivateKey + KeySigner keysigner.KeySigner HTTPPort int SettlementRPCUrl string L1RPCUrl string @@ -63,7 +64,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 { @@ -114,7 +115,7 @@ func NewNode(opts *Options) (*Node, error) { for _, winner := range opts.OverrideWinners { err := setBuilderMapping( ctx, - opts.PrivateKey, + opts.KeySigner, chainID, settlementClient, oracleContract, @@ -148,7 +149,7 @@ func NewNode(opts *Options) (*Node, error) { updtrClosed := updtr.Start(ctx) settlr := settler.NewSettler( - opts.PrivateKey, + opts.KeySigner, chainID, owner, oracleContract, @@ -276,14 +277,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 } diff --git a/pkg/settler/settler.go b/pkg/settler/settler.go index dcda472..cbe440c 100644 --- a/pkg/settler/settler.go +++ b/pkg/settler/settler.go @@ -2,7 +2,6 @@ package settler import ( "context" - "crypto/ecdsa" "errors" "math/big" "time" @@ -10,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/primevprotocol/mev-oracle/pkg/keysigner" "github.com/prometheus/client_golang/prometheus" "github.com/rs/zerolog/log" ) @@ -51,7 +51,7 @@ type Transactor interface { } type Settler struct { - privateKey *ecdsa.PrivateKey + keySigner keysigner.KeySigner chainID *big.Int owner common.Address rollupClient Oracle @@ -61,7 +61,7 @@ type Settler struct { } func NewSettler( - privateKey *ecdsa.PrivateKey, + keySigner keysigner.KeySigner, chainID *big.Int, owner common.Address, rollupClient Oracle, @@ -73,14 +73,14 @@ func NewSettler( settlerRegister: settlerRegister, owner: owner, client: client, - privateKey: privateKey, + keySigner: keySigner, chainID: chainID, metrics: newMetrics(), } } func (s *Settler) getTransactOpts(ctx context.Context) (*bind.TransactOpts, error) { - auth, err := bind.NewKeyedTransactorWithChainID(s.privateKey, s.chainID) + auth, err := s.keySigner.GetAuth(s.chainID) if err != nil { return nil, err } From 112b99770694359ea205ed4621a661152abfdaed Mon Sep 17 00:00:00 2001 From: Mikelle Date: Tue, 6 Feb 2024 05:56:05 +0800 Subject: [PATCH 2/4] added checker for keystore path and password --- cmd/main.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index f54c920..fdaad45 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -187,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) }, }, }} @@ -263,7 +263,27 @@ func getEthAddressFromPubKey(key *ecdsa.PublicKey) common.Address { return common.BytesToAddress(address) } -func start(c *cli.Context) error { +func initializeApplication(c *cli.Context) error { + if err := verifyKeystorePasswordPresence(c); err != nil { + return err + } + if err := launchOracleWithConfig(c); err != nil { + return err + } + return nil +} + +// 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) From 725b501ad84c1bb6f605533365ec6f706621cdea Mon Sep 17 00:00:00 2001 From: Mikelle Date: Tue, 6 Feb 2024 05:58:50 +0800 Subject: [PATCH 3/4] deleted unused function --- pkg/node/node.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkg/node/node.go b/pkg/node/node.go index 7a74c7a..529ac9d 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -235,15 +235,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 From bd2936064201870436be91a06ec8b149bf449090 Mon Sep 17 00:00:00 2001 From: Mikelle Date: Tue, 6 Feb 2024 06:09:36 +0800 Subject: [PATCH 4/4] fixed imports --- pkg/node/node.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/node/node.go b/pkg/node/node.go index 529ac9d..592eccc 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -2,7 +2,6 @@ package node import ( "context" - "crypto/ecdsa" "database/sql" "errors" "fmt" @@ -13,7 +12,6 @@ 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" @@ -24,7 +22,6 @@ import ( "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 {