Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

protecccc validators bls #1473

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
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
14 changes: 14 additions & 0 deletions cosmos/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ func readConfigFromAppOptsParser(parser AppOptionsParser) (*Config, error) {
return nil, err
}

if conf.Polar.IsValidator, err = parser.GetBool(flags.IsValidator); err != nil {
return nil, err
}

if conf.Polar.ValidatorJSONRPCEndpoint, err =
parser.GetString(flags.ValidatorJSONRPCEndpoint); err != nil {
return nil, err
}

if conf.Polar.ForceForwardReCheckTxs, err = parser.GetBool(
flags.ForceForwardReCheckTxs); err != nil {
return nil, err
}

// Polar Miner settings
if conf.Polar.Miner.Etherbase, err =
parser.GetCommonAddress(flags.MinerEtherbase); err != nil {
Expand Down
9 changes: 6 additions & 3 deletions cosmos/config/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ const (
OptimisticExecution = "polaris.optimistic-execution"

// Polar Root.
RPCEvmTimeout = "polaris.polar.rpc-evm-timeout"
RPCTxFeeCap = "polaris.polar.rpc-tx-fee-cap"
RPCGasCap = "polaris.polar.rpc-gas-cap"
RPCEvmTimeout = "polaris.polar.rpc-evm-timeout"
RPCTxFeeCap = "polaris.polar.rpc-tx-fee-cap"
RPCGasCap = "polaris.polar.rpc-gas-cap"
IsValidator = "polaris.polar.is-validator"
ValidatorJSONRPCEndpoint = "polaris.polar.validator-jsonrpc-endpoint"
ForceForwardReCheckTxs = "polaris.polar.force-forward-recheck-txs"
itsdevbear marked this conversation as resolved.
Show resolved Hide resolved

// Miner.
MinerEtherbase = "polaris.polar.miner.etherbase"
Expand Down
9 changes: 9 additions & 0 deletions cosmos/config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ rpc-evm-timeout = "{{ .Polaris.Polar.RPCEVMTimeout }}"
# Transaction fee cap for RPC requests
rpc-tx-fee-cap = "{{ .Polaris.Polar.RPCTxFeeCap }}"

# JSON-RPC endpoint for forwarding ethereum transactions directly to validators.
validator-jsonrpc-endpoint = "{{ .Polaris.Polar.ValidatorJSONRPCEndpoint }}"

# Whether the node is a validator
is-validator = {{ .Polaris.Polar.IsValidator }}

# If we want to force forwarding on ReCheckTxs
force-forward-recheck-txs = {{ .Polaris.Polar.ForceForwardReCheckTxs }}

# Chain config
[polaris.polar.chain]
chain-id = "{{ .Polaris.Polar.Chain.ChainID }}"
Expand Down
9 changes: 7 additions & 2 deletions cosmos/runtime/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ import (
type Provider struct {
evmAnteHandler sdk.AnteHandler // Ante handler for EVM transactions
cosmosAnteHandler sdk.AnteHandler // Ante handler for Cosmos transactions
isValidator bool
}

// NewAnteHandler creates a new Provider with a mempool and Cosmos ante handler.
// It sets up the EVM ante handler with the necessary decorators.
func NewAnteHandler(
mempool *txpool.Mempool, cosmosAnteHandler sdk.AnteHandler,
mempool *txpool.Mempool, cosmosAnteHandler sdk.AnteHandler, isValidator bool,
) *Provider {
evmAnteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(), // Set up the context decorator for the EVM ante handler
Expand All @@ -55,6 +56,7 @@ func NewAnteHandler(
return &Provider{
evmAnteHandler: sdk.ChainAnteDecorators(evmAnteDecorators...),
cosmosAnteHandler: cosmosAnteHandler,
isValidator: isValidator,
}
}

Expand All @@ -65,8 +67,11 @@ func (ah *Provider) AnteHandler() func(
) (sdk.Context, error) {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
// If the transaction contains a single EVM transaction, use the EVM ante handler
if len(tx.GetMsgs()) == 1 {
if len(tx.GetMsgs()) == 1 { //nolint:nestif // todo:fix.
if _, ok := tx.GetMsgs()[0].(*evmtypes.WrappedEthereumTransaction); ok {
if ah.isValidator {
return ctx, errors.New("validator cannot accept EVM from comet")
}
return ah.evmAnteHandler(ctx, tx, simulate)
} else if _, ok = tx.GetMsgs()[0].(*evmtypes.WrappedPayloadEnvelope); ok {
if ctx.ExecMode() != sdk.ExecModeCheck {
Expand Down
11 changes: 9 additions & 2 deletions cosmos/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ type Polaris struct {
// into the txpool are happening during this process. The mempool object then read locks for
// adding transactions into the txpool.
blockBuilderMu sync.RWMutex

cfg *eth.Config
}

// New creates a new Polaris runtime from the provided dependencies.
Expand All @@ -105,6 +107,7 @@ func New(
var err error
p := &Polaris{
logger: logger,
cfg: cfg,
}

ctx := sdk.Context{}.
Expand All @@ -124,11 +127,15 @@ func New(

priceLimit := big.NewInt(0).SetUint64(cfg.Polar.LegacyTxPool.PriceLimit)
p.WrappedTxPool = txpool.New(
p.logger,
p.ExecutionLayer.Backend().Blockchain(),
p.ExecutionLayer.Backend().TxPool(),
int64(cfg.Polar.LegacyTxPool.Lifetime),
&p.blockBuilderMu,
priceLimit,
p.cfg.Polar.IsValidator,
p.cfg.Polar.ForceForwardReCheckTxs,
p.cfg.Polar.ValidatorJSONRPCEndpoint,
)

return p
Expand Down Expand Up @@ -163,7 +170,7 @@ func (p *Polaris) Build(
}

app.SetAnteHandler(
antelib.NewAnteHandler(p.WrappedTxPool, cosmHandler).AnteHandler(),
antelib.NewAnteHandler(p.WrappedTxPool, cosmHandler, p.cfg.Polar.IsValidator).AnteHandler(),
)

return nil
Expand All @@ -177,7 +184,7 @@ func (p *Polaris) SetupServices(clientCtx client.Context) error {
clientCtx.TxConfig, evmtypes.WrapPayload))

// Initialize the txpool with a new transaction serializer.
p.WrappedTxPool.Init(p.logger, clientCtx, libtx.NewSerializer[*ethtypes.Transaction](
p.WrappedTxPool.Init(clientCtx, libtx.NewSerializer[*ethtypes.Transaction](
clientCtx.TxConfig, evmtypes.WrapTx))

// Register services with Polaris.
Expand Down
5 changes: 3 additions & 2 deletions cosmos/runtime/txpool/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ func (m *Mempool) AnteHandle(
telemetry.IncrCounter(float32(1), MetricKeyCometPoolTxs)
msgs := tx.GetMsgs()

// TODO: Record the time it takes to build a payload.

// We only want to eject transactions from comet on recheck.
if ctx.ExecMode() == sdk.ExecModeCheck || ctx.ExecMode() == sdk.ExecModeReCheck {
if wet, ok := utils.GetAs[*types.WrappedEthereumTransaction](msgs[0]); ok {
Expand All @@ -55,6 +53,9 @@ func (m *Mempool) AnteHandle(
); shouldEject {
telemetry.IncrCounter(float32(1), MetricKeyAnteEjectedTxs)
return ctx, errors.New("eject from comet mempool")
} else if ctx.ExecMode() == sdk.ExecModeReCheck && m.forceBroadcastOnRecheck {
// We optionally force a re-broadcast.
m.ForwardToValidator(ethTx)
}
}
}
Expand Down
28 changes: 19 additions & 9 deletions cosmos/runtime/txpool/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,28 +96,35 @@ type handler struct {

// Queue for failed transactions
failedTxs chan *failedTx

isValidator bool
}

// newHandler creates a new handler.
func newHandler(
clientCtx TxBroadcaster, txPool TxSubProvider, serializer TxSerializer,
crc CometRemoteCache, logger log.Logger,
crc CometRemoteCache, logger log.Logger, isValidator bool,
) *handler {
h := &handler{
logger: logger,
clientCtx: clientCtx,
serializer: serializer,
crc: crc,
txPool: txPool,
txsCh: make(chan core.NewTxsEvent, txChanSize),
stopCh: make(chan struct{}),
failedTxs: make(chan *failedTx, txChanSize),
logger: logger,
clientCtx: clientCtx,
serializer: serializer,
crc: crc,
txPool: txPool,
txsCh: make(chan core.NewTxsEvent, txChanSize),
stopCh: make(chan struct{}),
failedTxs: make(chan *failedTx, txChanSize),
isValidator: isValidator,
}
return h
}

// Start starts the handler.
func (h *handler) Start() error {
if h.isValidator {
return nil
}

if h.running.Load() {
return errors.New("handler already started")
}
Expand All @@ -129,6 +136,9 @@ func (h *handler) Start() error {

// Stop stops the handler.
func (h *handler) Stop() error {
if h.isValidator {
return nil
}
if !h.Running() {
return errors.New("handler already stopped")
}
Expand Down
4 changes: 3 additions & 1 deletion cosmos/runtime/txpool/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ var _ = Describe("", func() {
subprovider = mocks.NewTxSubProvider(t)
subprovider.On("SubscribeTransactions", mock.Anything, mock.Anything).Return(subscription)
serializer = mocks.NewTxSerializer(t)
h = newHandler(broadcaster, subprovider, serializer, newCometRemoteCache(), log.NewTestLogger(t))
h = newHandler(
broadcaster, subprovider, serializer,
newCometRemoteCache(), log.NewTestLogger(t), false)
err := h.Start()
Expect(err).NotTo(HaveOccurred())
for !h.Running() {
Expand Down
83 changes: 74 additions & 9 deletions cosmos/runtime/txpool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"errors"
"math/big"
"sync"
"time"

"cosmossdk.io/log"

Expand All @@ -39,6 +40,12 @@ import (

ethtxpool "github.com/ethereum/go-ethereum/core/txpool"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)

const (
attempts = 5
retryInterval = 5 * time.Second
)

// Mempool implements the mempool.Mempool & Lifecycle interfaces.
Expand All @@ -64,36 +71,75 @@ type GethTxPool interface {
// geth txpool during `CheckTx`, that is the only purpose of `Mempool“.
type Mempool struct {
eth.TxPool
logger log.Logger
lifetime int64
chain core.ChainReader
handler Lifecycle
crc CometRemoteCache
blockBuilderMu *sync.RWMutex
priceLimit *big.Int

isValidator bool
validatorJSONRPC string
forceBroadcastOnRecheck bool
ethclient *ethclient.Client
}

// New creates a new Mempool.
func New(
logger log.Logger,
chain core.ChainReader, txpool eth.TxPool, lifetime int64,
blockBuilderMu *sync.RWMutex, priceLimit *big.Int,
blockBuilderMu *sync.RWMutex, priceLimit *big.Int, isValidator,
forceBroadcastOnRecheck bool,
validatorJSONRPC string,
) *Mempool {
var (
ec *ethclient.Client
err error
)

if validatorJSONRPC != "" {
for attempt := 0; attempt < attempts; attempt++ {
logger.Info("Attempting to dial validator JSON RPC",
"url", validatorJSONRPC, "attempt", attempt+1)
ec, err = ethclient.Dial(validatorJSONRPC)
if err == nil {
logger.Info(
"Successfully connected to validator JSON RPC", "url", validatorJSONRPC)
break
}
if attempt < attempts-1 {
logger.Error("Failed to dial validator JSON RPC, retrying...", "error", err)
time.Sleep(retryInterval)
} else {
logger.Error(
"Failed to dial validator JSON RPC, no more retries left", "error", err)
panic(err)
itsdevbear marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

return &Mempool{
TxPool: txpool,
chain: chain,
lifetime: lifetime,
crc: newCometRemoteCache(),
blockBuilderMu: blockBuilderMu,
priceLimit: priceLimit,
logger: logger,
TxPool: txpool,
chain: chain,
lifetime: lifetime,
crc: newCometRemoteCache(),
blockBuilderMu: blockBuilderMu,
priceLimit: priceLimit,
isValidator: isValidator,
forceBroadcastOnRecheck: forceBroadcastOnRecheck,
validatorJSONRPC: validatorJSONRPC,
ethclient: ec,
}
}

// Init initializes the Mempool (notably the TxHandler).
func (m *Mempool) Init(
logger log.Logger,
txBroadcaster TxBroadcaster,
txSerializer TxSerializer,
) {
m.handler = newHandler(txBroadcaster, m.TxPool, txSerializer, m.crc, logger)
m.handler = newHandler(txBroadcaster, m.TxPool, txSerializer, m.crc, m.logger, m.isValidator)
}

// Start starts the Mempool TxHandler.
Expand All @@ -108,6 +154,10 @@ func (m *Mempool) Stop() error {

// Insert attempts to insert a Tx into the app-side mempool returning an error upon failure.
func (m *Mempool) Insert(ctx context.Context, sdkTx sdk.Tx) error {
if m.isValidator {
return errors.New("validator cannot insert transactions into the mempool from comet")
}
itsdevbear marked this conversation as resolved.
Show resolved Hide resolved

sCtx := sdk.UnwrapSDKContext(ctx)
msgs := sdkTx.GetMsgs()
if len(msgs) != 1 {
Expand All @@ -123,6 +173,9 @@ func (m *Mempool) Insert(ctx context.Context, sdkTx sdk.Tx) error {
// Add the eth tx to the Geth txpool.
ethTx := wet.Unwrap()

// Fowrad to a validator if we have one.
m.ForwardToValidator(ethTx)

// Insert the tx into the txpool as a remote.
m.blockBuilderMu.RLock()
errs := m.TxPool.Add([]*ethtypes.Transaction{ethTx}, false, false)
Expand All @@ -145,6 +198,18 @@ func (m *Mempool) Insert(ctx context.Context, sdkTx sdk.Tx) error {
return nil
}

func (m *Mempool) ForwardToValidator(ethTx *ethtypes.Transaction) {
if m.ethclient != nil {
// Broadcast the transaction to the validator.
// Note: we don't care about the response here.
go func() {
if err := m.ethclient.SendTransaction(context.Background(), ethTx); err != nil {
m.logger.Error("failed to broadcast transaction to validator", "error", err)
}
}()
}
}

// CountTx returns the number of transactions currently in the mempool.
func (m *Mempool) CountTx() int {
runnable, blocked := m.TxPool.Stats()
Expand Down
11 changes: 11 additions & 0 deletions eth/polar/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,15 @@ type Config struct {
// RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for
// send-transaction variants. The unit is ether.
RPCTxFeeCap float64

// ValidatorJSONRPCEndpoint is the JSON-RPC endpoint of a validator, you
// want to forward transactions to.
ValidatorJSONRPCEndpoint string

// IsValidator is a flag to indicate if the node is a validator.
IsValidator bool

// ForceForwardReCheckTxs is a flag to indicate if the node should forward
// transactions on recheck.
ForceForwardReCheckTxs bool
Comment on lines +106 to +115
Copy link

Choose a reason for hiding this comment

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

The addition of ValidatorJSONRPCEndpoint, IsValidator, and ForceForwardReCheckTxs fields to the Config struct aligns with the PR's objectives to enhance validator configurations. However, consider adding comments to describe the purpose and expected values for these new fields, especially ValidatorJSONRPCEndpoint and ForceForwardReCheckTxs, to improve code maintainability and clarity for future developers.


While the new fields ValidatorJSONRPCEndpoint, IsValidator, and ForceForwardReCheckTxs are crucial for enhancing validator configurations, it's important to ensure that their usage does not introduce security vulnerabilities (e.g., unvalidated input for ValidatorJSONRPCEndpoint could lead to SSRF attacks) or performance issues (e.g., excessive rechecking of transactions). Conduct thorough testing and validation to mitigate these risks.

}
Loading