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

signer tls, send tx on heartbeat

parent 135a6e1d
......@@ -6,6 +6,9 @@ log_level = "debug"
[signer_service]
# URL to the signer service
url = "http://localhost:1234"
tls_ca_cert = "tls/ca.crt"
tls_cert = "tls/tls.crt"
tls_key = "tls/tls.key"
[healthz]
# Whether or not to enable healthz endpoint
......@@ -34,6 +37,14 @@ signer_method = "signer"
address="0x0123"
# For static signer method, the private key to use
# 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]
# URL to the RPC provider
......@@ -48,6 +59,8 @@ send_interval = "5s"
wallet = "default"
[providers.p2]
# Uncomment to disable this provider
# disabled=true
# URL to the RPC provider
url = "http://localhost:8552"
# Read only providers are only used to check for transactions
......
......@@ -4,23 +4,31 @@ go 1.20
require (
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/gorilla/mux v1.8.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.14.0
github.com/rs/cors v1.7.0
github.com/rs/cors v1.8.2
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/btcsuite/btcd/btcec/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/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/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.10 // 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
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
import (
"math/big"
"github.com/BurntSushi/toml"
"github.com/pkg/errors"
)
......@@ -17,7 +19,10 @@ type Config 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 {
......@@ -34,17 +39,23 @@ type HealthzConfig struct {
type WalletConfig struct {
// default: 420 (Optimism Goerli)
ChainID uint `toml:"chain_id"`
ChainID big.Int `toml:"chain_id"`
// signer | static, default: signer
SignerMethod string `toml:"signer_method"`
// for static signing
Address string `toml:"address"`
Address string `toml:"address"`
// private key is used for static signing
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 {
Disabled bool `toml:"disabled"`
URL string `toml:"url"`
Wallet string `toml:"wallet"`
ReadOnly bool `toml:"read_only"`
......@@ -81,7 +92,7 @@ func (c *Config) Validate() error {
}
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)
}
if wallet.SignerMethod != "signer" && wallet.SignerMethod != "static" {
......@@ -91,6 +102,15 @@ func (c *Config) Validate() error {
if c.Signer.URL == "" {
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.PrivateKey == "" {
......
......@@ -3,12 +3,105 @@ package provider
import (
"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"
)
// Heartbeat poll for expected transactions
func (p *Provider) Heartbeat(ctx context.Context) {
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
......
......@@ -8,17 +8,23 @@ import (
)
type Provider struct {
name string
config *config.ProviderConfig
cancelFunc context.CancelFunc
name string
config *config.ProviderConfig
signerConfig *config.SignerServiceConfig
walletConfig *config.WalletConfig
cancelFunc context.CancelFunc
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{
name: name,
config: cfg,
name: name,
config: cfg,
signerConfig: signerConfig,
walletConfig: walletConfig,
client: http.DefaultClient,
}
......
......@@ -30,7 +30,11 @@ func (s *Service) Start(ctx context.Context) {
log.Info("healthz started")
}
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)
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