diff --git a/op-batcher/batch_submitter.go b/op-batcher/batch_submitter.go index 932ae996e80c5..8aff5c61196f6 100644 --- a/op-batcher/batch_submitter.go +++ b/op-batcher/batch_submitter.go @@ -3,6 +3,8 @@ package op_batcher import ( "bytes" "context" + "crypto/ecdsa" + "errors" "fmt" "io" "math/big" @@ -11,6 +13,7 @@ import ( _ "net/http/pprof" "os" "os/signal" + "strings" "sync" "syscall" "time" @@ -135,26 +138,43 @@ type BatchSubmitter struct { func NewBatchSubmitter(cfg Config, l log.Logger) (*BatchSubmitter, error) { ctx := context.Background() - // Parse wallet private key that will be used to submit L2 txs to the batch - // inbox address. - wallet, err := hdwallet.NewFromMnemonic(cfg.Mnemonic) - if err != nil { - return nil, err - } + var err error + var sequencerPrivKey *ecdsa.PrivateKey + var addr common.Address - acc := accounts.Account{ - URL: accounts.URL{ - Path: cfg.SequencerHDPath, - }, - } - addr, err := wallet.Address(acc) - if err != nil { - return nil, err + if cfg.PrivateKey != "" && cfg.Mnemonic != "" { + return nil, errors.New("cannot specify both a private key and a mnemonic") } - sequencerPrivKey, err := wallet.PrivateKey(acc) - if err != nil { - return nil, err + if cfg.PrivateKey == "" { + // Parse wallet private key that will be used to submit L2 txs to the batch + // inbox address. + wallet, err := hdwallet.NewFromMnemonic(cfg.Mnemonic) + if err != nil { + return nil, err + } + + acc := accounts.Account{ + URL: accounts.URL{ + Path: cfg.SequencerHDPath, + }, + } + addr, err = wallet.Address(acc) + if err != nil { + return nil, err + } + + sequencerPrivKey, err = wallet.PrivateKey(acc) + if err != nil { + return nil, err + } + } else { + sequencerPrivKey, err = crypto.HexToECDSA(strings.TrimPrefix(cfg.PrivateKey, "0x")) + if err != nil { + return nil, err + } + + addr = crypto.PubkeyToAddress(sequencerPrivKey.PublicKey) } batchInboxAddress, err := parseAddress(cfg.SequencerBatchInboxAddress) diff --git a/op-batcher/config.go b/op-batcher/config.go index da585a6730600..078584355d29f 100644 --- a/op-batcher/config.go +++ b/op-batcher/config.go @@ -56,6 +56,9 @@ type Config struct { // batched submission of sequencer transactions. SequencerHDPath string + // PrivateKey is the private key used to submit sequencer transactions. + PrivateKey string + // SequencerBatchInboxAddress is the address in which to send batch // transactions. SequencerBatchInboxAddress string @@ -91,6 +94,7 @@ func NewConfig(ctx *cli.Context) Config { ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name), Mnemonic: ctx.GlobalString(flags.MnemonicFlag.Name), SequencerHDPath: ctx.GlobalString(flags.SequencerHDPathFlag.Name), + PrivateKey: ctx.GlobalString(flags.PrivateKeyFlag.Name), SequencerBatchInboxAddress: ctx.GlobalString(flags.SequencerBatchInboxAddressFlag.Name), /* Optional Flags */ LogLevel: ctx.GlobalString(flags.LogLevelFlag.Name), diff --git a/op-batcher/flags/flags.go b/op-batcher/flags/flags.go index 3009476491917..c09f67aeb6634 100644 --- a/op-batcher/flags/flags.go +++ b/op-batcher/flags/flags.go @@ -82,15 +82,18 @@ var ( Name: "mnemonic", Usage: "The mnemonic used to derive the wallets for either the " + "sequencer or the l2output", - Required: true, - EnvVar: prefixEnvVar("MNEMONIC"), + EnvVar: prefixEnvVar("MNEMONIC"), } SequencerHDPathFlag = cli.StringFlag{ Name: "sequencer-hd-path", Usage: "The HD path used to derive the sequencer wallet from the " + "mnemonic. The mnemonic flag must also be set.", - Required: true, - EnvVar: prefixEnvVar("SEQUENCER_HD_PATH"), + EnvVar: prefixEnvVar("SEQUENCER_HD_PATH"), + } + PrivateKeyFlag = cli.StringFlag{ + Name: "private-key", + Usage: "The private key to use with the l2output wallet. Must not be used with mnemonic.", + EnvVar: prefixEnvVar("PRIVATE_KEY"), } SequencerBatchInboxAddressFlag = cli.StringFlag{ Name: "sequencer-batch-inbox-address", @@ -143,12 +146,13 @@ var requiredFlags = []cli.Flag{ NumConfirmationsFlag, SafeAbortNonceTooLowCountFlag, ResubmissionTimeoutFlag, - MnemonicFlag, - SequencerHDPathFlag, SequencerBatchInboxAddressFlag, } var optionalFlags = []cli.Flag{ + MnemonicFlag, + SequencerHDPathFlag, + PrivateKeyFlag, LogLevelFlag, LogTerminalFlag, PprofEnabledFlag, diff --git a/op-proposer/config.go b/op-proposer/config.go index 343831b316b3d..9ab5e452774e0 100644 --- a/op-proposer/config.go +++ b/op-proposer/config.go @@ -49,6 +49,9 @@ type Config struct { // the l2output transactions. L2OutputHDPath string + // PrivateKey is the private key used for l2output transactions. + PrivateKey string + /* Optional Params */ // LogLevel is the lowest log level that will be output. @@ -78,6 +81,7 @@ func NewConfig(ctx *cli.Context) Config { ResubmissionTimeout: ctx.GlobalDuration(flags.ResubmissionTimeoutFlag.Name), Mnemonic: ctx.GlobalString(flags.MnemonicFlag.Name), L2OutputHDPath: ctx.GlobalString(flags.L2OutputHDPathFlag.Name), + PrivateKey: ctx.GlobalString(flags.PrivateKeyFlag.Name), /* Optional Flags */ LogLevel: ctx.GlobalString(flags.LogLevelFlag.Name), LogTerminal: ctx.GlobalBool(flags.LogTerminalFlag.Name), diff --git a/op-proposer/flags/flags.go b/op-proposer/flags/flags.go index 640fd744ae38f..b967c73bfc484 100644 --- a/op-proposer/flags/flags.go +++ b/op-proposer/flags/flags.go @@ -70,15 +70,18 @@ var ( Name: "mnemonic", Usage: "The mnemonic used to derive the wallets for either the " + "sequencer or the l2output", - Required: true, - EnvVar: prefixEnvVar("MNEMONIC"), + EnvVar: prefixEnvVar("MNEMONIC"), } L2OutputHDPathFlag = cli.StringFlag{ Name: "l2-output-hd-path", Usage: "The HD path used to derive the l2output wallet from the " + "mnemonic. The mnemonic flag must also be set.", - Required: true, - EnvVar: prefixEnvVar("L2_OUTPUT_HD_PATH"), + EnvVar: prefixEnvVar("L2_OUTPUT_HD_PATH"), + } + PrivateKeyFlag = cli.StringFlag{ + Name: "private-key", + Usage: "The private key to use with the l2output wallet. Must not be used with mnemonic.", + EnvVar: prefixEnvVar("PRIVATE_KEY"), } /* Optional Flags */ @@ -123,11 +126,12 @@ var requiredFlags = []cli.Flag{ NumConfirmationsFlag, SafeAbortNonceTooLowCountFlag, ResubmissionTimeoutFlag, - MnemonicFlag, - L2OutputHDPathFlag, } var optionalFlags = []cli.Flag{ + MnemonicFlag, + L2OutputHDPathFlag, + PrivateKeyFlag, LogLevelFlag, LogTerminalFlag, PprofEnabledFlag, diff --git a/op-proposer/l2_output_submitter.go b/op-proposer/l2_output_submitter.go index 7152ed010830b..24e95ac00d2a1 100644 --- a/op-proposer/l2_output_submitter.go +++ b/op-proposer/l2_output_submitter.go @@ -2,15 +2,20 @@ package op_proposer import ( "context" + "crypto/ecdsa" + "errors" "fmt" "net" "net/http" _ "net/http/pprof" "os" "os/signal" + "strings" "syscall" "time" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum-optimism/optimism/op-proposer/drivers/l2output" "github.com/ethereum-optimism/optimism/op-proposer/rollupclient" "github.com/ethereum-optimism/optimism/op-proposer/txmgr" @@ -121,20 +126,33 @@ func NewL2OutputSubmitter( ) (*L2OutputSubmitter, error) { ctx := context.Background() + var l2OutputPrivKey *ecdsa.PrivateKey + var err error - // Parse l2output wallet private key and L2OO contract address. - wallet, err := hdwallet.NewFromMnemonic(cfg.Mnemonic) - if err != nil { - return nil, err + if cfg.PrivateKey != "" && cfg.Mnemonic != "" { + return nil, errors.New("cannot specify both a private key and a mnemonic") } - l2OutputPrivKey, err := wallet.PrivateKey(accounts.Account{ - URL: accounts.URL{ - Path: cfg.L2OutputHDPath, - }, - }) - if err != nil { - return nil, err + if cfg.PrivateKey == "" { + // Parse l2output wallet private key and L2OO contract address. + wallet, err := hdwallet.NewFromMnemonic(cfg.Mnemonic) + if err != nil { + return nil, err + } + + l2OutputPrivKey, err = wallet.PrivateKey(accounts.Account{ + URL: accounts.URL{ + Path: cfg.L2OutputHDPath, + }, + }) + if err != nil { + return nil, err + } + } else { + l2OutputPrivKey, err = crypto.HexToECDSA(strings.TrimPrefix(cfg.PrivateKey, "0x")) + if err != nil { + return nil, err + } } l2ooAddress, err := parseAddress(cfg.L2OOAddress)