Commit 676efd21 authored by Felipe Andrade's avatar Felipe Andrade

signer tls, send tx on heartbeat

parent 135a6e1d
bin bin
tls
config.toml config.toml
...@@ -6,6 +6,9 @@ log_level = "debug" ...@@ -6,6 +6,9 @@ log_level = "debug"
[signer_service] [signer_service]
# URL to the signer service # URL to the signer service
url = "http://localhost:1234" url = "http://localhost:1234"
tls_ca_cert = "tls/ca.crt"
tls_cert = "tls/tls.crt"
tls_key = "tls/tls.key"
[healthz] [healthz]
# Whether or not to enable healthz endpoint # Whether or not to enable healthz endpoint
...@@ -34,6 +37,14 @@ signer_method = "signer" ...@@ -34,6 +37,14 @@ signer_method = "signer"
address="0x0123" address="0x0123"
# For static signer method, the private key to use # For static signer method, the private key to use
# private_key="" # private_key=""
# Transaction value in wei
tx_value=100000000000000
# Gas limit
gas_limit=21000
# Gas tip cap
gas_tip_cap=2000000000
# Fee cap
gas_fee_cap=20000000000
[providers.p1] [providers.p1]
# URL to the RPC provider # URL to the RPC provider
...@@ -48,6 +59,8 @@ send_interval = "5s" ...@@ -48,6 +59,8 @@ send_interval = "5s"
wallet = "default" wallet = "default"
[providers.p2] [providers.p2]
# Uncomment to disable this provider
# disabled=true
# URL to the RPC provider # URL to the RPC provider
url = "http://localhost:8552" url = "http://localhost:8552"
# Read only providers are only used to check for transactions # Read only providers are only used to check for transactions
......
...@@ -4,23 +4,31 @@ go 1.20 ...@@ -4,23 +4,31 @@ go 1.20
require ( require (
github.com/BurntSushi/toml v1.3.2 github.com/BurntSushi/toml v1.3.2
github.com/ethereum-optimism/optimism/op-signer v0.1.1
github.com/ethereum/go-ethereum v1.12.0 github.com/ethereum/go-ethereum v1.12.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.14.0 github.com/rs/cors v1.8.2
github.com/rs/cors v1.7.0
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/ethereum-optimism/optimism/op-service v0.10.14-0.20230209153120-0338ea88dff7 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c // indirect github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/prometheus/common v0.39.0 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/prometheus/procfs v0.9.0 // indirect github.com/tklauser/numcpus v0.5.0 // indirect
github.com/urfave/cli v1.22.9 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
) )
This diff is collapsed.
package config package config
import ( import (
"math/big"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
...@@ -17,7 +19,10 @@ type Config struct { ...@@ -17,7 +19,10 @@ type Config struct {
} }
type SignerServiceConfig struct { type SignerServiceConfig struct {
URL string `toml:"url"` URL string `toml:"url"`
TLSCaCert string `toml:"tls_ca_cert"`
TLSCert string `toml:"tls_cert"`
TLSKey string `toml:"tls_key"`
} }
type MetricsConfig struct { type MetricsConfig struct {
...@@ -34,17 +39,23 @@ type HealthzConfig struct { ...@@ -34,17 +39,23 @@ type HealthzConfig struct {
type WalletConfig struct { type WalletConfig struct {
// default: 420 (Optimism Goerli) // default: 420 (Optimism Goerli)
ChainID uint `toml:"chain_id"` ChainID big.Int `toml:"chain_id"`
// signer | static, default: signer // signer | static, default: signer
SignerMethod string `toml:"signer_method"` SignerMethod string `toml:"signer_method"`
Address string `toml:"address"`
// for static signing // private key is used for static signing
Address string `toml:"address"`
PrivateKey string `toml:"private_key"` PrivateKey string `toml:"private_key"`
// transaction parameters
TxValue big.Int `toml:"tx_value"`
GasLimit uint64 `toml:"gas_limit"`
GasTipCap big.Int `toml:"gas_tip_cap"`
GasFeeCap big.Int `toml:"gas_fee_cap"`
} }
type ProviderConfig struct { type ProviderConfig struct {
Disabled bool `toml:"disabled"`
URL string `toml:"url"` URL string `toml:"url"`
Wallet string `toml:"wallet"` Wallet string `toml:"wallet"`
ReadOnly bool `toml:"read_only"` ReadOnly bool `toml:"read_only"`
...@@ -81,7 +92,7 @@ func (c *Config) Validate() error { ...@@ -81,7 +92,7 @@ func (c *Config) Validate() error {
} }
for name, wallet := range c.Wallets { for name, wallet := range c.Wallets {
if wallet.ChainID == 0 { if wallet.ChainID.BitLen() == 0 {
return errors.Errorf("wallet [%s] chain_id is missing", name) return errors.Errorf("wallet [%s] chain_id is missing", name)
} }
if wallet.SignerMethod != "signer" && wallet.SignerMethod != "static" { if wallet.SignerMethod != "signer" && wallet.SignerMethod != "static" {
...@@ -91,6 +102,15 @@ func (c *Config) Validate() error { ...@@ -91,6 +102,15 @@ func (c *Config) Validate() error {
if c.Signer.URL == "" { if c.Signer.URL == "" {
return errors.New("signer url is missing") return errors.New("signer url is missing")
} }
if c.Signer.TLSCaCert == "" {
return errors.New("signer tls_ca_cert is missing")
}
if c.Signer.TLSCert == "" {
return errors.New("signer tls_cert is missing")
}
if c.Signer.TLSKey == "" {
return errors.New("signer tls_key is missing")
}
} }
if wallet.SignerMethod == "static" { if wallet.SignerMethod == "static" {
if wallet.PrivateKey == "" { if wallet.PrivateKey == "" {
......
...@@ -3,12 +3,105 @@ package provider ...@@ -3,12 +3,105 @@ package provider
import ( import (
"context" "context"
"github.com/ethereum-optimism/optimism/op-service/tls"
signer "github.com/ethereum-optimism/optimism/op-signer/client"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/pkg/errors"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// Heartbeat poll for expected transactions // Heartbeat poll for expected transactions
func (p *Provider) Heartbeat(ctx context.Context) { func (p *Provider) Heartbeat(ctx context.Context) {
log.Debug("heartbeat", "provider", p.name) log.Debug("heartbeat", "provider", p.name)
ethClient, err := p.dial(ctx)
if err != nil {
log.Error("cant dial to provider", "provider", p.name, "url", p.config.URL, "err", err)
}
nonce, err := p.nonce(ctx, ethClient)
if err != nil {
log.Error("cant get nounce", "provider", p.name, "err", err)
}
tx := p.createTx(nonce)
signedTx, err := p.sign(ctx, tx)
if err != nil {
log.Error("cant sign tx", "tx", tx, "err", err)
}
err = ethClient.SendTransaction(ctx, signedTx)
if err != nil {
log.Error("cant send transaction", "provider", p.name, "err", err)
}
log.Info("transaction sent", "hash", signedTx.Hash().Hex())
}
func (p *Provider) dial(ctx context.Context) (*ethclient.Client, error) {
return ethclient.Dial(p.config.URL)
}
func (p *Provider) createTx(nonce uint64) *types.Transaction {
toAddress := common.HexToAddress(p.walletConfig.Address)
var data []byte
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: &p.walletConfig.ChainID,
Nonce: nonce,
GasFeeCap: &p.walletConfig.GasFeeCap,
GasTipCap: &p.walletConfig.GasTipCap,
Gas: p.walletConfig.GasLimit,
To: &toAddress,
Value: &p.walletConfig.TxValue,
Data: data,
})
log.Debug("tx", "tx", tx)
return tx
}
func (p *Provider) sign(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) {
if p.walletConfig.SignerMethod == "static" {
log.Debug("using static signer")
privateKey, err := crypto.HexToECDSA(p.walletConfig.PrivateKey)
if err != nil {
return nil, err
}
return types.SignTx(tx, types.LatestSignerForChainID(&p.walletConfig.ChainID), privateKey)
} else if p.walletConfig.SignerMethod == "signer" {
tlsConfig := tls.CLIConfig{
TLSCaCert: p.signerConfig.TLSCaCert,
TLSCert: p.signerConfig.TLSCert,
TLSKey: p.signerConfig.TLSKey,
}
client, err := signer.NewSignerClient(log.Root(), p.signerConfig.URL, tlsConfig)
log.Debug("signerclient", "client", client, "err", err)
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("could not initialize signer client")
}
signedTx, err := client.SignTransaction(ctx, &p.walletConfig.ChainID, tx)
log.Debug("signedtx", "tx", signedTx, "err", err)
if err != nil {
return nil, err
}
return signedTx, nil
} else {
return nil, errors.New("invalid signer method")
}
}
func (p *Provider) nonce(ctx context.Context, client *ethclient.Client) (uint64, error) {
fromAddress := common.HexToAddress(p.walletConfig.Address)
return client.PendingNonceAt(ctx, fromAddress)
} }
// Roundtrip send a new transaction to measure round trip latency // Roundtrip send a new transaction to measure round trip latency
......
...@@ -8,17 +8,23 @@ import ( ...@@ -8,17 +8,23 @@ import (
) )
type Provider struct { type Provider struct {
name string name string
config *config.ProviderConfig config *config.ProviderConfig
cancelFunc context.CancelFunc signerConfig *config.SignerServiceConfig
walletConfig *config.WalletConfig
cancelFunc context.CancelFunc
client *http.Client client *http.Client
} }
func New(name string, cfg *config.ProviderConfig) *Provider { func New(name string, cfg *config.ProviderConfig,
signerConfig *config.SignerServiceConfig,
walletConfig *config.WalletConfig) *Provider {
p := &Provider{ p := &Provider{
name: name, name: name,
config: cfg, config: cfg,
signerConfig: signerConfig,
walletConfig: walletConfig,
client: http.DefaultClient, client: http.DefaultClient,
} }
......
...@@ -30,7 +30,11 @@ func (s *Service) Start(ctx context.Context) { ...@@ -30,7 +30,11 @@ func (s *Service) Start(ctx context.Context) {
log.Info("healthz started") log.Info("healthz started")
} }
for name, providerConfig := range s.Config.Providers { for name, providerConfig := range s.Config.Providers {
s.Providers[name] = provider.New(name, providerConfig) if providerConfig.Disabled {
log.Info("provider is disabled", "provider", name)
continue
}
s.Providers[name] = provider.New(name, providerConfig, &s.Config.Signer, s.Config.Wallets[providerConfig.Wallet])
s.Providers[name].Start(ctx) s.Providers[name].Start(ctx)
log.Info("provider started", "provider", name) log.Info("provider started", "provider", name)
} }
......
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