Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
27 changes: 19 additions & 8 deletions op-batcher/batcher/espresso_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,30 @@ func (bs *BatcherService) EspressoStreamer() *espressoStreamers.Streamer {
return bs.driver.espressoStreamer
}

// initChainSigner asserts that the configured TxManager implements the
// ChainSigner interface and stores the embedded ChainSigner on the service.
// Espresso uses ChainSigner to sign batch authentication payloads sent to the
// BatchAuthenticator contract; the cast is required by every Espresso path.
// initChainSigner builds the ChainSigner from the same signing configuration
// the txmgr consumes and stores it on the service. Espresso uses ChainSigner to
// sign batch authentication payloads sent to the BatchAuthenticator contract.
// The signer is only used for Sign (arbitrary-hash signing), so the chain ID and
// from address are taken from the already-built TxManager.
func (bs *BatcherService) initChainSigner(cfg *CLIConfig) error {
if !cfg.Espresso.Enabled {
return nil
}
cast, castOk := bs.TxManager.(opcrypto.ChainSigner)
if !castOk {
return fmt.Errorf("tx manager does not implement ChainSigner")
tcfg := cfg.TxMgrConfig

// Mirror the txmgr's backwards-compatible HD-path resolution.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are using the KMS to sign payloads sent to the BatchAuthenticator contract do we need this?

@jjeangal jjeangal Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use it only for the mnemonic/private-key fallback paths. When the KMS signer is enabled the hdPath is ignored. But the factory still needs it for the other two branches, and it must resolve the path identically to txmgr or a mnemonic would derive a different key.

hdPath := tcfg.HDPath
if hdPath == "" && tcfg.SequencerHDPath != "" {
hdPath = tcfg.SequencerHDPath
} else if hdPath == "" && tcfg.L2OutputHDPath != "" {
hdPath = tcfg.L2OutputHDPath
}
bs.ChainSigner = cast

factory, from, err := opcrypto.ChainSignerFactoryFromConfig(bs.Log, tcfg.PrivateKey, tcfg.Mnemonic, hdPath, tcfg.SignerCLIConfig)
if err != nil {
return fmt.Errorf("failed to init Espresso chain signer: %w", err)
}
bs.ChainSigner = factory(bs.TxManager.ChainID().ToBig(), from)
return nil
}

Expand Down
76 changes: 0 additions & 76 deletions op-service/crypto/signature.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
package crypto

import (
"bytes"
"context"
"crypto/ecdsa"
"errors"
"fmt"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts"
"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/log"

hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet"
opsigner "github.com/ethereum-optimism/optimism/op-service/signer"
)

func PrivateKeySignerFn(key *ecdsa.PrivateKey, chainID *big.Int) bind.SignerFn {
Expand All @@ -44,70 +35,3 @@ func SignerFnFromBind(fn bind.SignerFn) SignerFn {
// SignerFn is a generic transaction signing function. It may be a remote signer so it takes a context.
// It also takes the address that should be used to sign the transaction with.
type SignerFn func(context.Context, common.Address, *types.Transaction) (*types.Transaction, error)

// SignerFactory creates a SignerFn that is bound to a specific ChainID
type SignerFactory func(chainID *big.Int) SignerFn

// SignerFactoryFromConfig considers three ways that signers are created & then creates single factory from those config options.
// It can either take a remote signer (via opsigner.CLIConfig) or it can be provided either a mnemonic + derivation path or a private key.
// It prefers the remote signer, then the mnemonic or private key (only one of which can be provided).
func SignerFactoryFromConfig(l log.Logger, privateKey, mnemonic, hdPath string, signerConfig opsigner.CLIConfig) (SignerFactory, common.Address, error) {
var signer SignerFactory
var fromAddress common.Address
if signerConfig.Enabled() {
signerClient, err := opsigner.NewSignerClientFromConfig(l, signerConfig)
if err != nil {
l.Error("Unable to create Signer Client", "error", err)
return nil, common.Address{}, fmt.Errorf("failed to create the signer client: %w", err)
}
fromAddress = common.HexToAddress(signerConfig.Address)
signer = func(chainID *big.Int) SignerFn {
return func(ctx context.Context, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
if !bytes.Equal(address[:], fromAddress[:]) {
return nil, fmt.Errorf("attempting to sign for %s, expected %s: ", address, signerConfig.Address)
}
return signerClient.SignTransaction(ctx, chainID, address, tx)
}
}
} else {
var privKey *ecdsa.PrivateKey
var err error

if privateKey != "" && mnemonic != "" {
return nil, common.Address{}, errors.New("cannot specify both a private key and a mnemonic")
}
if privateKey == "" {
// Parse l2output wallet private key and L2OO contract address.
wallet, err := hdwallet.NewFromMnemonic(mnemonic)
if err != nil {
return nil, common.Address{}, fmt.Errorf("failed to parse mnemonic: %w", err)
}

privKey, err = wallet.PrivateKey(accounts.Account{
URL: accounts.URL{
Path: hdPath,
},
})
if err != nil {
return nil, common.Address{}, fmt.Errorf("failed to create a wallet: %w", err)
}
} else {
privKey, err = crypto.HexToECDSA(strings.TrimPrefix(privateKey, "0x"))
if err != nil {
return nil, common.Address{}, fmt.Errorf("failed to parse the private key: %w", err)
}
}
// we force the curve to Geth's instance, because Geth does an equality check in the nocgo version:
// https://github.com/ethereum/go-ethereum/blob/723b1e36ad6a9e998f06f74cc8b11d51635c6402/crypto/signature_nocgo.go#L82
privKey.PublicKey.Curve = crypto.S256()
fromAddress = crypto.PubkeyToAddress(privKey.PublicKey)
signer = func(chainID *big.Int) SignerFn {
s := PrivateKeySignerFn(privKey, chainID)
return func(_ context.Context, addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
return s(addr, tx)
}
}
}

return signer, fromAddress, nil
}
56 changes: 0 additions & 56 deletions op-service/crypto/signature_test.go
Comment thread
philippecamacho marked this conversation as resolved.

This file was deleted.

5 changes: 0 additions & 5 deletions op-service/txmgr/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,6 @@ func NewConfig(cfg CLIConfig, l log.Logger) (*Config, error) {
Signer: chainSigner.SignTransaction,
From: from,

ChainSigner: chainSigner,

TxSendTimeout: cfg.TxSendTimeout,
TxNotInMempoolTimeout: cfg.TxNotInMempoolTimeout,
NetworkTimeout: cfg.NetworkTimeout,
Expand Down Expand Up @@ -667,9 +665,6 @@ type Config struct {
Signer opcrypto.SignerFn
From common.Address

// ChainSigner is used to allow for easy signing of transactions and arbitrary data.
ChainSigner opcrypto.ChainSigner

// GasPriceEstimatorFn is used to estimate the gas price for a transaction.
// If nil, DefaultGasPriceEstimatorFn is used.
GasPriceEstimatorFn GasPriceEstimatorFn
Expand Down
22 changes: 0 additions & 22 deletions op-service/txmgr/espresso.go

This file was deleted.