diff --git a/p2p/cmd/main.go b/p2p/cmd/main.go index 78d38bf9f..f059cb014 100644 --- a/p2p/cmd/main.go +++ b/p2p/cmd/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "math/big" "os" "path/filepath" "slices" @@ -199,6 +200,19 @@ var ( Value: contracts.TestnetContracts.BlockTracker, }) + optionAutodepositAmount = altsrc.NewStringFlag(&cli.StringFlag{ + Name: "autodeposit-amount", + Usage: "amount to auto deposit", + EnvVars: []string{"MEV_COMMIT_AUTODEPOSIT_AMOUNT"}, + }) + + optionAutodepositEnabled = altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "autodeposit-enabled", + Usage: "enable auto deposit", + EnvVars: []string{"MEV_COMMIT_AUTODEPOSIT_ENABLED"}, + Value: false, + }) + optionSettlementRPCEndpoint = altsrc.NewStringFlag(&cli.StringFlag{ Name: "settlement-rpc-endpoint", Usage: "rpc endpoint of the settlement layer", @@ -260,6 +274,8 @@ func main() { optionProviderRegistryAddr, optionPreconfStoreAddr, optionBlockTrackerAddr, + optionAutodepositAmount, + optionAutodepositEnabled, optionSettlementRPCEndpoint, optionSettlementWSRPCEndpoint, optionNATAddr, @@ -326,6 +342,16 @@ func launchNodeWithConfig(c *cli.Context) error { natAddr = fmt.Sprintf("%s:%d", c.String(optionNATAddr.Name), c.Int(optionNATPort.Name)) } + var ( + autodepositAmount *big.Int + ok bool + ) + if c.String(optionAutodepositAmount.Name) != "" && c.Bool(optionAutodepositEnabled.Name) { + autodepositAmount, ok = new(big.Int).SetString(c.String(optionAutodepositAmount.Name), 10) + if !ok { + return fmt.Errorf("failed to parse autodeposit amount %q", c.String(optionAutodepositAmount.Name)) + } + } crtFile := c.String(optionServerTLSCert.Name) keyFile := c.String(optionServerTLSPrivateKey.Name) if (crtFile == "") != (keyFile == "") { @@ -346,6 +372,7 @@ func launchNodeWithConfig(c *cli.Context) error { ProviderRegistryContract: c.String(optionProviderRegistryAddr.Name), BidderRegistryContract: c.String(optionBidderRegistryAddr.Name), BlockTrackerContract: c.String(optionBlockTrackerAddr.Name), + AutodepositAmount: autodepositAmount, RPCEndpoint: c.String(optionSettlementRPCEndpoint.Name), WSRPCEndpoint: c.String(optionSettlementWSRPCEndpoint.Name), NatAddr: natAddr, diff --git a/p2p/pkg/autodepositor/autodepositor.go b/p2p/pkg/autodepositor/autodepositor.go index dfc8713f4..e47a0b180 100644 --- a/p2p/pkg/autodepositor/autodepositor.go +++ b/p2p/pkg/autodepositor/autodepositor.go @@ -22,6 +22,10 @@ type BidderRegistryContract interface { WithdrawFromWindows(opts *bind.TransactOpts, windows []*big.Int) (*types.Transaction, error) } +type BlockTrackerContract interface { + GetCurrentWindow() (*big.Int, error) +} + type AutoDepositTracker struct { startMu sync.Mutex isWorking bool @@ -29,6 +33,8 @@ type AutoDepositTracker struct { deposits sync.Map windowChan chan *blocktracker.BlocktrackerNewWindow brContract BidderRegistryContract + btContract BlockTrackerContract + initialAmount *big.Int optsGetter OptsGetter logger *slog.Logger cancelFunc context.CancelFunc @@ -37,14 +43,18 @@ type AutoDepositTracker struct { func New( evtMgr events.EventManager, brContract BidderRegistryContract, + btContract BlockTrackerContract, optsGetter OptsGetter, + initialAmount *big.Int, logger *slog.Logger, ) *AutoDepositTracker { return &AutoDepositTracker{ eventMgr: evtMgr, brContract: brContract, + btContract: btContract, optsGetter: optsGetter, windowChan: make(chan *blocktracker.BlocktrackerNewWindow, 1), + initialAmount: initialAmount, logger: logger, } } @@ -60,28 +70,76 @@ func (adt *AutoDepositTracker) Start( return fmt.Errorf("auto deposit tracker is already running") } + if startWindow == nil { + var err error + startWindow, err = adt.btContract.GetCurrentWindow() + if err != nil { + adt.logger.Error("failed to get current window", "error", err) + return err + } + // adding +2 as oracle runs two windows behind + startWindow = new(big.Int).Add(startWindow, big.NewInt(2)) + + } + + eg, egCtx := errgroup.WithContext(context.Background()) + egCtx, cancel := context.WithCancel(egCtx) + adt.cancelFunc = cancel + + sub, err := adt.initSub(egCtx) + + if err != nil { + return fmt.Errorf("error subscribing to event: %w", err) + } + + err = adt.doInitialDeposit(ctx, startWindow, amount) + if err != nil { + return fmt.Errorf("failed to do initial deposit, err: %w", err) + } + + adt.startAutodeposit(egCtx, eg, amount, sub) + + started := make(chan struct{}) + go func() { + close(started) + if err := eg.Wait(); err != nil { + adt.logger.Error("error in errgroup", "err", err) + } + adt.startMu.Lock() + adt.isWorking = false + adt.startMu.Unlock() + }() + select { + case <-ctx.Done(): + return ctx.Err() + case <-started: + adt.isWorking = true + } + return nil +} + +func (adt *AutoDepositTracker) doInitialDeposit(ctx context.Context, startWindow, amount *big.Int) error { nextWindow := new(big.Int).Add(startWindow, big.NewInt(1)) opts, err := adt.optsGetter(ctx) if err != nil { - return err + return fmt.Errorf("failed to get transact opts, err: %w", err) } - opts.Value = big.NewInt(0).Mul(amount, big.NewInt(2)) // Make initial deposit for the first two windows _, err = adt.brContract.DepositForWindows(opts, []*big.Int{startWindow, nextWindow}) if err != nil { - return err + return fmt.Errorf("failed to deposit for windows, err: %w", err) } adt.deposits.Store(startWindow.Uint64(), true) adt.deposits.Store(nextWindow.Uint64(), true) - eg, egCtx := errgroup.WithContext(context.Background()) - egCtx, cancel := context.WithCancel(egCtx) - adt.cancelFunc = cancel + return nil +} +func (adt *AutoDepositTracker) initSub(egCtx context.Context) (events.Subscription, error) { evt := events.NewEventHandler( "NewWindow", func(update *blocktracker.BlocktrackerNewWindow) { @@ -98,9 +156,12 @@ func (adt *AutoDepositTracker) Start( sub, err := adt.eventMgr.Subscribe(evt) if err != nil { - return fmt.Errorf("error subscribing to event: %w", err) + return nil, fmt.Errorf("error subscribing to event: %w", err) } + return sub, nil +} +func (adt *AutoDepositTracker) startAutodeposit(egCtx context.Context, eg *errgroup.Group, amount *big.Int, sub events.Subscription) { eg.Go(func() error { for { select { @@ -161,24 +222,6 @@ func (adt *AutoDepositTracker) Start( } } }) - - started := make(chan struct{}) - go func() { - close(started) - if err := eg.Wait(); err != nil { - adt.logger.Error("error in errgroup", "err", err) - } - adt.startMu.Lock() - adt.isWorking = false - adt.startMu.Unlock() - }() - select { - case <-ctx.Done(): - return ctx.Err() - case <-started: - adt.isWorking = true - } - return nil } func (adt *AutoDepositTracker) Stop() ([]*big.Int, error) { diff --git a/p2p/pkg/autodepositor/autodepositor_test.go b/p2p/pkg/autodepositor/autodepositor_test.go index d291f3fa9..a65cf67e2 100644 --- a/p2p/pkg/autodepositor/autodepositor_test.go +++ b/p2p/pkg/autodepositor/autodepositor_test.go @@ -33,6 +33,14 @@ func (m *MockBidderRegistryContract) WithdrawFromWindows(opts *bind.TransactOpts return m.WithdrawFromWindowsFunc(opts, windows) } +type MockBlockTrackerContract struct { + GetCurrentWindowFunc func() (*big.Int, error) +} + +func (m *MockBlockTrackerContract) GetCurrentWindow() (*big.Int, error) { + return m.GetCurrentWindowFunc() +} + func TestAutoDepositTracker_Start(t *testing.T) { t.Parallel() @@ -47,6 +55,7 @@ func TestAutoDepositTracker_Start(t *testing.T) { t.Fatal(err) } + amount := big.NewInt(100) logger := util.NewTestLogger(os.Stdout) evtMgr := events.NewListener(logger, &btABI, &brABI) brContract := &MockBidderRegistryContract{ @@ -57,17 +66,21 @@ func TestAutoDepositTracker_Start(t *testing.T) { return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil }, } + btContract := &MockBlockTrackerContract{ + GetCurrentWindowFunc: func() (*big.Int, error) { + return big.NewInt(1), nil + }, + } optsGetter := func(ctx context.Context) (*bind.TransactOpts, error) { return &bind.TransactOpts{}, nil } // Create AutoDepositTracker instance - adt := autodepositor.New(evtMgr, brContract, optsGetter, logger) + adt := autodepositor.New(evtMgr, brContract, btContract, optsGetter, nil, logger) // Start AutoDepositTracker ctx := context.Background() startWindow := big.NewInt(2) - amount := big.NewInt(100) err = adt.Start(ctx, startWindow, amount) if err != nil { t.Fatal(err) @@ -150,12 +163,17 @@ func TestAutoDepositTracker_Start_CancelContext(t *testing.T) { return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil }, } + btContract := &MockBlockTrackerContract{ + GetCurrentWindowFunc: func() (*big.Int, error) { + return big.NewInt(1), nil + }, + } optsGetter := func(ctx context.Context) (*bind.TransactOpts, error) { return &bind.TransactOpts{}, nil } // Create AutoDepositTracker instance - adt := autodepositor.New(evtMgr, brContract, optsGetter, logger) + adt := autodepositor.New(evtMgr, brContract, btContract, optsGetter, nil, logger) // Start AutoDepositTracker with a cancelable context ctx, cancel := context.WithCancel(context.Background()) @@ -185,12 +203,13 @@ func TestAutoDepositTracker_Stop_NotRunning(t *testing.T) { logger := util.NewTestLogger(io.Discard) evtMgr := events.NewListener(logger, &btABI, &brABI) brContract := &MockBidderRegistryContract{} + btContract := &MockBlockTrackerContract{} optsGetter := func(ctx context.Context) (*bind.TransactOpts, error) { return &bind.TransactOpts{}, nil } // Create AutoDepositTracker instance - adt := autodepositor.New(evtMgr, brContract, optsGetter, logger) + adt := autodepositor.New(evtMgr, brContract, btContract, optsGetter, nil, logger) // Stop AutoDepositTracker when not running _, err = adt.Stop() @@ -220,12 +239,18 @@ func TestAutoDepositTracker_IsWorking(t *testing.T) { return types.NewTransaction(1, common.Address{}, nil, 0, nil, nil), nil }, } + btContract := &MockBlockTrackerContract{ + GetCurrentWindowFunc: func() (*big.Int, error) { + return big.NewInt(1), nil + }, + } + optsGetter := func(ctx context.Context) (*bind.TransactOpts, error) { return &bind.TransactOpts{}, nil } // Create AutoDepositTracker instance - adt := autodepositor.New(evtMgr, brContract, optsGetter, logger) + adt := autodepositor.New(evtMgr, brContract, btContract, optsGetter, nil, logger) // Assert initial IsWorking status if adt.IsWorking() { @@ -272,3 +297,4 @@ func publishNewWindow( } evtMgr.PublishLogEvent(context.Background(), testLog) } + diff --git a/p2p/pkg/node/node.go b/p2p/pkg/node/node.go index bac3c5cea..933e530af 100644 --- a/p2p/pkg/node/node.go +++ b/p2p/pkg/node/node.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "log/slog" + "math/big" "net" "net/http" "strings" @@ -74,6 +75,7 @@ type Options struct { BlockTrackerContract string ProviderRegistryContract string BidderRegistryContract string + AutodepositAmount *big.Int RPCEndpoint string WSRPCEndpoint string NatAddr string @@ -82,8 +84,9 @@ type Options struct { } type Node struct { - cancelFunc context.CancelFunc - closers []io.Closer + cancelFunc context.CancelFunc + closers []io.Closer + autoDeposit *autodepositor.AutoDepositTracker } func NewNode(opts *Options) (*Node, error) { @@ -439,10 +442,17 @@ func NewNode(opts *Options) (*Node, error) { autoDeposit := autodepositor.New( evtMgr, bidderRegistry, + blockTrackerSession, optsGetter, + opts.AutodepositAmount, opts.Logger.With("component", "auto_deposit_tracker"), ) + if opts.AutodepositAmount != nil { + autoDeposit.Start(context.Background(), nil, opts.AutodepositAmount) + nd.autoDeposit = autoDeposit + } + bidderAPI := bidderapi.NewService( opts.KeySigner.GetAddress(), blocksPerWindow, @@ -643,6 +653,9 @@ func (n *Node) Close() error { for _, c := range n.closers { err = errors.Join(err, c.Close()) } + + _, adErr := n.autoDeposit.Stop() + errors.Join(err, adErr) return err }