From 8891e2b2facafb5d0e206c16064e3fb2cc50d18b Mon Sep 17 00:00:00 2001 From: Fornax <23104993+0xfornax@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:49:08 -0300 Subject: [PATCH] Remove cancel bond reduction and reduce bond tasks --- bindings/minipool/bond-reducer.go | 143 ------ rocketpool-cli/minipool/commands.go | 31 -- rocketpool-cli/minipool/reduce-bond.go | 207 --------- rocketpool/api/minipool/commands.go | 110 ----- rocketpool/api/minipool/reduce-bond.go | 279 ------------ rocketpool/api/minipool/utils.go | 5 - rocketpool/api/node/status.go | 62 --- rocketpool/node/node.go | 10 - rocketpool/node/reduce-bonds.go | 406 ------------------ .../watchtower/cancel-bond-reductions.go | 276 ------------ rocketpool/watchtower/watchtower.go | 10 - shared/types/api/minipool.go | 2 - shared/utils/rp/node.go | 105 +---- 13 files changed, 2 insertions(+), 1644 deletions(-) delete mode 100644 bindings/minipool/bond-reducer.go delete mode 100644 rocketpool-cli/minipool/reduce-bond.go delete mode 100644 rocketpool/api/minipool/reduce-bond.go delete mode 100644 rocketpool/node/reduce-bonds.go delete mode 100644 rocketpool/watchtower/cancel-bond-reductions.go diff --git a/bindings/minipool/bond-reducer.go b/bindings/minipool/bond-reducer.go deleted file mode 100644 index 3fa760844..000000000 --- a/bindings/minipool/bond-reducer.go +++ /dev/null @@ -1,143 +0,0 @@ -package minipool - -import ( - "fmt" - "math/big" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/bindings/rocketpool" -) - -// Estimate the gas required to vote to cancel a minipool's bond reduction -func EstimateVoteCancelReductionGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return rocketpool.GasInfo{}, err - } - return rocketMinipoolBondReducer.GetTransactionGasInfo(opts, "voteCancelReduction", minipoolAddress) -} - -// Vote to cancel a minipool's bond reduction -func VoteCancelReduction(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.TransactOpts) (common.Hash, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return common.Hash{}, err - } - tx, err := rocketMinipoolBondReducer.Transact(opts, "voteCancelReduction", minipoolAddress) - if err != nil { - return common.Hash{}, fmt.Errorf("error voting to cancel bond reduction for minipool %s: %w", minipoolAddress.Hex(), err) - } - return tx.Hash(), nil -} - -// Gets whether or not the bond reduction process for this minipool has already been cancelled -func GetReduceBondCancelled(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (bool, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return false, err - } - isCancelled := new(bool) - if err := rocketMinipoolBondReducer.Call(opts, isCancelled, "getReduceBondCancelled", minipoolAddress); err != nil { - return false, fmt.Errorf("error getting reduce bond cancelled status for minipool %s: %w", minipoolAddress.Hex(), err) - } - return *isCancelled, nil -} - -// Gets the time at which the MP owner started the bond reduction process -func GetReduceBondTime(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (time.Time, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return time.Time{}, err - } - reduceBondTime := new(*big.Int) - if err := rocketMinipoolBondReducer.Call(opts, reduceBondTime, "getReduceBondTime", minipoolAddress); err != nil { - return time.Time{}, fmt.Errorf("error getting reduce bond time for minipool %s: %w", minipoolAddress.Hex(), err) - } - return time.Unix((*reduceBondTime).Int64(), 0), nil -} - -// Gets the amount of ETH a minipool is reducing its bond to -func GetReduceBondValue(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return nil, err - } - reduceBondValue := new(*big.Int) - if err := rocketMinipoolBondReducer.Call(opts, reduceBondValue, "getReduceBondValue", minipoolAddress); err != nil { - return nil, fmt.Errorf("error getting reduce bond value for minipool %s: %w", minipoolAddress.Hex(), err) - } - return *reduceBondValue, nil -} - -// Gets the timestamp at which the bond was last reduced -func GetLastBondReductionTime(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (time.Time, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return time.Time{}, err - } - lastBondReductionTime := new(*big.Int) - if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionTime, "getLastBondReductionTime", minipoolAddress); err != nil { - return time.Time{}, fmt.Errorf("error getting last bond reduction time for minipool %s: %w", minipoolAddress.Hex(), err) - } - return time.Unix((*lastBondReductionTime).Int64(), 0), nil -} - -// Gets the previous bond amount of the minipool prior to its last reduction -func GetLastBondReductionPrevValue(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return nil, err - } - lastBondReductionPrevValue := new(*big.Int) - if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionPrevValue, "getLastBondReductionPrevValue", minipoolAddress); err != nil { - return nil, fmt.Errorf("error getting last bond reduction previous value for minipool %s: %w", minipoolAddress.Hex(), err) - } - return *lastBondReductionPrevValue, nil -} - -// Gets the previous node fee (commission) of the minipool prior to its last reduction -func GetLastBondReductionPrevNodeFee(rp *rocketpool.RocketPool, minipoolAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return nil, err - } - lastBondReductionPrevNodeFee := new(*big.Int) - if err := rocketMinipoolBondReducer.Call(opts, lastBondReductionPrevNodeFee, "getLastBondReductionPrevNodeFee", minipoolAddress); err != nil { - return nil, fmt.Errorf("error getting last bond reduction previous node fee for minipool %s: %w", minipoolAddress.Hex(), err) - } - return *lastBondReductionPrevNodeFee, nil -} - -// Estimate the gas required to begin a minipool bond reduction -func EstimateBeginReduceBondAmountGas(rp *rocketpool.RocketPool, minipoolAddress common.Address, newBondAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return rocketpool.GasInfo{}, err - } - return rocketMinipoolBondReducer.GetTransactionGasInfo(opts, "beginReduceBondAmount", minipoolAddress, newBondAmount) -} - -// Begin a minipool bond reduction -func BeginReduceBondAmount(rp *rocketpool.RocketPool, minipoolAddress common.Address, newBondAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { - rocketMinipoolBondReducer, err := getRocketMinipoolBondReducer(rp, nil) - if err != nil { - return common.Hash{}, err - } - tx, err := rocketMinipoolBondReducer.Transact(opts, "beginReduceBondAmount", minipoolAddress, newBondAmount) - if err != nil { - return common.Hash{}, fmt.Errorf("error beginning bond reduction for minipool %s: %w", minipoolAddress.Hex(), err) - } - return tx.Hash(), nil -} - -// Get contracts -var rocketMinipoolBondReducerLock sync.Mutex - -func getRocketMinipoolBondReducer(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { - rocketMinipoolBondReducerLock.Lock() - defer rocketMinipoolBondReducerLock.Unlock() - return rp.GetContract("rocketMinipoolBondReducer", opts) -} diff --git a/rocketpool-cli/minipool/commands.go b/rocketpool-cli/minipool/commands.go index 5c2765a51..a43aa3494 100644 --- a/rocketpool-cli/minipool/commands.go +++ b/rocketpool-cli/minipool/commands.go @@ -193,37 +193,6 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { }, }, - { - Name: "reduce-bond", - Aliases: []string{"rb"}, - Usage: "Manually completes the ETH bond reduction process for a minipool from 16 ETH down to 8 ETH once it is eligible. Note that `begin-bond-reduction` has been removed after Saturn 1.", - UsageText: "rocketpool minipool reduce-bond [options]", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "minipool, m", - Usage: "The minipool/s to reduce the bond for (address or 'all')", - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Validate flags - if c.String("minipool") != "" && c.String("minipool") != "all" { - if _, err := cliutils.ValidateAddress("minipool address", c.String("minipool")); err != nil { - return err - } - } - - // Run - return reduceBondAmount(c) - - }, - }, - { Name: "distribute-balance", Aliases: []string{"d"}, diff --git a/rocketpool-cli/minipool/reduce-bond.go b/rocketpool-cli/minipool/reduce-bond.go deleted file mode 100644 index 605bace5b..000000000 --- a/rocketpool-cli/minipool/reduce-bond.go +++ /dev/null @@ -1,207 +0,0 @@ -package minipool - -import ( - "bytes" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" - "github.com/rocket-pool/smartnode/bindings/utils/eth" - "github.com/rocket-pool/smartnode/shared/services/gas" - "github.com/rocket-pool/smartnode/shared/services/rocketpool" - "github.com/rocket-pool/smartnode/shared/types/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" - "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" - "github.com/urfave/cli" -) - -func reduceBondAmount(c *cli.Context) error { - - // Get RP client - rp, err := rocketpool.NewClientFromCtx(c).WithReady() - if err != nil { - return err - } - defer rp.Close() - - // Get minipool statuses - status, err := rp.MinipoolStatus() - if err != nil { - return err - } - - // Get the bond reduction variables - settingsResponse, err := rp.GetTNDAOMinipoolSettings() - if err != nil { - return err - } - - fmt.Println("NOTE: this function is used to complete the bond reduction process for a minipool. Note that `rocketpool minipool begin-bond-reduction` has been removed after Saturn 1.") - fmt.Println() - - // Get reduceable minipools - reduceableMinipools := []api.MinipoolDetails{} - for _, minipool := range status.Minipools { - timeSinceBondReductionStart := time.Since(minipool.ReduceBondTime) - nodeDepositBalance := eth.WeiToEth(minipool.Node.DepositBalance) - if nodeDepositBalance == 16 && timeSinceBondReductionStart > (time.Duration(settingsResponse.BondReductionWindowStart)*time.Second) && timeSinceBondReductionStart < (time.Duration(settingsResponse.BondReductionWindowStart+settingsResponse.BondReductionWindowLength)*time.Second) && !minipool.ReduceBondCancelled { - reduceableMinipools = append(reduceableMinipools, minipool) - } - } - - if len(reduceableMinipools) == 0 { - fmt.Println("No minipools can have their bond reduced at this time.") - return nil - } - - // Workaround for the fee distribution issue - err = forceFeeDistribution(c, rp) - if err != nil { - return err - } - - // Get selected minipools - var selectedMinipools []api.MinipoolDetails - if c.String("minipool") == "" { - - // Prompt for minipool selection - options := make([]string, len(reduceableMinipools)+1) - options[0] = "All available minipools" - for mi, minipool := range reduceableMinipools { - options[mi+1] = fmt.Sprintf("%s (Current bond: %d ETH)", minipool.Address.Hex(), int(eth.WeiToEth(minipool.Node.DepositBalance))) - } - selected, _ := prompt.Select("Please select a minipool to reduce the ETH bond for:", options) - - // Get minipools - if selected == 0 { - selectedMinipools = reduceableMinipools - } else { - selectedMinipools = []api.MinipoolDetails{reduceableMinipools[selected-1]} - } - - } else { - - // Get matching minipools - if c.String("minipool") == "all" { - selectedMinipools = reduceableMinipools - } else { - selectedAddress := common.HexToAddress(c.String("minipool")) - for _, minipool := range reduceableMinipools { - if bytes.Equal(minipool.Address.Bytes(), selectedAddress.Bytes()) { - selectedMinipools = []api.MinipoolDetails{minipool} - break - } - } - if selectedMinipools == nil { - return fmt.Errorf("The minipool %s cannot have its bond reduced.", selectedAddress.Hex()) - } - } - - } - - // Get the total gas limit estimate - var totalGas uint64 = 0 - var totalSafeGas uint64 = 0 - var gasInfo rocketpoolapi.GasInfo - for _, minipool := range selectedMinipools { - canResponse, err := rp.CanReduceBondAmount(minipool.Address) - if err != nil { - return fmt.Errorf("error checking if minipool %s can have its bond reduced: %w", minipool.Address.Hex(), err) - } else if !canResponse.CanReduce { - fmt.Printf("Minipool %s cannot have its bond reduced:\n", minipool.Address.Hex()) - fmt.Println("The minipool version is too low. Please run `rocketpool minipool delegate-upgrade` to update it.") - return nil - } else { - gasInfo = canResponse.GasInfo - totalGas += canResponse.GasInfo.EstGasLimit - totalSafeGas += canResponse.GasInfo.SafeGasLimit - } - } - gasInfo.EstGasLimit = totalGas - gasInfo.SafeGasLimit = totalSafeGas - - // Assign max fees - err = gas.AssignMaxFeeAndLimit(gasInfo, rp, c.Bool("yes")) - if err != nil { - return err - } - - // Prompt for confirmation - if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to reduce the bond for %d minipools from 16 ETH to 8 ETH?", len(selectedMinipools))) { - fmt.Println("Cancelled.") - return nil - } - - // Begin bond reduction - for _, minipool := range selectedMinipools { - response, err := rp.ReduceBondAmount(minipool.Address) - if err != nil { - fmt.Printf("Could not reduce bond for minipool %s: %s.\n", minipool.Address.Hex(), err.Error()) - continue - } - - fmt.Printf("Reducing bond for minipool %s...\n", minipool.Address.Hex()) - cliutils.PrintTransactionHash(rp, response.TxHash) - if _, err = rp.WaitForTransaction(response.TxHash); err != nil { - fmt.Printf("Could not reduce bond for minipool %s: %s.\n", minipool.Address.Hex(), err.Error()) - } else { - fmt.Printf("Successfully reduced bond for minipool %s.\n", minipool.Address.Hex()) - } - } - - // Return - return nil - -} - -func forceFeeDistribution(c *cli.Context, rp *rocketpool.Client) error { - // Get the gas estimate - canDistributeResponse, err := rp.CanDistribute() - if err != nil { - return err - } - - balance := eth.WeiToEth(canDistributeResponse.Balance) - if balance == 0 { - fmt.Println("Your fee distributor does not have any ETH and does not need to be distributed.") - fmt.Println() - return nil - } - fmt.Println("NOTE: prior to bond reduction, you must distribute the funds in your fee distributor.") - fmt.Println() - - // Print info - rEthShare := balance - canDistributeResponse.NodeShare - fmt.Printf("Your fee distributor's balance of %.6f ETH will be distributed as follows:\n", balance) - fmt.Printf("\tYour withdrawal address will receive %.6f ETH.\n", canDistributeResponse.NodeShare) - fmt.Printf("\trETH pool stakers will receive %.6f ETH.\n\n", rEthShare) - - // Assign max fees - err = gas.AssignMaxFeeAndLimit(canDistributeResponse.GasInfo, rp, c.Bool("yes")) - if err != nil { - return err - } - - // Prompt for confirmation - if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to distribute the ETH from your node's fee distributor?")) { - fmt.Println("Cancelled.") - return nil - } - - // Distribute - response, err := rp.Distribute() - if err != nil { - return err - } - - fmt.Printf("Distributing rewards...\n") - cliutils.PrintTransactionHash(rp, response.TxHash) - if _, err = rp.WaitForTransaction(response.TxHash); err != nil { - return err - } - - // Log & return - fmt.Println("Successfully distributed your fee distributor's balance. Your rewards should arrive in your withdrawal address shortly.") - return nil -} diff --git a/rocketpool/api/minipool/commands.go b/rocketpool/api/minipool/commands.go index ea12090f1..de5a173f4 100644 --- a/rocketpool/api/minipool/commands.go +++ b/rocketpool/api/minipool/commands.go @@ -426,100 +426,6 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { }, }, - { - Name: "can-begin-reduce-bond-amount", - Usage: "Check whether the minipool can begin the bond reduction process", - UsageText: "rocketpool api minipool can-begin-reduce-bond-amount minipool-address new-bond-amount-wei", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - newBondAmountWei, err := cliutils.ValidateWeiAmount("new bond amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canBeginReduceBondAmount(c, minipoolAddress, newBondAmountWei)) - return nil - - }, - }, - { - Name: "begin-reduce-bond-amount", - Usage: "Begin the bond reduction process for a minipool", - UsageText: "rocketpool api minipool begin-reduce-bond-amount minipool-address new-bond-amount-wei", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - newBondAmountWei, err := cliutils.ValidateWeiAmount("new bond amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(beginReduceBondAmount(c, minipoolAddress, newBondAmountWei)) - return nil - - }, - }, - - { - Name: "can-reduce-bond-amount", - Usage: "Check if a minipool's bond can be reduced", - UsageText: "rocketpool api minipool can-reduce-bond-amount minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canReduceBondAmount(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "reduce-bond-amount", - Usage: "Reduce a minipool's bond", - UsageText: "rocketpool api minipool reduce-bond-amount minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(reduceBondAmount(c, minipoolAddress)) - return nil - - }, - }, - { Name: "get-distribute-balance-details", Usage: "Get the balance distribution details for all of the node's minipools", @@ -683,22 +589,6 @@ func RegisterSubcommands(command *cli.Command, name string, aliases []string) { }, }, - { - Name: "get-bond-reduction-enabled", - Usage: "Check whether bond reduction is enabled", - UsageText: "rocketpool api minipool get-bond-reduction-enabled", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getBondReductionEnabled(c)) - return nil - }, - }, }, }) } diff --git a/rocketpool/api/minipool/reduce-bond.go b/rocketpool/api/minipool/reduce-bond.go deleted file mode 100644 index b714dba7c..000000000 --- a/rocketpool/api/minipool/reduce-bond.go +++ /dev/null @@ -1,279 +0,0 @@ -package minipool - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/bindings/minipool" - "github.com/rocket-pool/smartnode/bindings/rocketpool" - "github.com/rocket-pool/smartnode/bindings/settings/protocol" - "github.com/rocket-pool/smartnode/shared/services" - "github.com/rocket-pool/smartnode/shared/services/beacon" - "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" - "github.com/urfave/cli" - "golang.org/x/sync/errgroup" -) - -func canBeginReduceBondAmount(c *cli.Context, minipoolAddress common.Address, newBondAmountWei *big.Int) (*api.CanBeginReduceBondAmountResponse, error) { - // Get services - if err := services.RequireNodeRegistered(c); err != nil { - return nil, err - } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - bc, err := services.GetBeaconClient(c) - if err != nil { - return nil, err - } - - // Response - response := api.CanBeginReduceBondAmountResponse{} - - // Data - var wg errgroup.Group - var nodeDepositAmount *big.Int - - // Check if bond reduction is enabled - wg.Go(func() error { - bondReductionEnabled, err := protocol.GetBondReductionEnabled(rp, nil) - if err != nil { - return fmt.Errorf("error checking if bond reduction is enabled: %w", err) - } - response.BondReductionDisabled = !bondReductionEnabled - return nil - }) - - // Check the minipool version - wg.Go(func() error { - version, err := rocketpool.GetContractVersion(rp, minipoolAddress, nil) - if err != nil { - return fmt.Errorf("error getting minipool %s contract version: %w", minipoolAddress.Hex(), err) - } - response.MinipoolVersionTooLow = (version < 3) - return nil - }) - - // Check the balance and status on Beacon - wg.Go(func() error { - var err error - pubkey, err := minipool.GetMinipoolPubkey(rp, minipoolAddress, nil) - if err != nil { - return fmt.Errorf("error retrieving pubkey for minipool %s: %w", minipoolAddress.Hex(), err) - } - status, err := bc.GetValidatorStatus(pubkey, nil) - if err != nil { - return fmt.Errorf("error getting validator status for minipool %s (pubkey %s): %w", minipoolAddress.Hex(), pubkey.Hex(), err) - } - response.Balance = status.Balance - response.BeaconState = status.Status - return nil - }) - - // Get match request info - wg.Go(func() error { - mp, err := minipool.NewMinipool(rp, minipoolAddress, nil) - if err != nil { - return fmt.Errorf("error creating binding for minipool %s: %w", minipoolAddress.Hex(), err) - } - nodeDepositAmount, err = mp.GetNodeDepositBalance(nil) - if err != nil { - return fmt.Errorf("error getting node deposit balance for minipool %s: %w", minipoolAddress.Hex(), err) - } - // How much more ETH they're requesting from the staking pool - response.BorrowRequest = big.NewInt(0).Sub(nodeDepositAmount, newBondAmountWei) - return nil - }) - - // Wait for data - if err := wg.Wait(); err != nil { - return nil, err - } - - // Check the beacon state - response.InvalidBeaconState = !(response.BeaconState == beacon.ValidatorState_PendingInitialized || - response.BeaconState == beacon.ValidatorState_PendingQueued || - response.BeaconState == beacon.ValidatorState_ActiveOngoing) - - // Make sure the balance is high enough - threshold := uint64(32000000000) - response.BalanceTooLow = response.Balance < threshold - - response.CanReduce = !(response.BondReductionDisabled || response.MinipoolVersionTooLow || response.BalanceTooLow || response.InvalidBeaconState) - - // Get gas estimate - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - gasInfo, err := minipool.EstimateBeginReduceBondAmountGas(rp, minipoolAddress, newBondAmountWei, opts) - if err == nil { - response.GasInfo = gasInfo - } - - // Update & return response - return &response, nil -} - -func beginReduceBondAmount(c *cli.Context, minipoolAddress common.Address, newBondAmountWei *big.Int) (*api.BeginReduceBondAmountResponse, error) { - // Get services - if err := services.RequireNodeRegistered(c); err != nil { - return nil, err - } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Response - response := api.BeginReduceBondAmountResponse{} - - // Get gas estimate - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - - // Start bond reduction - hash, err := minipool.BeginReduceBondAmount(rp, minipoolAddress, newBondAmountWei, opts) - if err != nil { - return nil, err - } - response.TxHash = hash - - // Return response - return &response, nil -} - -func canReduceBondAmount(c *cli.Context, minipoolAddress common.Address) (*api.CanReduceBondAmountResponse, error) { - // Get services - if err := services.RequireNodeRegistered(c); err != nil { - return nil, err - } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Response - response := api.CanReduceBondAmountResponse{} - - // Make the minipool binding - mp, err := minipool.NewMinipool(rp, minipoolAddress, nil) - if err != nil { - return nil, fmt.Errorf("error creating minipool binding for %s: %w", minipoolAddress.Hex(), err) - } - response.MinipoolVersion = mp.GetVersion() - mpv3, success := minipool.GetMinipoolAsV3(mp) - if success { - // Get gas estimate - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - gasInfo, err := mpv3.EstimateReduceBondAmountGas(opts) - if err == nil { - response.GasInfo = gasInfo - } - } - - response.CanReduce = success - - // Update & return response - return &response, nil -} - -func reduceBondAmount(c *cli.Context, minipoolAddress common.Address) (*api.ReduceBondAmountResponse, error) { - // Get services - if err := services.RequireNodeRegistered(c); err != nil { - return nil, err - } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Response - response := api.ReduceBondAmountResponse{} - - // Make the minipool binding - mp, err := minipool.NewMinipool(rp, minipoolAddress, nil) - if err != nil { - return nil, fmt.Errorf("error creating minipool binding for %s: %w", minipoolAddress.Hex(), err) - } - mpv3, success := minipool.GetMinipoolAsV3(mp) - if !success { - return nil, fmt.Errorf("bond reduction is not supported for minipool version %d; please upgrade the delegate for minipool %s to reduce its bond", mp.GetVersion(), minipoolAddress.Hex()) - } - - // Get the node transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - - // Start bond reduction - hash, err := mpv3.ReduceBondAmount(opts) - if err != nil { - return nil, err - } - response.TxHash = hash - - // Return response - return &response, nil -} - -func getBondReductionEnabled(c *cli.Context) (*api.GetBondReductionEnabledResponse, error) { - // Get services - if err := services.RequireNodeRegistered(c); err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Response - response := api.GetBondReductionEnabledResponse{} - - // Check if bond reduction is enabled - bondReductionEnabled, err := protocol.GetBondReductionEnabled(rp, nil) - if err != nil { - return nil, fmt.Errorf("error checking if bond reduction is enabled: %w", err) - } - response.BondReductionEnabled = bondReductionEnabled - - // Return response - return &response, nil -} diff --git a/rocketpool/api/minipool/utils.go b/rocketpool/api/minipool/utils.go index 9caae7cfd..5de8e92f2 100644 --- a/rocketpool/api/minipool/utils.go +++ b/rocketpool/api/minipool/utils.go @@ -262,11 +262,6 @@ func getMinipoolDetails(rp *rocketpool.RocketPool, minipoolAddress common.Addres details.Queue, err = minipool.GetQueueDetails(rp, mp.GetAddress(), nil) return err }) - wg.Go(func() error { - var err error - details.ReduceBondTime, err = minipool.GetReduceBondTime(rp, minipoolAddress, nil) - return err - }) // Wait for data if err := wg.Wait(); err != nil { diff --git a/rocketpool/api/node/status.go b/rocketpool/api/node/status.go index 4f1928bc2..623f0ee3e 100644 --- a/rocketpool/api/node/status.go +++ b/rocketpool/api/node/status.go @@ -16,7 +16,6 @@ import ( "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/bindings/settings/protocol" - tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/rocket-pool/smartnode/bindings/tokens" "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/bindings/utils/eth" @@ -497,39 +496,8 @@ func getTrueBorrowAndBondAmounts(rp *rocketpool.RocketPool, bc beacon.Client, no userDeposits := make([]*big.Int, len(mpDetails)) pendingNodeDeposits := make([]*big.Int, len(mpDetails)) pendingUserDeposits := make([]*big.Int, len(mpDetails)) - - latestBlockHeader, err := rp.Client.HeaderByNumber(context.Background(), nil) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting latest block header: %w", err) - } - blockTime := time.Unix(int64(latestBlockHeader.Time), 0) - var reductionWindowStart uint64 - var reductionWindowLength uint64 - - // Data - var wg1 errgroup.Group - - wg1.Go(func() error { - var err error - reductionWindowStart, err = tnsettings.GetBondReductionWindowStart(rp, nil) - return err - }) - wg1.Go(func() error { - var err error - reductionWindowLength, err = tnsettings.GetBondReductionWindowLength(rp, nil) - return err - }) - - // Wait for data - if err = wg1.Wait(); err != nil { - return nil, nil, nil, nil, err - } - - reductionWindowEnd := time.Duration(reductionWindowStart+reductionWindowLength) * time.Second - // Data var wg errgroup.Group - zeroTime := time.Unix(0, 0) for i, mpd := range mpDetails { if !mpd.Exists { @@ -562,36 +530,6 @@ func getTrueBorrowAndBondAmounts(rp *rocketpool.RocketPool, bc beacon.Client, no } userDeposits[i] = userDeposit - reduceBondTime, err := minipool.GetReduceBondTime(rp, address, nil) - if err != nil { - return fmt.Errorf("error getting bond reduction time for minipool %s: %w", address.Hex(), err) - } - - reduceBondCancelled, err := minipool.GetReduceBondCancelled(rp, address, nil) - if err != nil { - return fmt.Errorf("error getting bond reduction cancel status for minipool %s: %w", address.Hex(), err) - } - - // Ignore minipools that don't have a bond reduction pending - timeSinceReductionStart := blockTime.Sub(reduceBondTime) - if reduceBondTime == zeroTime || - reduceBondCancelled || - timeSinceReductionStart > reductionWindowEnd { - pendingNodeDeposits[i] = nodeDeposit - pendingUserDeposits[i] = userDeposit - return nil - } - - // Get the new (pending) bond - newBond, err := minipool.GetReduceBondValue(rp, address, nil) - if err != nil { - return fmt.Errorf("error getting pending bond reduced balance for minipool %s: %w", address.Hex(), err) - } - pendingNodeDeposits[i] = newBond - - // New user deposit = old + delta - pendingUserDeposits[i] = big.NewInt(0).Sub(nodeDeposit, newBond) - pendingUserDeposits[i].Add(pendingUserDeposits[i], userDeposit) return nil }) } diff --git a/rocketpool/node/node.go b/rocketpool/node/node.go index 4ba5541de..11497f44d 100644 --- a/rocketpool/node/node.go +++ b/rocketpool/node/node.go @@ -168,10 +168,6 @@ func run(c *cli.Context) error { if err != nil { return err } - reduceBonds, err := newReduceBonds(c, log.NewColorLogger(ReduceBondAmountColor)) - if err != nil { - return err - } defendPdaoProps, err := newDefendPdaoProps(c, log.NewColorLogger(DefendPdaoPropsColor)) if err != nil { return err @@ -333,12 +329,6 @@ func run(c *cli.Context) error { } time.Sleep(taskCooldown) - // Run the reduce bond check - if err := reduceBonds.run(state); err != nil { - errorLog.Println(err) - } - time.Sleep(taskCooldown) - // Run the set use latest delegate check if err := setUseLatestDelegate.run(state); err != nil { errorLog.Println(err) diff --git a/rocketpool/node/reduce-bonds.go b/rocketpool/node/reduce-bonds.go deleted file mode 100644 index 39260384e..000000000 --- a/rocketpool/node/reduce-bonds.go +++ /dev/null @@ -1,406 +0,0 @@ -package node - -import ( - "context" - "fmt" - "math/big" - "time" - - "github.com/docker/docker/client" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/bindings/minipool" - "github.com/rocket-pool/smartnode/bindings/node" - "github.com/rocket-pool/smartnode/bindings/rocketpool" - "github.com/rocket-pool/smartnode/bindings/types" - "github.com/rocket-pool/smartnode/bindings/utils/eth" - "github.com/urfave/cli" - "golang.org/x/sync/errgroup" - - rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" - "github.com/rocket-pool/smartnode/shared/services" - "github.com/rocket-pool/smartnode/shared/services/alerting" - "github.com/rocket-pool/smartnode/shared/services/config" - rpgas "github.com/rocket-pool/smartnode/shared/services/gas" - "github.com/rocket-pool/smartnode/shared/services/state" - "github.com/rocket-pool/smartnode/shared/services/wallet" - "github.com/rocket-pool/smartnode/shared/utils/api" - "github.com/rocket-pool/smartnode/shared/utils/log" -) - -// Reduce bonds task -type reduceBonds struct { - c *cli.Context - log log.ColorLogger - cfg *config.RocketPoolConfig - w wallet.Wallet - rp *rocketpool.RocketPool - d *client.Client - gasThreshold float64 - maxFee *big.Int - maxPriorityFee *big.Int - gasLimit uint64 -} - -// Create reduce bonds task -func newReduceBonds(c *cli.Context, logger log.ColorLogger) (*reduceBonds, error) { - - // Get services - cfg, err := services.GetConfig(c) - if err != nil { - return nil, err - } - w, err := services.GetHdWallet(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - d, err := services.GetDocker(c) - if err != nil { - return nil, err - } - - gasThreshold := cfg.Smartnode.AutoTxGasThreshold.Value.(float64) - - // Get the user-requested max fee - maxFeeGwei := cfg.Smartnode.ManualMaxFee.Value.(float64) - var maxFee *big.Int - if maxFeeGwei == 0 { - maxFee = nil - } else { - maxFee = eth.GweiToWei(maxFeeGwei) - } - - // Get the user-requested max fee - priorityFeeGwei := cfg.Smartnode.PriorityFee.Value.(float64) - var priorityFee *big.Int - if priorityFeeGwei == 0 { - logger.Printlnf("WARNING: priority fee was missing or 0, setting a default of %.2f.", rpgas.DefaultPriorityFeeGwei) - priorityFee = eth.GweiToWei(rpgas.DefaultPriorityFeeGwei) - } else { - priorityFee = eth.GweiToWei(priorityFeeGwei) - } - - // Return task - return &reduceBonds{ - c: c, - log: logger, - cfg: cfg, - w: w, - rp: rp, - d: d, - gasThreshold: gasThreshold, - maxFee: maxFee, - maxPriorityFee: priorityFee, - gasLimit: 0, - }, nil - -} - -// Reduce bonds -func (t *reduceBonds) run(state *state.NetworkState) error { - // Check if auto-txs were disabled - if t.cfg.Smartnode.AutoTxGasThreshold.Value.(float64) == 0 { - return nil - } - - // Log - t.log.Println("Checking for minipool bonds to reduce...") - - // Get the latest state - opts := &bind.CallOpts{ - BlockNumber: big.NewInt(0).SetUint64(state.ElBlockNumber), - } - - // Get node account - nodeAccount, err := t.w.GetNodeAccount() - if err != nil { - return err - } - - // Get the bond reduction details - windowStart := state.NetworkDetails.BondReductionWindowStart - windowLength := state.NetworkDetails.BondReductionWindowLength - - // Get the time of the latest block - latestEth1Block, err := t.rp.Client.HeaderByNumber(context.Background(), opts.BlockNumber) - if err != nil { - return fmt.Errorf("can't get the latest block time: %w", err) - } - latestBlockTime := time.Unix(int64(latestEth1Block.Time), 0) - - // Get reducible minipools - minipools, err := t.getReduceableMinipools(nodeAccount.Address, windowStart, windowLength, latestBlockTime, state, opts) - if err != nil { - return err - } - if len(minipools) == 0 { - return nil - } - - // Log - t.log.Printlnf("%d minipool(s) are ready for bond reduction...", len(minipools)) - - // Workaround for the fee distribution issue - success, err := t.forceFeeDistribution() - if err != nil { - return err - } - if !success { - return nil - } - - // Reduce bonds - successCount := 0 - for _, mp := range minipools { - success, err := t.reduceBond(mp, windowStart, windowLength, latestBlockTime, opts) - alerting.AlertMinipoolBondReduced(t.cfg, mp.MinipoolAddress, err == nil) - if err != nil { - t.log.Println(fmt.Errorf("could not reduce bond for minipool %s: %w", mp.MinipoolAddress.Hex(), err)) - return err - } - if success { - successCount++ - } - } - - // Return - return nil - -} - -// Temp mitigation for the -func (t *reduceBonds) forceFeeDistribution() (bool, error) { - - // Get node account - nodeAccount, err := t.w.GetNodeAccount() - if err != nil { - return false, err - } - - // Get fee distributor - distributorAddress, err := node.GetDistributorAddress(t.rp, nodeAccount.Address, nil) - if err != nil { - return false, err - } - distributor, err := node.NewDistributor(t.rp, distributorAddress, nil) - if err != nil { - return false, err - } - - // Sync - var wg errgroup.Group - var balanceRaw *big.Int - var nodeShare float64 - - // Get the contract's balance - wg.Go(func() error { - var err error - balanceRaw, err = t.rp.Client.BalanceAt(context.Background(), distributorAddress, nil) - return err - }) - - // Get the node share of the balance - wg.Go(func() error { - nodeShareRaw, err := distributor.GetNodeShare(nil) - if err != nil { - return fmt.Errorf("error getting node share for distributor %s: %w", distributorAddress.Hex(), err) - } - nodeShare = eth.WeiToEth(nodeShareRaw) - return nil - }) - - // Wait for data - if err := wg.Wait(); err != nil { - return false, err - } - - balance := eth.WeiToEth(balanceRaw) - if balance == 0 { - t.log.Println("Your fee distributor does not have any ETH and does not need to be distributed.") - return true, nil - } - t.log.Println("NOTE: prior to bond reduction, you must distribute the funds in your fee distributor.") - - // Print info - rEthShare := balance - nodeShare - t.log.Printlnf("Your fee distributor's balance of %.6f ETH will be distributed as follows:\n", balance) - t.log.Printlnf("\tYour withdrawal address will receive %.6f ETH.", nodeShare) - t.log.Printlnf("\trETH pool stakers will receive %.6f ETH.\n", rEthShare) - - opts, err := t.w.GetNodeAccountTransactor() - if err != nil { - return false, err - } - - // Get the gas limit - gasInfo, err := distributor.EstimateDistributeGas(opts) - if err != nil { - return false, fmt.Errorf("could not estimate the gas required to distribute node fees: %w", err) - } - var gas *big.Int - if t.gasLimit != 0 { - gas = new(big.Int).SetUint64(t.gasLimit) - } else { - gas = new(big.Int).SetUint64(gasInfo.SafeGasLimit) - } - - // Get the max fee - maxFee := t.maxFee - if maxFee == nil || maxFee.Uint64() == 0 { - maxFee, err = rpgas.GetHeadlessMaxFeeWeiWithLatestBlock(t.cfg, t.rp) - if err != nil { - return false, err - } - } - - // Print the gas info - if !api.PrintAndCheckGasInfo(gasInfo, true, t.gasThreshold, &t.log, maxFee, t.gasLimit) { - return false, nil - } - - opts.GasFeeCap = maxFee - opts.GasTipCap = GetPriorityFee(t.maxPriorityFee, maxFee) - opts.GasLimit = gas.Uint64() - - // Distribute - fmt.Printf("Distributing rewards...\n") - hash, err := distributor.Distribute(opts) - if err != nil { - return false, err - } - - // Print TX info and wait for it to be included in a block - err = api.PrintAndWaitForTransaction(t.cfg, hash, t.rp.Client, &t.log) - if err != nil { - return false, err - } - - // Log & return - fmt.Println("Successfully distributed your fee distributor's balance. Your rewards should arrive in your withdrawal address shortly.") - return true, nil -} - -// Get reducible minipools -func (t *reduceBonds) getReduceableMinipools(nodeAddress common.Address, windowStart time.Duration, windowLength time.Duration, latestBlockTime time.Time, state *state.NetworkState, opts *bind.CallOpts) ([]*rpstate.NativeMinipoolDetails, error) { - - // Filter minipools - reduceableMinipools := []*rpstate.NativeMinipoolDetails{} - for _, mpd := range state.MinipoolDetailsByNode[nodeAddress] { - - // TEMP - reduceBondTime, err := minipool.GetReduceBondTime(t.rp, mpd.MinipoolAddress, opts) - if err != nil { - return nil, fmt.Errorf("error getting reduce bond time for minipool %s: %w", mpd.MinipoolAddress.Hex(), err) - } - reduceBondCancelled, err := minipool.GetReduceBondCancelled(t.rp, mpd.MinipoolAddress, opts) - if err != nil { - return nil, fmt.Errorf("error getting reduce bond cancelled for minipool %s: %w", mpd.MinipoolAddress.Hex(), err) - } - - depositBalance := eth.WeiToEth(mpd.NodeDepositBalance) - timeSinceReductionStart := latestBlockTime.Sub(reduceBondTime) - - if depositBalance == 16 && - timeSinceReductionStart < (windowStart+windowLength) && - !reduceBondCancelled && - mpd.Status == types.Staking { - if timeSinceReductionStart > windowStart { - reduceableMinipools = append(reduceableMinipools, mpd) - } else { - remainingTime := windowStart - timeSinceReductionStart - t.log.Printlnf("Minipool %s has %s left until it can have its bond reduced.", mpd.MinipoolAddress.Hex(), remainingTime) - } - } - } - - // Return - return reduceableMinipools, nil - -} - -// Reduce a minipool's bond -func (t *reduceBonds) reduceBond(mpd *rpstate.NativeMinipoolDetails, windowStart time.Duration, windowLength time.Duration, latestBlockTime time.Time, callOpts *bind.CallOpts) (bool, error) { - - // Log - t.log.Printlnf("Reducing bond for minipool %s...", mpd.MinipoolAddress.Hex()) - - // Get transactor - opts, err := t.w.GetNodeAccountTransactor() - if err != nil { - return false, err - } - - // Make the minipool binding - mpBinding, err := minipool.NewMinipoolFromVersion(t.rp, mpd.MinipoolAddress, mpd.Version, callOpts) - if err != nil { - return false, fmt.Errorf("error creating minipool binding for %s: %w", mpd.MinipoolAddress.Hex(), err) - } - - // Get the updated minipool interface - mpv3, success := minipool.GetMinipoolAsV3(mpBinding) - if !success { - return false, fmt.Errorf("cannot reduce bond for minipool %s because its delegate version is too low (v%d); please update the delegate", mpBinding.GetAddress().Hex(), mpBinding.GetVersion()) - } - - // Get the gas limit - gasInfo, err := mpv3.EstimateReduceBondAmountGas(opts) - if err != nil { - return false, fmt.Errorf("could not estimate the gas required to reduce bond: %w", err) - } - var gas *big.Int - if t.gasLimit != 0 { - gas = new(big.Int).SetUint64(t.gasLimit) - } else { - gas = new(big.Int).SetUint64(gasInfo.SafeGasLimit) - } - - // Get the max fee - maxFee := t.maxFee - if maxFee == nil || maxFee.Uint64() == 0 { - maxFee, err = rpgas.GetHeadlessMaxFeeWeiWithLatestBlock(t.cfg, t.rp) - if err != nil { - return false, err - } - } - - // TEMP - reduceBondTime, err := minipool.GetReduceBondTime(t.rp, mpd.MinipoolAddress, callOpts) - if err != nil { - return false, fmt.Errorf("error getting reduce bond time for minipool %s: %w", mpd.MinipoolAddress.Hex(), err) - } - - // Print the gas info - if !api.PrintAndCheckGasInfo(gasInfo, true, t.gasThreshold, &t.log, maxFee, t.gasLimit) { - timeSinceReductionStart := latestBlockTime.Sub(reduceBondTime) - remainingTime := (windowStart + windowLength) - timeSinceReductionStart - t.log.Printlnf("Time until bond reduction times out: %s", remainingTime) - return false, nil - } - - opts.GasFeeCap = maxFee - opts.GasTipCap = GetPriorityFee(t.maxPriorityFee, maxFee) - opts.GasLimit = gas.Uint64() - - // Reduce bond - hash, err := mpv3.ReduceBondAmount(opts) - if err != nil { - return false, err - } - - // Print TX info and wait for it to be included in a block - err = api.PrintAndWaitForTransaction(t.cfg, hash, t.rp.Client, &t.log) - if err != nil { - return false, err - } - - // Log - t.log.Printlnf("Successfully reduced bond for minipool %s.", mpd.MinipoolAddress.Hex()) - - // Return - return true, nil - -} diff --git a/rocketpool/watchtower/cancel-bond-reductions.go b/rocketpool/watchtower/cancel-bond-reductions.go deleted file mode 100644 index b0a0d5d18..000000000 --- a/rocketpool/watchtower/cancel-bond-reductions.go +++ /dev/null @@ -1,276 +0,0 @@ -package watchtower - -import ( - "fmt" - "math/big" - "sync" - "time" - - "github.com/rocket-pool/smartnode/rocketpool/watchtower/collectors" - - "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/bindings/minipool" - "github.com/rocket-pool/smartnode/bindings/rocketpool" - "github.com/rocket-pool/smartnode/bindings/utils/eth" - rpstate "github.com/rocket-pool/smartnode/bindings/utils/state" - "github.com/rocket-pool/smartnode/rocketpool/watchtower/utils" - "github.com/rocket-pool/smartnode/shared/services" - "github.com/rocket-pool/smartnode/shared/services/beacon" - "github.com/rocket-pool/smartnode/shared/services/config" - "github.com/rocket-pool/smartnode/shared/services/state" - "github.com/rocket-pool/smartnode/shared/services/wallet" - "github.com/rocket-pool/smartnode/shared/utils/api" - "github.com/rocket-pool/smartnode/shared/utils/log" - "github.com/urfave/cli" -) - -const ( - scrubBuffer uint64 = 10000000 // 0.01 ETH -) - -type cancelBondReductions struct { - c *cli.Context - log log.ColorLogger - errLog log.ColorLogger - cfg *config.RocketPoolConfig - w wallet.Wallet - rp *rocketpool.RocketPool - ec rocketpool.ExecutionClient - coll *collectors.BondReductionCollector - lock *sync.Mutex - isRunning bool - generationPrefix string -} - -// Create cancel bond reductions task -func newCancelBondReductions(c *cli.Context, logger log.ColorLogger, errorLogger log.ColorLogger, coll *collectors.BondReductionCollector) (*cancelBondReductions, error) { - - // Get services - cfg, err := services.GetConfig(c) - if err != nil { - return nil, err - } - w, err := services.GetHdWallet(c) - if err != nil { - return nil, err - } - ec, err := services.GetEthClient(c) - if err != nil { - return nil, err - } - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Return task - lock := &sync.Mutex{} - return &cancelBondReductions{ - c: c, - log: logger, - errLog: errorLogger, - cfg: cfg, - w: w, - rp: rp, - ec: ec, - coll: coll, - lock: lock, - isRunning: false, - generationPrefix: "[Bond Reduction]", - }, nil - -} - -// Start the bond reduction cancellation thread -func (t *cancelBondReductions) run(state *state.NetworkState) error { - // Wait for eth clients to sync - if err := services.WaitEthClientSynced(t.c, true); err != nil { - return err - } - if err := services.WaitBeaconClientSynced(t.c, true); err != nil { - return err - } - - // Log - t.log.Println("Checking for bond reductions to cancel...") - - // Check if the check is already running - t.lock.Lock() - if t.isRunning { - t.log.Println("Bond reduction cancel check is already running in the background.") - t.lock.Unlock() - return nil - } - t.lock.Unlock() - - // Run the check - go func() { - t.lock.Lock() - t.isRunning = true - t.lock.Unlock() - t.printMessage("Starting bond reduction cancel check in a separate thread.") - - err := t.checkBondReductions(state) - if err != nil { - t.handleError(fmt.Errorf("%s %w", t.generationPrefix, err)) - return - } - - t.lock.Lock() - t.isRunning = false - t.lock.Unlock() - }() - - // Return - return nil - -} - -// Check for bond reductions to cancel -func (t *cancelBondReductions) checkBondReductions(state *state.NetworkState) error { - - t.printMessage(fmt.Sprintf("Checking for Beacon slot %d (EL block %d)", state.BeaconSlotNumber, state.ElBlockNumber)) - - // Check if any of the minipools have bond reduction requests - zero := big.NewInt(0) - reductionMps := []*rpstate.NativeMinipoolDetails{} - for i, mpd := range state.MinipoolDetails { - if mpd.ReduceBondTime.Cmp(zero) == 1 { - reductionMps = append(reductionMps, &state.MinipoolDetails[i]) - } - } - - // If there aren't any, return - if len(reductionMps) == 0 { - t.printMessage("No minipools have requested a bond reduction.") - return nil - } - - // Metrics - balanceTooLowCount := float64(0) - invalidStateCount := float64(0) - - // Check the status of each one - threshold := uint64(32000000000) - scrubBuffer - for _, mpd := range reductionMps { - validator := state.MinipoolValidatorDetails[mpd.Pubkey] - if validator.Exists { - switch validator.Status { - case beacon.ValidatorState_PendingInitialized, - beacon.ValidatorState_PendingQueued: - // Do nothing because this validator isn't live yet - continue - - case beacon.ValidatorState_ActiveOngoing: - // Check the balance - if validator.Balance < threshold { - // Cancel because it's under-balance - t.cancelBondReduction(mpd.MinipoolAddress, fmt.Sprintf("minipool balance is %d (below the threshold)", validator.Balance)) - balanceTooLowCount += 1 - } - - case beacon.ValidatorState_ActiveExiting, - beacon.ValidatorState_ActiveSlashed, - beacon.ValidatorState_ExitedUnslashed, - beacon.ValidatorState_ExitedSlashed, - beacon.ValidatorState_WithdrawalPossible, - beacon.ValidatorState_WithdrawalDone: - t.cancelBondReduction(mpd.MinipoolAddress, "minipool is already slashed, exiting, or exited") - invalidStateCount += 1 - - default: - t.updateMetricsCollector(state, float64(len(reductionMps)), invalidStateCount, balanceTooLowCount) - return fmt.Errorf("unknown validator state: %v", validator.Status) - } - } - } - - t.updateMetricsCollector(state, float64(len(reductionMps)), invalidStateCount, balanceTooLowCount) - - return nil - -} - -// Update the bond reduction metrics collector -func (t *cancelBondReductions) updateMetricsCollector(state *state.NetworkState, minipoolCount float64, invalidStateCount float64, balanceTooLowCount float64) { - if t.coll != nil { - t.coll.UpdateLock.Lock() - defer t.coll.UpdateLock.Unlock() - - // Get the time of the state's EL block - genesisTime := time.Unix(int64(state.BeaconConfig.GenesisTime), 0) - secondsSinceGenesis := time.Duration(state.BeaconSlotNumber*state.BeaconConfig.SecondsPerSlot) * time.Second - stateBlockTime := genesisTime.Add(secondsSinceGenesis) - - t.coll.LatestBlockTime = float64(stateBlockTime.Unix()) - t.coll.TotalMinipools = float64(minipoolCount) - t.coll.InvalidState = invalidStateCount - t.coll.BalanceTooLow = balanceTooLowCount - } -} - -// Cancel a bond reduction -func (t *cancelBondReductions) cancelBondReduction(address common.Address, reason string) { - - // Log - t.printMessage("=== CANCELLING BOND REDUCTION ===") - t.printMessage(fmt.Sprintf("Minipool: %s", address.Hex())) - t.printMessage(fmt.Sprintf("Reason: %s", reason)) - t.printMessage("=================================") - - // Get transactor - opts, err := t.w.GetNodeAccountTransactor() - if err != nil { - t.printMessage(fmt.Sprintf("error getting node account transactor: %s", err.Error())) - return - } - - // Get the gas limit - gasInfo, err := minipool.EstimateVoteCancelReductionGas(t.rp, address, opts) - if err != nil { - t.printMessage(fmt.Sprintf("could not estimate the gas required to voteCancelReduction the minipool: %s", err.Error())) - return - } - - // Print the gas info - maxFee := eth.GweiToWei(utils.GetWatchtowerMaxFee(t.cfg)) - if !api.PrintAndCheckGasInfo(gasInfo, false, 0, &t.log, maxFee, 0) { - return - } - - // Set the gas settings - opts.GasFeeCap = maxFee - opts.GasTipCap = eth.GweiToWei(utils.GetWatchtowerPrioFee(t.cfg)) - opts.GasLimit = gasInfo.SafeGasLimit - - // Cancel the reduction - hash, err := minipool.VoteCancelReduction(t.rp, address, opts) - if err != nil { - t.printMessage(fmt.Sprintf("could not vote to cancel bond reduction: %s", err.Error())) - return - } - - // Print TX info and wait for it to be included in a block - err = api.PrintAndWaitForTransaction(t.cfg, hash, t.rp.Client, &t.log) - if err != nil { - t.printMessage(fmt.Sprintf("error waiting for cancel transaction: %s", err.Error())) - return - } - - // Log - t.log.Printlnf("Successfully voted to cancel the bond reduction of minipool %s.", address.Hex()) - -} - -func (t *cancelBondReductions) handleError(err error) { - t.errLog.Println(err) - t.errLog.Println("*** Bond reduction cancel check failed. ***") - t.lock.Lock() - t.isRunning = false - t.lock.Unlock() -} - -// Print a message from the tree generation goroutine -func (t *cancelBondReductions) printMessage(message string) { - t.log.Printlnf("%s %s", t.generationPrefix, message) -} diff --git a/rocketpool/watchtower/watchtower.go b/rocketpool/watchtower/watchtower.go index 23977559f..0b59b0d6d 100644 --- a/rocketpool/watchtower/watchtower.go +++ b/rocketpool/watchtower/watchtower.go @@ -171,10 +171,6 @@ func run(c *cli.Context) error { if err != nil { return fmt.Errorf("error during manual tree generation check: %w", err) } - cancelBondReductions, err := newCancelBondReductions(c, log.NewColorLogger(CancelBondsColor), errorLog, bondReductionCollector) - if err != nil { - return fmt.Errorf("error during bond reduction cancel check: %w", err) - } checkSoloMigrations, err := newCheckSoloMigrations(c, log.NewColorLogger(CheckSoloMigrationsColor), errorLog, soloMigrationCollector) if err != nil { return fmt.Errorf("error during solo migration check: %w", err) @@ -319,12 +315,6 @@ func run(c *cli.Context) error { } time.Sleep(taskCooldown) - // Run the bond cancel check - if err := cancelBondReductions.run(state); err != nil { - errorLog.Println(err) - } - time.Sleep(taskCooldown) - // Run the solo migration check if err := checkSoloMigrations.run(state); err != nil { errorLog.Println(err) diff --git a/shared/types/api/minipool.go b/shared/types/api/minipool.go index e80b5cdbc..fe4d0a5ff 100644 --- a/shared/types/api/minipool.go +++ b/shared/types/api/minipool.go @@ -43,8 +43,6 @@ type MinipoolDetails struct { TimeUntilDissolve time.Duration `json:"timeUntilDissolve"` DissolveTimeout time.Duration `json:"dissolveTimeout"` Penalties uint64 `json:"penalties"` - ReduceBondTime time.Time `json:"reduceBondTime"` - ReduceBondCancelled bool `json:"reduceBondCancelled"` } type ValidatorDetails struct { Exists bool `json:"exists"` diff --git a/shared/utils/rp/node.go b/shared/utils/rp/node.go index 60a631991..459f725a8 100644 --- a/shared/utils/rp/node.go +++ b/shared/utils/rp/node.go @@ -5,17 +5,14 @@ import ( "context" "fmt" "math/big" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/rocketpool" - tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services/beacon" - "golang.org/x/sync/errgroup" ) func GetNodeValidatorIndices(rp *rocketpool.RocketPool, ec rocketpool.ExecutionClient, bc beacon.Client, nodeAddress common.Address) ([]string, error) { @@ -65,108 +62,10 @@ func GetNodeValidatorIndices(rp *rocketpool.RocketPool, ec rocketpool.ExecutionC // Checks the given node's current borrowed ETH, its limit on borrowed ETH, and how much ETH is preparing to be borrowed by pending bond reductions func CheckCollateral(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (ethBorrowed *big.Int, ethBorrowedLimit *big.Int, pendingBorrowAmount *big.Int, err error) { - // Get the node's minipool addresses - addresses, err := minipool.GetNodeMinipoolAddresses(rp, nodeAddress, opts) + ethBorrowed, err = node.GetNodeETHBorrowed(rp, nodeAddress, opts) if err != nil { - err = fmt.Errorf("error getting minipool addresses for node %s: %w", nodeAddress.Hex(), err) - return + return nil, nil, nil, fmt.Errorf("error getting node's borrowed ETH amount: %w", err) } - latestBlockHeader, err := rp.Client.HeaderByNumber(context.Background(), nil) - if err != nil { - return nil, nil, nil, fmt.Errorf("error getting latest block header: %w", err) - } - blockTime := time.Unix(int64(latestBlockHeader.Time), 0) - var reductionWindowStart uint64 - var reductionWindowLength uint64 - - // Data - var wg1 errgroup.Group - - wg1.Go(func() error { - var err error - reductionWindowStart, err = tnsettings.GetBondReductionWindowStart(rp, nil) - return err - }) - wg1.Go(func() error { - var err error - reductionWindowLength, err = tnsettings.GetBondReductionWindowLength(rp, nil) - return err - }) - - // Wait for data - if err = wg1.Wait(); err != nil { - return nil, nil, nil, err - } - - reductionWindowEnd := time.Duration(reductionWindowStart+reductionWindowLength) * time.Second - - // Data - var wg errgroup.Group - deltas := make([]*big.Int, len(addresses)) - zeroTime := time.Unix(0, 0) - - wg.Go(func() error { - var err error - ethBorrowed, err = node.GetNodeETHBorrowed(rp, nodeAddress, opts) - if err != nil { - return fmt.Errorf("error getting node's borrowed ETH amount: %w", err) - } - return nil - }) - - for i, address := range addresses { - wg.Go(func() error { - reduceBondTime, err := minipool.GetReduceBondTime(rp, address, opts) - if err != nil { - return fmt.Errorf("error getting bond reduction time for minipool %s: %w", address.Hex(), err) - } - - reduceBondCancelled, err := minipool.GetReduceBondCancelled(rp, address, nil) - if err != nil { - return fmt.Errorf("error getting bond reduction cancel status for minipool %s: %w", address.Hex(), err) - } - - // Ignore minipools that don't have a bond reduction pending - timeSinceReductionStart := blockTime.Sub(reduceBondTime) - if reduceBondTime == zeroTime || - reduceBondCancelled || - timeSinceReductionStart > reductionWindowEnd { - deltas[i] = big.NewInt(0) - return nil - } - - // Get the old and new (pending) bonds - mp, err := minipool.NewMinipool(rp, address, opts) - if err != nil { - return fmt.Errorf("error creating binding for minipool %s: %w", address.Hex(), err) - } - oldBond, err := mp.GetNodeDepositBalance(opts) - if err != nil { - return fmt.Errorf("error getting node deposit balance for minipool %s: %w", address.Hex(), err) - } - newBond, err := minipool.GetReduceBondValue(rp, address, opts) - if err != nil { - return fmt.Errorf("error getting pending bond reduced balance for minipool %s: %w", address.Hex(), err) - } - - // Delta = old - new - deltas[i] = big.NewInt(0).Sub(oldBond, newBond) - return nil - }) - } - - // Wait for data - if err = wg.Wait(); err != nil { - return - } - - // Get the total pending borrow amount - totalDelta := big.NewInt(0) - for _, delta := range deltas { - totalDelta.Add(totalDelta, delta) - } - pendingBorrowAmount = totalDelta - return }