Commit 76ac0cb4 authored by Sabnock01's avatar Sabnock01

move op-signer into op-service

parent 7e7d2d0f
...@@ -17,8 +17,7 @@ import ( ...@@ -17,8 +17,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet" hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet"
optls "github.com/ethereum-optimism/optimism/op-service/tls" opsigner "github.com/ethereum-optimism/optimism/op-service/signer"
opsigner "github.com/ethereum-optimism/optimism/op-signer/client"
) )
func PrivateKeySignerFn(key *ecdsa.PrivateKey, chainID *big.Int) bind.SignerFn { func PrivateKeySignerFn(key *ecdsa.PrivateKey, chainID *big.Int) bind.SignerFn {
...@@ -44,9 +43,9 @@ type SignerFn func(context.Context, common.Address, *types.Transaction) (*types. ...@@ -44,9 +43,9 @@ type SignerFn func(context.Context, common.Address, *types.Transaction) (*types.
type SignerFactory func(chainID *big.Int) SignerFn type SignerFactory func(chainID *big.Int) SignerFn
// SignerFactoryFromConfig considers three ways that signers are created & then creates single factory from those config options. // SignerFactoryFromConfig considers three ways that signers are created & then creates single factory from those config options.
// It can either take a remote signer (via optls.SignerCLIConfig) or it can be provided either a mnemonic + derivation path or a private key. // 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). // 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 optls.SignerCLIConfig) (SignerFactory, common.Address, error) { func SignerFactoryFromConfig(l log.Logger, privateKey, mnemonic, hdPath string, signerConfig opsigner.CLIConfig) (SignerFactory, common.Address, error) {
var signer SignerFactory var signer SignerFactory
var fromAddress common.Address var fromAddress common.Address
if signerConfig.Enabled() { if signerConfig.Enabled() {
......
package signer
import (
"errors"
"github.com/urfave/cli/v2"
opservice "github.com/ethereum-optimism/optimism/op-service"
optls "github.com/ethereum-optimism/optimism/op-service/tls"
)
const (
EndpointFlagName = "signer.endpoint"
AddressFlagName = "signer.address"
)
func CLIFlags(envPrefix string) []cli.Flag {
envPrefix += "_SIGNER"
flags := []cli.Flag{
&cli.StringFlag{
Name: EndpointFlagName,
Usage: "Signer endpoint the client will connect to",
EnvVars: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"),
},
&cli.StringFlag{
Name: AddressFlagName,
Usage: "Address the signer is signing transactions for",
EnvVars: opservice.PrefixEnvVar(envPrefix, "ADDRESS"),
},
}
flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer")...)
return flags
}
type CLIConfig struct {
Endpoint string
Address string
TLSConfig optls.CLIConfig
}
func NewCLIConfig() CLIConfig {
return CLIConfig{
TLSConfig: optls.NewCLIConfig(),
}
}
func (c CLIConfig) Check() error {
if err := c.TLSConfig.Check(); err != nil {
return err
}
if !((c.Endpoint == "" && c.Address == "") || (c.Endpoint != "" && c.Address != "")) {
return errors.New("signer endpoint and address must both be set or not set")
}
return nil
}
func (c CLIConfig) Enabled() bool {
if c.Endpoint != "" && c.Address != "" {
return true
}
return false
}
func ReadCLIConfig(ctx *cli.Context) CLIConfig {
cfg := CLIConfig{
Endpoint: ctx.String(EndpointFlagName),
Address: ctx.String(AddressFlagName),
TLSConfig: optls.ReadCLIConfigWithPrefix(ctx, "signer"),
}
return cfg
}
package signer
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
)
func TestDefaultCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := configForArgs()
defaultCfg := NewCLIConfig()
require.Equal(t, defaultCfg, cfg)
}
func TestDefaultConfigIsValid(t *testing.T) {
err := NewCLIConfig().Check()
require.NoError(t, err)
}
func TestInvalidConfig(t *testing.T) {
tests := []struct {
name string
expected string
configChange func(config *CLIConfig)
}{
{
name: "MissingEndpoint",
expected: "signer endpoint and address must both be set or not set",
configChange: func(config *CLIConfig) {
config.Address = "0x1234"
},
},
{
name: "MissingAddress",
expected: "signer endpoint and address must both be set or not set",
configChange: func(config *CLIConfig) {
config.Endpoint = "http://localhost"
},
},
{
name: "InvalidTLSConfig",
expected: "all tls flags must be set if at least one is set",
configChange: func(config *CLIConfig) {
config.TLSConfig.TLSKey = ""
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := NewCLIConfig()
test.configChange(&cfg)
err := cfg.Check()
require.ErrorContains(t, err, test.expected)
})
}
}
func configForArgs(args ...string) CLIConfig {
app := cli.NewApp()
app.Flags = CLIFlags("TEST_")
app.Name = "test"
var config CLIConfig
app.Action = func(ctx *cli.Context) error {
config = ReadCLIConfig(ctx)
return nil
}
_ = app.Run(args)
return config
}
package signer
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"math/big"
"net/http"
"os"
"time"
optls "github.com/ethereum-optimism/optimism/op-service/tls"
"github.com/ethereum-optimism/optimism/op-service/tls/certman"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
type SignerClient struct {
client *rpc.Client
status string
logger log.Logger
}
func NewSignerClient(logger log.Logger, endpoint string, tlsConfig optls.CLIConfig) (*SignerClient, error) {
var httpClient *http.Client
if tlsConfig.TLSCaCert != "" {
logger.Info("tlsConfig specified, loading tls config")
caCert, err := os.ReadFile(tlsConfig.TLSCaCert)
if err != nil {
return nil, fmt.Errorf("failed to read tls.ca: %w", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// certman watches for newer client certifictes and automatically reloads them
cm, err := certman.New(logger, tlsConfig.TLSCert, tlsConfig.TLSKey)
if err != nil {
logger.Error("failed to read tls cert or key", "err", err)
return nil, err
}
if err := cm.Watch(); err != nil {
logger.Error("failed to start certman watcher", "err", err)
return nil, err
}
httpClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
RootCAs: caCertPool,
GetClientCertificate: func(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
return cm.GetCertificate(nil)
},
},
},
}
} else {
logger.Info("no tlsConfig specified, using default http client")
httpClient = http.DefaultClient
}
rpcClient, err := rpc.DialOptions(context.Background(), endpoint, rpc.WithHTTPClient(httpClient))
if err != nil {
return nil, err
}
signer := &SignerClient{logger: logger, client: rpcClient}
// Check if reachable
version, err := signer.pingVersion()
if err != nil {
return nil, err
}
signer.status = fmt.Sprintf("ok [version=%v]", version)
return signer, nil
}
func NewSignerClientFromConfig(logger log.Logger, config CLIConfig) (*SignerClient, error) {
return NewSignerClient(logger, config.Endpoint, config.TLSConfig)
}
func (s *SignerClient) pingVersion() (string, error) {
var v string
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
if err := s.client.CallContext(ctx, &v, "health_status"); err != nil {
return "", err
}
return v, nil
}
func (s *SignerClient) SignTransaction(ctx context.Context, chainId *big.Int, from common.Address, tx *types.Transaction) (*types.Transaction, error) {
args := NewTransactionArgsFromTransaction(chainId, from, tx)
var result hexutil.Bytes
if err := s.client.CallContext(ctx, &result, "eth_signTransaction", args); err != nil {
return nil, fmt.Errorf("eth_signTransaction failed: %w", err)
}
signed := &types.Transaction{}
if err := signed.UnmarshalBinary(result); err != nil {
return nil, err
}
return signed, nil
}
package signer
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
// TransactionArgs represents the arguments to construct a new transaction
// or a message call.
type TransactionArgs struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons.
// "input" is the newer name and should be preferred by clients.
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
// NewTransactionArgsFromTransaction creates a TransactionArgs struct from an EIP-1559 transaction
func NewTransactionArgsFromTransaction(chainId *big.Int, from common.Address, tx *types.Transaction) *TransactionArgs {
data := hexutil.Bytes(tx.Data())
nonce := hexutil.Uint64(tx.Nonce())
gas := hexutil.Uint64(tx.Gas())
accesses := tx.AccessList()
args := &TransactionArgs{
From: &from,
Input: &data,
Nonce: &nonce,
Value: (*hexutil.Big)(tx.Value()),
Gas: &gas,
To: tx.To(),
ChainID: (*hexutil.Big)(chainId),
MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()),
MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()),
AccessList: &accesses,
}
return args
}
// data retrieves the transaction calldata. Input field is preferred.
func (args *TransactionArgs) data() []byte {
if args.Input != nil {
return *args.Input
}
if args.Data != nil {
return *args.Data
}
return nil
}
// ToTransaction converts the arguments to a transaction.
func (args *TransactionArgs) ToTransaction() *types.Transaction {
var data types.TxData
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: args.To,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(*args.Nonce),
Gas: uint64(*args.Gas),
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
Value: (*big.Int)(args.Value),
Data: args.data(),
AccessList: al,
}
return types.NewTx(data)
}
...@@ -108,64 +108,3 @@ func ReadCLIConfigWithPrefix(ctx *cli.Context, flagPrefix string) CLIConfig { ...@@ -108,64 +108,3 @@ func ReadCLIConfigWithPrefix(ctx *cli.Context, flagPrefix string) CLIConfig {
TLSKey: ctx.String(prefixFunc(TLSKeyFlagName)), TLSKey: ctx.String(prefixFunc(TLSKeyFlagName)),
} }
} }
const (
EndpointFlagName = "signer.endpoint"
AddressFlagName = "signer.address"
)
func SignerCLIFlags(envPrefix string) []cli.Flag {
envPrefix += "_SIGNER"
flags := []cli.Flag{
&cli.StringFlag{
Name: EndpointFlagName,
Usage: "Signer endpoint the client will connect to",
EnvVars: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"),
},
&cli.StringFlag{
Name: AddressFlagName,
Usage: "Address the signer is signing transactions for",
EnvVars: opservice.PrefixEnvVar(envPrefix, "ADDRESS"),
},
}
flags = append(flags, CLIFlagsWithFlagPrefix(envPrefix, "signer")...)
return flags
}
type SignerCLIConfig struct {
Endpoint string
Address string
TLSConfig CLIConfig
}
func NewSignerCLIConfig() SignerCLIConfig {
return SignerCLIConfig{
TLSConfig: NewCLIConfig(),
}
}
func (c SignerCLIConfig) Check() error {
if err := c.TLSConfig.Check(); err != nil {
return err
}
if !((c.Endpoint == "" && c.Address == "") || (c.Endpoint != "" && c.Address != "")) {
return errors.New("signer endpoint and address must both be set or not set")
}
return nil
}
func (c SignerCLIConfig) Enabled() bool {
if c.Endpoint != "" && c.Address != "" {
return true
}
return false
}
func ReadSignerCLIConfig(ctx *cli.Context) SignerCLIConfig {
cfg := SignerCLIConfig{
Endpoint: ctx.String(EndpointFlagName),
Address: ctx.String(AddressFlagName),
TLSConfig: ReadCLIConfigWithPrefix(ctx, "signer"),
}
return cfg
}
...@@ -55,65 +55,3 @@ func configForArgs(args ...string) CLIConfig { ...@@ -55,65 +55,3 @@ func configForArgs(args ...string) CLIConfig {
_ = app.Run(args) _ = app.Run(args)
return config return config
} }
func TestDefaultSignerCLIOptionsMatchDefaultConfig(t *testing.T) {
cfg := signerConfigForArgs()
defaultCfg := NewSignerCLIConfig()
require.Equal(t, defaultCfg, cfg)
}
func TestDefaultSignerConfigIsValid(t *testing.T) {
err := NewSignerCLIConfig().Check()
require.NoError(t, err)
}
func TestInvalidSignerConfig(t *testing.T) {
tests := []struct {
name string
expected string
configChange func(config *SignerCLIConfig)
}{
{
name: "MissingEndpoint",
expected: "signer endpoint and address must both be set or not set",
configChange: func(config *SignerCLIConfig) {
config.Address = "0x1234"
},
},
{
name: "MissingAddress",
expected: "signer endpoint and address must both be set or not set",
configChange: func(config *SignerCLIConfig) {
config.Endpoint = "http://localhost"
},
},
{
name: "InvalidTLSConfig",
expected: "all tls flags must be set if at least one is set",
configChange: func(config *SignerCLIConfig) {
config.TLSConfig.TLSKey = ""
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := NewSignerCLIConfig()
test.configChange(&cfg)
err := cfg.Check()
require.ErrorContains(t, err, test.expected)
})
}
}
func signerConfigForArgs(args ...string) SignerCLIConfig {
app := cli.NewApp()
app.Flags = SignerCLIFlags("TEST_")
app.Name = "test"
var config SignerCLIConfig
app.Action = func(ctx *cli.Context) error {
config = ReadSignerCLIConfig(ctx)
return nil
}
_ = app.Run(args)
return config
}
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto"
optls "github.com/ethereum-optimism/optimism/op-service/tls" opsigner "github.com/ethereum-optimism/optimism/op-service/signer"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -120,7 +120,7 @@ func CLIFlags(envPrefix string) []cli.Flag { ...@@ -120,7 +120,7 @@ func CLIFlags(envPrefix string) []cli.Flag {
Value: defaultReceiptQueryInterval, Value: defaultReceiptQueryInterval,
EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"), EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"),
}, },
}, optls.SignerCLIFlags(envPrefix)...) }, opsigner.CLIFlags(envPrefix)...)
} }
type CLIConfig struct { type CLIConfig struct {
...@@ -130,7 +130,7 @@ type CLIConfig struct { ...@@ -130,7 +130,7 @@ type CLIConfig struct {
SequencerHDPath string SequencerHDPath string
L2OutputHDPath string L2OutputHDPath string
PrivateKey string PrivateKey string
SignerCLIConfig optls.SignerCLIConfig SignerCLIConfig opsigner.CLIConfig
NumConfirmations uint64 NumConfirmations uint64
SafeAbortNonceTooLowCount uint64 SafeAbortNonceTooLowCount uint64
ResubmissionTimeout time.Duration ResubmissionTimeout time.Duration
...@@ -150,7 +150,7 @@ func NewCLIConfig(l1RPCURL string) CLIConfig { ...@@ -150,7 +150,7 @@ func NewCLIConfig(l1RPCURL string) CLIConfig {
TxSendTimeout: defaultTxSendTimeout, TxSendTimeout: defaultTxSendTimeout,
TxNotInMempoolTimeout: defaultTxNotInMempoolTimeout, TxNotInMempoolTimeout: defaultTxNotInMempoolTimeout,
ReceiptQueryInterval: defaultReceiptQueryInterval, ReceiptQueryInterval: defaultReceiptQueryInterval,
SignerCLIConfig: optls.NewSignerCLIConfig(), SignerCLIConfig: opsigner.NewCLIConfig(),
} }
} }
...@@ -190,7 +190,7 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig { ...@@ -190,7 +190,7 @@ func ReadCLIConfig(ctx *cli.Context) CLIConfig {
SequencerHDPath: ctx.String(SequencerHDPathFlag.Name), SequencerHDPath: ctx.String(SequencerHDPathFlag.Name),
L2OutputHDPath: ctx.String(L2OutputHDPathFlag.Name), L2OutputHDPath: ctx.String(L2OutputHDPathFlag.Name),
PrivateKey: ctx.String(PrivateKeyFlagName), PrivateKey: ctx.String(PrivateKeyFlagName),
SignerCLIConfig: optls.ReadSignerCLIConfig(ctx), SignerCLIConfig: opsigner.ReadCLIConfig(ctx),
NumConfirmations: ctx.Uint64(NumConfirmationsFlagName), NumConfirmations: ctx.Uint64(NumConfirmationsFlagName),
SafeAbortNonceTooLowCount: ctx.Uint64(SafeAbortNonceTooLowCountFlagName), SafeAbortNonceTooLowCount: ctx.Uint64(SafeAbortNonceTooLowCountFlagName),
ResubmissionTimeout: ctx.Duration(ResubmissionTimeoutFlagName), ResubmissionTimeout: ctx.Duration(ResubmissionTimeoutFlagName),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment