Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ vendor
dist
out/
cache/
/challenger
58 changes: 32 additions & 26 deletions cmd/challenger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (
"context"
_ "embed"
"fmt"
"math/big"
"net/http"
"os"
"os/signal"
"strings"
"sync"

challenger "github.com/chronicleprotocol/challenger/core"
Expand Down Expand Up @@ -53,15 +53,10 @@ type options struct {
FromBlock int64
ChainID uint64
TransactionType string
MetricsAddr string
LogLevel string
}

var (
maxGasLimit = uint64(0)
maxGasFee = (*big.Int)(nil)
maxGasPriorityFee = (*big.Int)(nil)
gasFeeMultiplier = float64(1)
gasPriorityFeeMultiplier = float64(1)
)

// Checks and return private key based on given options
func (o *options) getKey() (*wallet.PrivateKey, error) {
Expand Down Expand Up @@ -92,7 +87,7 @@ func (o *options) getKey() (*wallet.PrivateKey, error) {
if err != nil {
return nil, fmt.Errorf("failed to read password file: %v", err)
}
password = string(p)
password = strings.TrimRight(string(p), "\n\r")
}

return wallet.NewKeyFromJSON(o.Key, password)
Expand All @@ -102,11 +97,14 @@ func main() {
var opts options
cmd := &cobra.Command{
Use: "run",
Args: cobra.ExactArgs(1),
Args: cobra.NoArgs,
Aliases: []string{"agent"},
Run: func(cmd *cobra.Command, args []string) {
// TODO: update after completion
logger.SetLevel(logger.DebugLevel)
lvl, err := logger.ParseLevel(opts.LogLevel)
if err != nil {
logger.Fatalf("Invalid log level %q: %v", opts.LogLevel, err)
}
logger.SetLevel(lvl)

logger.Debugf("Hello, Challenger!")

Expand Down Expand Up @@ -164,19 +162,19 @@ func main() {
switch opts.TransactionType {
case "legacy":
txModifiers = append(txModifiers, txmodifier.NewLegacyGasFeeEstimator(txmodifier.LegacyGasFeeEstimatorOptions{
Multiplier: gasFeeMultiplier,
Multiplier: 1,
MinGasPrice: nil,
MaxGasPrice: maxGasFee,
MaxGasPrice: nil,
Replace: false,
}))
case "eip1559":
txModifiers = append(txModifiers, txmodifier.NewEIP1559GasFeeEstimator(txmodifier.EIP1559GasFeeEstimatorOptions{
GasPriceMultiplier: gasFeeMultiplier,
PriorityFeePerGasMultiplier: gasPriorityFeeMultiplier,
GasPriceMultiplier: 1,
PriorityFeePerGasMultiplier: 1,
MinGasPrice: nil,
MaxGasPrice: maxGasFee,
MaxGasPrice: nil,
MinPriorityFeePerGas: nil,
MaxPriorityFeePerGas: maxGasPriorityFee,
MaxPriorityFeePerGas: nil,
Replace: false,
}))
case "", "none":
Expand All @@ -194,7 +192,7 @@ func main() {
// Set manual gas limit for flashbots, they might require more gas.
//nolint:gocritic
baseTxModifiers := append(txModifiers, txmodifier.NewGasLimitEstimator(txmodifier.GasLimitEstimatorOptions{
MaxGas: maxGasLimit,
MaxGas: 0,
Multiplier: defaultGasLimitMultiplier,
}))

Expand Down Expand Up @@ -255,7 +253,6 @@ func main() {
challenger.ErrorsCounter.WithLabelValues(
addr.String(),
p.GetFrom(ctx).String(),
err.Error(),
).Inc()

logger.Fatalf("Failed to run challenger: %v", err)
Expand All @@ -270,11 +267,16 @@ func main() {
challenger.LastScannedBlockGauge,
)
http.Handle("/metrics", promhttp.Handler())
// TODO: move `:9090` to config
logger.
WithError(http.ListenAndServe(":9090", nil)). //nolint:gosec
Error("metrics server error")
<-ctx.Done()
srv := &http.Server{Addr: opts.MetricsAddr} //nolint:gosec
go func() {
<-ctx.Done()
if err := srv.Shutdown(context.Background()); err != nil {
logger.WithError(err).Error("metrics server shutdown error")
}
}()
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.WithError(err).Error("metrics server error")
}
}()

wg.Wait()
Expand All @@ -292,6 +294,10 @@ func main() {
Int64Var(&opts.FromBlock, "from-block", 0, "Block number to start from. If not provided, binary will try to get it from given RPC")
cmd.PersistentFlags().Uint64Var(&opts.ChainID, "chain-id", 0, "If no chain_id provided binary will try to get chain_id from given RPC")
cmd.PersistentFlags().StringVar(&opts.TransactionType, "tx-type", "none", "Transaction type definition, possible values are: `legacy`, `eip1559` or `none`")
cmd.PersistentFlags().StringVar(&opts.MetricsAddr, "metrics-addr", ":9090", "Address for the Prometheus metrics server")
cmd.PersistentFlags().StringVar(&opts.LogLevel, "log-level", "info", "Log level: trace, debug, info, warn, error, fatal, panic")

_ = cmd.Execute()
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
70 changes: 42 additions & 28 deletions core/challenger.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type Challenger struct {
provider IScribeOptimisticProvider
lastProcessedBlock *big.Int
wg *sync.WaitGroup
inFlight map[uint64]struct{}
inFlightMu sync.Mutex
}

// NewChallenger creates a new instance of Challenger.
Expand All @@ -58,6 +60,7 @@ func NewChallenger(
provider: provider,
lastProcessedBlock: latestBlock,
wg: wg,
inFlight: make(map[uint64]struct{}),
}
}

Expand Down Expand Up @@ -125,8 +128,28 @@ func (c *Challenger) isPokeChallengeable(poke *OpPokedEvent, challengePeriod uin
}

// SpawnChallenge spawns new goroutine and challenges the `OpPoked` event.
// It skips the challenge if one is already in-flight for the same block number.
func (c *Challenger) SpawnChallenge(poke *OpPokedEvent) {
blockNum := poke.BlockNumber.Uint64()

c.inFlightMu.Lock()
if _, ok := c.inFlight[blockNum]; ok {
c.inFlightMu.Unlock()
logger.
WithField("address", c.address).
Debugf("Skipping duplicate challenge for block %v, already in-flight", poke.BlockNumber)
return
}
c.inFlight[blockNum] = struct{}{}
c.inFlightMu.Unlock()

go func() {
defer func() {
c.inFlightMu.Lock()
delete(c.inFlight, blockNum)
c.inFlightMu.Unlock()
}()

logger.
WithField("address", c.address).
Warnf("Challenging OpPoked event from block %v", poke.BlockNumber)
Expand Down Expand Up @@ -212,31 +235,33 @@ func (c *Challenger) executeTick() error {
return nil
}

func (c *Challenger) handleTickError(err error) {
if err == nil {
return
}
logger.
WithField("address", c.address).
Errorf("Failed to execute tick with error: %v", err)
ErrorsCounter.WithLabelValues(
c.address.String(),
c.provider.GetFrom(c.ctx).String(),
).Inc()
}

// Run starts the challenger processing loop.
// If you provide `subscriptionURL` - it will listen for events from WS connection otherwise, it will poll for new events every 30 seconds.
// It polls for new events every 30 seconds.
func (c *Challenger) Run() error {
defer c.wg.Done()

// Executing first tick
err := c.executeTick()
if err != nil {
logger.
WithField("address", c.address).
Errorf("Failed to execute tick with error: %v", err)

// Add error to metrics
ErrorsCounter.WithLabelValues(
c.address.String(),
c.provider.GetFrom(c.ctx).String(),
err.Error(),
).Inc()
}
c.handleTickError(c.executeTick())

logger.
WithField("address", c.address).
Infof("Started contract monitoring")

ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()

for {
select {
Expand All @@ -251,18 +276,7 @@ func (c *Challenger) Run() error {
WithField("address", c.address).
Debugf("Tick at: %v", t)

err := c.executeTick()
if err != nil {
logger.
WithField("address", c.address).
Errorf("Failed to execute tick with error: %v", err)
// Add error to metrics
ErrorsCounter.WithLabelValues(
c.address.String(),
c.provider.GetFrom(c.ctx).String(),
err.Error(),
).Inc()
}
c.handleTickError(c.executeTick())
}
}
}
Expand All @@ -278,7 +292,7 @@ func PickUnchallengedPokes(pokes []*OpPokedEvent, challenges []*OpPokeChallenged

if len(pokes) == 1 {
for _, challenge := range challenges {
if challenge.BlockNumber.Cmp(pokes[0].BlockNumber) == -1 {
if challenge.BlockNumber.Cmp(pokes[0].BlockNumber) >= 0 {
return result
}
}
Expand All @@ -301,7 +315,7 @@ func PickUnchallengedPokes(pokes []*OpPokedEvent, challenges []*OpPokeChallenged
result = append(result, ev)
continue
}
if len(sortable)-1 > i+1 && sortable[i+1].Name() == "OpPokeChallengedSuccessfullyEvent" {
if i+1 < len(sortable) && sortable[i+1].Name() == "OpPokeChallengedSuccessfullyEvent" {
continue
}
result = append(result, ev)
Expand Down
Loading
Loading