diff --git a/op-node/flags/p2p_flags.go b/op-node/flags/p2p_flags.go index 269b973c52da..6a38fbb817f8 100644 --- a/op-node/flags/p2p_flags.go +++ b/op-node/flags/p2p_flags.go @@ -7,6 +7,7 @@ import ( "github.com/urfave/cli/v2" "github.com/ethereum-optimism/optimism/op-node/p2p" + opsigner "github.com/ethereum-optimism/optimism/op-service/signer" ) func p2pEnv(envprefix, v string) []string { @@ -87,7 +88,7 @@ func deprecatedP2PFlags(envPrefix string) []cli.Flag { // None of these flags are strictly required. // Some are hidden if they are too technical, or not recommended. func P2PFlags(envPrefix string) []cli.Flag { - return []cli.Flag{ + return append([]cli.Flag{ &cli.BoolFlag{ Name: DisableP2PName, Usage: "Completely disable the P2P stack", @@ -410,5 +411,5 @@ func P2PFlags(envPrefix string) []cli.Flag { Required: false, EnvVars: p2pEnv(envPrefix, "PING"), }, - } + }, opsigner.CLIFlags(envPrefix, P2PCategory)...) } diff --git a/op-node/p2p/cli/load_signer.go b/op-node/p2p/cli/load_signer.go index 7416fa76397a..3c0c532edb20 100644 --- a/op-node/p2p/cli/load_signer.go +++ b/op-node/p2p/cli/load_signer.go @@ -5,18 +5,18 @@ import ( "strings" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" "github.com/ethereum-optimism/optimism/op-node/flags" "github.com/ethereum-optimism/optimism/op-node/p2p" + opsigner "github.com/ethereum-optimism/optimism/op-service/signer" ) -// TODO: implement remote signer setup (config to authenticated endpoint) -// and remote signer itself (e.g. a open http client to make signing requests) - // LoadSignerSetup loads a configuration for a Signer to be set up later -func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) { +func LoadSignerSetup(ctx *cli.Context, logger log.Logger) (p2p.SignerSetup, error) { key := ctx.String(flags.SequencerP2PKeyName) + signerCfg := opsigner.ReadCLIConfig(ctx) if key != "" { // Mnemonics are bad because they leak *all* keys when they leak. // Unencrypted keys from file are bad because they are easy to leak (and we are not checking file permissions). @@ -26,9 +26,13 @@ func LoadSignerSetup(ctx *cli.Context) (p2p.SignerSetup, error) { } return &p2p.PreparedSigner{Signer: p2p.NewLocalSigner(priv)}, nil + } else if signerCfg.Enabled() { + remoteSigner, err := p2p.NewRemoteSigner(logger, signerCfg) + if err != nil { + return nil, err + } + return &p2p.PreparedSigner{Signer: remoteSigner}, nil } - // TODO: create remote signer - return nil, nil } diff --git a/op-node/p2p/gossip_test.go b/op-node/p2p/gossip_test.go index 0833f270e40b..9f047f4b5ba8 100644 --- a/op-node/p2p/gossip_test.go +++ b/op-node/p2p/gossip_test.go @@ -3,31 +3,33 @@ package p2p import ( "bytes" "context" + "crypto/ecdsa" "fmt" "io" "math/big" "testing" "time" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" - "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/golang/snappy" - // "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" + oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" + opsigner "github.com/ethereum-optimism/optimism/op-service/signer" + "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" + "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/crypto" - "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" + pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" - - "github.com/ethereum-optimism/optimism/op-service/testlog" ) func TestGuardGossipValidator(t *testing.T) { @@ -62,30 +64,122 @@ func TestVerifyBlockSignature(t *testing.T) { L2ChainID: big.NewInt(100), } peerId := peer.ID("foo") - secrets, err := e2eutils.DefaultMnemonicConfig.Secrets() + secrets, err := crypto.GenerateKey() + require.NoError(t, err) + msg := []byte("any msg") + + t.Run("Valid", func(t *testing.T) { + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.PublicKey)} + signer := &PreparedSigner{Signer: NewLocalSigner(secrets)} + sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) + require.NoError(t, err) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) + require.Equal(t, pubsub.ValidationAccept, result) + }) + + t.Run("WrongSigner", func(t *testing.T) { + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: common.HexToAddress("0x1234")} + signer := &PreparedSigner{Signer: NewLocalSigner(secrets)} + sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) + require.NoError(t, err) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) + require.Equal(t, pubsub.ValidationReject, result) + }) + + t.Run("InvalidSignature", func(t *testing.T) { + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.PublicKey)} + sig := make([]byte, 65) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig, msg) + require.Equal(t, pubsub.ValidationReject, result) + }) + + t.Run("NoSequencer", func(t *testing.T) { + runCfg := &testutils.MockRuntimeConfig{} + signer := &PreparedSigner{Signer: NewLocalSigner(secrets)} + sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) + require.NoError(t, err) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) + require.Equal(t, pubsub.ValidationIgnore, result) + }) +} + +type mockRemoteSigner struct { + priv *ecdsa.PrivateKey +} + +func (t *mockRemoteSigner) SignBlockPayload(args opsigner.BlockPayloadArgs) (hexutil.Bytes, error) { + signingHash, err := args.ToSigningHash() + if err != nil { + return nil, err + } + signature, err := crypto.Sign(signingHash[:], t.priv) + if err != nil { + return nil, err + } + return signature, nil +} + +func TestVerifyBlockSignatureWithRemoteSigner(t *testing.T) { + secrets, err := crypto.GenerateKey() require.NoError(t, err) + + remoteSigner := &mockRemoteSigner{secrets} + server := oprpc.NewServer( + "127.0.0.1", + 0, + "test", + oprpc.WithAPIs([]rpc.API{ + { + Namespace: "opsigner", + Service: remoteSigner, + }, + }), + ) + + require.NoError(t, server.Start()) + defer func() { + _ = server.Stop() + }() + + logger := testlog.Logger(t, log.LevelCrit) + cfg := &rollup.Config{ + L2ChainID: big.NewInt(100), + } + + peerId := peer.ID("foo") msg := []byte("any msg") + signerCfg := opsigner.NewCLIConfig() + signerCfg.Endpoint = fmt.Sprintf("http://%s", server.Endpoint()) + signerCfg.TLSConfig.TLSKey = "" + signerCfg.TLSConfig.TLSCert = "" + signerCfg.TLSConfig.TLSCaCert = "" + signerCfg.TLSConfig.Enabled = false + t.Run("Valid", func(t *testing.T) { - runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)} - signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)} + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.PublicKey)} + remoteSigner, err := NewRemoteSigner(logger, signerCfg) + require.NoError(t, err) + signer := &PreparedSigner{Signer: remoteSigner} sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) require.NoError(t, err) - result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) require.Equal(t, pubsub.ValidationAccept, result) }) t.Run("WrongSigner", func(t *testing.T) { runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: common.HexToAddress("0x1234")} - signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)} + remoteSigner, err := NewRemoteSigner(logger, signerCfg) + require.NoError(t, err) + signer := &PreparedSigner{Signer: remoteSigner} sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) require.NoError(t, err) - result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) require.Equal(t, pubsub.ValidationReject, result) }) t.Run("InvalidSignature", func(t *testing.T) { - runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)} + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.PublicKey)} sig := make([]byte, 65) result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig, msg) require.Equal(t, pubsub.ValidationReject, result) @@ -93,12 +187,36 @@ func TestVerifyBlockSignature(t *testing.T) { t.Run("NoSequencer", func(t *testing.T) { runCfg := &testutils.MockRuntimeConfig{} - signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)} + remoteSigner, err := NewRemoteSigner(logger, signerCfg) + require.NoError(t, err) + signer := &PreparedSigner{Signer: remoteSigner} sig, err := signer.Sign(context.Background(), SigningDomainBlocksV1, cfg.L2ChainID, msg) require.NoError(t, err) - result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:65], msg) + result := verifyBlockSignature(logger, cfg, runCfg, peerId, sig[:], msg) require.Equal(t, pubsub.ValidationIgnore, result) }) + + t.Run("RemoteSignerNoTLS", func(t *testing.T) { + signerCfg := opsigner.NewCLIConfig() + signerCfg.Endpoint = fmt.Sprintf("http://%s", server.Endpoint()) + signerCfg.TLSConfig.TLSKey = "invalid" + signerCfg.TLSConfig.TLSCert = "invalid" + signerCfg.TLSConfig.TLSCaCert = "invalid" + signerCfg.TLSConfig.Enabled = true + + _, err := NewRemoteSigner(logger, signerCfg) + require.Error(t, err) + }) + + t.Run("RemoteSignerInvalidEndpoint", func(t *testing.T) { + signerCfg := opsigner.NewCLIConfig() + signerCfg.Endpoint = "Invalid" + signerCfg.TLSConfig.TLSKey = "" + signerCfg.TLSConfig.TLSCert = "" + signerCfg.TLSConfig.TLSCaCert = "" + _, err := NewRemoteSigner(logger, signerCfg) + require.Error(t, err) + }) } type MarshalSSZ interface { @@ -146,10 +264,10 @@ func TestBlockValidator(t *testing.T) { cfg := &rollup.Config{ L2ChainID: big.NewInt(100), } - secrets, err := e2eutils.DefaultMnemonicConfig.Secrets() + secrets, err := crypto.GenerateKey() require.NoError(t, err) - runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.SequencerP2P.PublicKey)} - signer := &PreparedSigner{Signer: NewLocalSigner(secrets.SequencerP2P)} + runCfg := &testutils.MockRuntimeConfig{P2PSeqAddress: crypto.PubkeyToAddress(secrets.PublicKey)} + signer := &PreparedSigner{Signer: NewLocalSigner(secrets)} // Params Set 2: Call the validation function peerID := peer.ID("foo") diff --git a/op-node/p2p/signer.go b/op-node/p2p/signer.go index cd5e9f94a01b..20a52d0a2625 100644 --- a/op-node/p2p/signer.go +++ b/op-node/p2p/signer.go @@ -9,8 +9,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup" + opsigner "github.com/ethereum-optimism/optimism/op-service/signer" ) var SigningDomainBlocksV1 = [32]byte{} @@ -20,40 +22,27 @@ type Signer interface { io.Closer } -func SigningHash(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) { - var msgInput [32 + 32 + 32]byte - // domain: first 32 bytes - copy(msgInput[:32], domain[:]) - // chain_id: second 32 bytes - if chainID.BitLen() > 256 { - return common.Hash{}, errors.New("chain_id is too large") - } - chainID.FillBytes(msgInput[32:64]) - // payload_hash: third 32 bytes, hash of encoded payload - copy(msgInput[64:], crypto.Keccak256(payloadBytes)) - - return crypto.Keccak256Hash(msgInput[:]), nil -} - func BlockSigningHash(cfg *rollup.Config, payloadBytes []byte) (common.Hash, error) { - return SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes) + return opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes, nil).ToSigningHash() } // LocalSigner is suitable for testing type LocalSigner struct { - priv *ecdsa.PrivateKey - hasher func(domain [32]byte, chainID *big.Int, payloadBytes []byte) (common.Hash, error) + priv *ecdsa.PrivateKey } func NewLocalSigner(priv *ecdsa.PrivateKey) *LocalSigner { - return &LocalSigner{priv: priv, hasher: SigningHash} + return &LocalSigner{priv: priv} } func (s *LocalSigner) Sign(ctx context.Context, domain [32]byte, chainID *big.Int, encodedMsg []byte) (sig *[65]byte, err error) { if s.priv == nil { return nil, errors.New("signer is closed") } - signingHash, err := s.hasher(domain, chainID, encodedMsg) + + blockPayloadArgs := opsigner.NewBlockPayloadArgs(domain, chainID, encodedMsg, nil) + signingHash, err := blockPayloadArgs.ToSigningHash() + if err != nil { return nil, err } @@ -69,6 +58,39 @@ func (s *LocalSigner) Close() error { return nil } +type RemoteSigner struct { + client *opsigner.SignerClient + sender *common.Address +} + +func NewRemoteSigner(logger log.Logger, config opsigner.CLIConfig) (*RemoteSigner, error) { + signerClient, err := opsigner.NewSignerClientFromConfig(logger, config) + if err != nil { + return nil, err + } + senderAddress := common.HexToAddress(config.Address) + return &RemoteSigner{signerClient, &senderAddress}, nil +} + +func (s *RemoteSigner) Sign(ctx context.Context, domain [32]byte, chainID *big.Int, encodedMsg []byte) (sig *[65]byte, err error) { + if s.client == nil { + return nil, errors.New("signer is closed") + } + + blockPayloadArgs := opsigner.NewBlockPayloadArgs(domain, chainID, encodedMsg, s.sender) + signature, err := s.client.SignBlockPayload(ctx, blockPayloadArgs) + + if err != nil { + return nil, err + } + return &signature, nil +} + +func (s *RemoteSigner) Close() error { + s.client = nil + return nil +} + type PreparedSigner struct { Signer } diff --git a/op-node/p2p/signer_test.go b/op-node/p2p/signer_test.go index abcfe4825b30..53f78504d18f 100644 --- a/op-node/p2p/signer_test.go +++ b/op-node/p2p/signer_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/ethereum-optimism/optimism/op-node/rollup" + opsigner "github.com/ethereum-optimism/optimism/op-service/signer" "github.com/stretchr/testify/require" ) @@ -14,10 +15,10 @@ func TestSigningHash_DifferentDomain(t *testing.T) { } payloadBytes := []byte("arbitraryData") - hash, err := SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes) + hash, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg.L2ChainID, payloadBytes, nil).ToSigningHash() require.NoError(t, err, "creating first signing hash") - hash2, err := SigningHash([32]byte{3}, cfg.L2ChainID, payloadBytes) + hash2, err := opsigner.NewBlockPayloadArgs([32]byte{3}, cfg.L2ChainID, payloadBytes, nil).ToSigningHash() require.NoError(t, err, "creating second signing hash") require.NotEqual(t, hash, hash2, "signing hash should be different when domain is different") @@ -32,10 +33,10 @@ func TestSigningHash_DifferentChainID(t *testing.T) { } payloadBytes := []byte("arbitraryData") - hash, err := SigningHash(SigningDomainBlocksV1, cfg1.L2ChainID, payloadBytes) + hash, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg1.L2ChainID, payloadBytes, nil).ToSigningHash() require.NoError(t, err, "creating first signing hash") - hash2, err := SigningHash(SigningDomainBlocksV1, cfg2.L2ChainID, payloadBytes) + hash2, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg2.L2ChainID, payloadBytes, nil).ToSigningHash() require.NoError(t, err, "creating second signing hash") require.NotEqual(t, hash, hash2, "signing hash should be different when chain ID is different") @@ -46,10 +47,10 @@ func TestSigningHash_DifferentMessage(t *testing.T) { L2ChainID: big.NewInt(100), } - hash, err := SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, []byte("msg1")) + hash, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg.L2ChainID, []byte("msg1"), nil).ToSigningHash() require.NoError(t, err, "creating first signing hash") - hash2, err := SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, []byte("msg2")) + hash2, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg.L2ChainID, []byte("msg2"), nil).ToSigningHash() require.NoError(t, err, "creating second signing hash") require.NotEqual(t, hash, hash2, "signing hash should be different when message is different") @@ -62,6 +63,6 @@ func TestSigningHash_LimitChainID(t *testing.T) { cfg := &rollup.Config{ L2ChainID: chainID, } - _, err := SigningHash(SigningDomainBlocksV1, cfg.L2ChainID, []byte("arbitraryData")) + _, err := opsigner.NewBlockPayloadArgs(SigningDomainBlocksV1, cfg.L2ChainID, []byte("arbitraryData"), nil).ToSigningHash() require.ErrorContains(t, err, "chain_id is too large") } diff --git a/op-node/service.go b/op-node/service.go index 4d12c7f5446f..55c1c7173b74 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -49,7 +49,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { driverConfig := NewDriverConfig(ctx) - p2pSignerSetup, err := p2pcli.LoadSignerSetup(ctx) + p2pSignerSetup, err := p2pcli.LoadSignerSetup(ctx, log) if err != nil { return nil, fmt.Errorf("failed to load p2p signer: %w", err) } diff --git a/op-service/signer/blockpayload_args.go b/op-service/signer/blockpayload_args.go new file mode 100644 index 000000000000..8239bc0967d6 --- /dev/null +++ b/op-service/signer/blockpayload_args.go @@ -0,0 +1,62 @@ +package signer + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// BlockPayloadArgs represents the arguments to sign a new block payload from the sequencer. +type BlockPayloadArgs struct { + Domain [32]byte `json:"domain"` + ChainID *big.Int `json:"chainId"` + PayloadHash []byte `json:"payloadHash"` + PayloadBytes []byte + SenderAddress *common.Address `json:"senderAddress"` +} + +// NewBlockPayloadArgs creates a BlockPayloadArgs struct +func NewBlockPayloadArgs(domain [32]byte, chainId *big.Int, payloadBytes []byte, senderAddress *common.Address) *BlockPayloadArgs { + payloadHash := crypto.Keccak256(payloadBytes) + args := &BlockPayloadArgs{ + Domain: domain, + ChainID: chainId, + PayloadHash: payloadHash, + PayloadBytes: payloadBytes, + SenderAddress: senderAddress, + } + return args +} + +func (args *BlockPayloadArgs) Check() error { + if args.ChainID == nil { + return errors.New("chainId not specified") + } + if len(args.PayloadHash) == 0 { + return errors.New("payloadHash not specified") + } + return nil +} + +// ToSigningHash creates a signingHash from the block payload args. +// Uses the hashing scheme from https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/rollup-node-p2p.md#block-signatures +func (args *BlockPayloadArgs) ToSigningHash() (common.Hash, error) { + if err := args.Check(); err != nil { + return common.Hash{}, err + } + var msgInput [32 + 32 + 32]byte + // domain: first 32 bytes + copy(msgInput[:32], args.Domain[:]) + // chain_id: second 32 bytes + if args.ChainID.BitLen() > 256 { + return common.Hash{}, errors.New("chain_id is too large") + } + args.ChainID.FillBytes(msgInput[32:64]) + + // payload_hash: third 32 bytes, hash of encoded payload + copy(msgInput[64:], args.PayloadHash[:]) + + return crypto.Keccak256Hash(msgInput[:]), nil +} diff --git a/op-service/signer/cli.go b/op-service/signer/cli.go index 0c1df648286d..534fd09c97bc 100644 --- a/op-service/signer/cli.go +++ b/op-service/signer/cli.go @@ -17,18 +17,20 @@ const ( HeadersFlagName = "signer.header" ) -func CLIFlags(envPrefix string) []cli.Flag { +func CLIFlags(envPrefix string, category 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"), + Name: EndpointFlagName, + Usage: "Signer endpoint the client will connect to", + EnvVars: opservice.PrefixEnvVar(envPrefix, "ENDPOINT"), + Category: category, }, &cli.StringFlag{ - Name: AddressFlagName, - Usage: "Address the signer is signing transactions for", - EnvVars: opservice.PrefixEnvVar(envPrefix, "ADDRESS"), + Name: AddressFlagName, + Usage: "Address the signer is signing requests for", + EnvVars: opservice.PrefixEnvVar(envPrefix, "ADDRESS"), + Category: category, }, &cli.StringSliceFlag{ Name: HeadersFlagName, @@ -36,7 +38,7 @@ func CLIFlags(envPrefix string) []cli.Flag { EnvVars: opservice.PrefixEnvVar(envPrefix, "HEADER"), }, } - flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer")...) + flags = append(flags, optls.CLIFlagsWithFlagPrefix(envPrefix, "signer", category)...) return flags } @@ -65,10 +67,7 @@ func (c CLIConfig) Check() error { } func (c CLIConfig) Enabled() bool { - if c.Endpoint != "" && c.Address != "" { - return true - } - return false + return c.Endpoint != "" && c.Address != "" } func ReadCLIConfig(ctx *cli.Context) CLIConfig { diff --git a/op-service/signer/cli_test.go b/op-service/signer/cli_test.go index 056ed4815601..3453258c6ca1 100644 --- a/op-service/signer/cli_test.go +++ b/op-service/signer/cli_test.go @@ -93,7 +93,7 @@ func TestInvalidConfig(t *testing.T) { func configForArgs(args ...string) CLIConfig { app := cli.NewApp() - app.Flags = CLIFlags("TEST_") + app.Flags = CLIFlags("TEST_", "") app.Name = "test" var config CLIConfig app.Action = func(ctx *cli.Context) error { diff --git a/op-service/signer/client.go b/op-service/signer/client.go index cdb9094dfe6b..acd753ecef7a 100644 --- a/op-service/signer/client.go +++ b/op-service/signer/client.go @@ -113,3 +113,19 @@ func (s *SignerClient) SignTransaction(ctx context.Context, chainId *big.Int, fr return &signed, nil } + +func (s *SignerClient) SignBlockPayload(ctx context.Context, args *BlockPayloadArgs) ([65]byte, error) { + var result hexutil.Bytes + + if err := s.client.CallContext(ctx, &result, "opsigner_signBlockPayload", args); err != nil { + return [65]byte{}, fmt.Errorf("opsigner_signBlockPayload failed: %w", err) + } + + if len(result) != 65 { + return [65]byte{}, fmt.Errorf("invalid signature: %s", result.String()) + } + + signature := [65]byte(result) + + return signature, nil +} diff --git a/op-service/tls/cli.go b/op-service/tls/cli.go index e2e086e922c3..f85de807a83e 100644 --- a/op-service/tls/cli.go +++ b/op-service/tls/cli.go @@ -21,7 +21,7 @@ const ( // CLIFlags returns flags with env var envPrefix // This should be used for server TLS configs, or when client and server tls configs are the same func CLIFlags(envPrefix string) []cli.Flag { - return CLIFlagsWithFlagPrefix(envPrefix, "") + return CLIFlagsWithFlagPrefix(envPrefix, "", "") } var ( @@ -33,7 +33,7 @@ var ( // CLIFlagsWithFlagPrefix returns flags with env var and cli flag prefixes // Should be used for client TLS configs when different from server on the same process -func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string) []cli.Flag { +func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string, category string) []cli.Flag { prefixFunc := func(flagName string) string { return strings.Trim(fmt.Sprintf("%s.%s", flagPrefix, flagName), ".") } @@ -48,22 +48,25 @@ func CLIFlagsWithFlagPrefix(envPrefix string, flagPrefix string) []cli.Flag { EnvVars: prefixEnvVars("TLS_ENABLED"), }, &cli.StringFlag{ - Name: prefixFunc(TLSCaCertFlagName), - Usage: "tls ca cert path", - Value: defaultTLSCaCert, - EnvVars: prefixEnvVars("TLS_CA"), + Name: prefixFunc(TLSCaCertFlagName), + Usage: "tls ca cert path", + Value: defaultTLSCaCert, + EnvVars: prefixEnvVars("TLS_CA"), + Category: category, }, &cli.StringFlag{ - Name: prefixFunc(TLSCertFlagName), - Usage: "tls cert path", - Value: defaultTLSCert, - EnvVars: prefixEnvVars("TLS_CERT"), + Name: prefixFunc(TLSCertFlagName), + Usage: "tls cert path", + Value: defaultTLSCert, + EnvVars: prefixEnvVars("TLS_CERT"), + Category: category, }, &cli.StringFlag{ - Name: prefixFunc(TLSKeyFlagName), - Usage: "tls key", - Value: defaultTLSKey, - EnvVars: prefixEnvVars("TLS_KEY"), + Name: prefixFunc(TLSKeyFlagName), + Usage: "tls key", + Value: defaultTLSKey, + EnvVars: prefixEnvVars("TLS_KEY"), + Category: category, }, } } diff --git a/op-service/tls/cli_test.go b/op-service/tls/cli_test.go index bd4ea4bf17c9..ce5e41d96c2d 100644 --- a/op-service/tls/cli_test.go +++ b/op-service/tls/cli_test.go @@ -53,7 +53,7 @@ func TestInvalidConfig(t *testing.T) { func configForArgs(args ...string) CLIConfig { app := cli.NewApp() - app.Flags = CLIFlagsWithFlagPrefix("TEST_", "test") + app.Flags = CLIFlagsWithFlagPrefix("TEST_", "test", "") app.Name = "test" var config CLIConfig app.Action = func(ctx *cli.Context) error { diff --git a/op-service/txmgr/cli.go b/op-service/txmgr/cli.go index 2390933d79ca..fc1aa2cceb0c 100644 --- a/op-service/txmgr/cli.go +++ b/op-service/txmgr/cli.go @@ -191,7 +191,7 @@ func CLIFlagsWithDefaults(envPrefix string, defaults DefaultFlagValues) []cli.Fl Value: defaults.ReceiptQueryInterval, EnvVars: prefixEnvVars("TXMGR_RECEIPT_QUERY_INTERVAL"), }, - }, opsigner.CLIFlags(envPrefix)...) + }, opsigner.CLIFlags(envPrefix, "")...) } type CLIConfig struct {