Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2f517d2
Add IsSaturn2Deployed check
0xfornax Jun 18, 2026
1f5f8f7
Add pdao parameters
0xfornax Jun 22, 2026
7b1e60d
Add verify-performance commands
0xfornax Jun 26, 2026
5cbb995
Accept multiple validators
0xfornax Jun 26, 2026
b3b40e5
Add saturn2deployed to the state
0xfornax Jul 1, 2026
7595859
Add defendChallengePerformance task
0xfornax Jul 1, 2026
c4a4115
Add tests
0xfornax Jul 1, 2026
7d37ba9
Add the proofBuffer param
0xfornax Jul 6, 2026
73fd202
Add challenge functions
0xfornax Jul 7, 2026
7b4c8db
Add saturn2 check and default values
0xfornax Jul 8, 2026
d4f0ded
Add EncodeParticipationBitset
0xfornax Jul 8, 2026
47fe8d0
Add estimate functions
0xfornax Jul 8, 2026
42878e3
Add IsRangeChallengeable
0xfornax Jul 8, 2026
17e5e99
Create challenge groups and call challenge
0xfornax Jul 8, 2026
b1b50e4
First pass implementing defendChallenge
0xfornax Jul 9, 2026
f9d5fd4
Call finaliseChallenge on old enough challenges
0xfornax Jul 9, 2026
6ee9d5c
Calculate participationBitmapWitness
0xfornax Jul 10, 2026
9817ca6
participationFlags from uint8 to bytes32 and historical participation…
0xfornax Jul 18, 2026
2d32d4c
Adapt to utils refactor
0xfornax Aug 6, 2026
c3fd6e7
Add PreviousEpochParticipationChunkProof for Gloas
0xfornax Aug 6, 2026
6d7ca8f
Add minipool contract v4
0xfornax Jul 9, 2026
7f82bd8
Add CheckMinipoolExitRequests task
0xfornax Jul 9, 2026
a9de921
Exit own validator when requested
0xfornax Jul 9, 2026
e7dbc0f
Add PenaliseMinipool
0xfornax Jul 15, 2026
a1ab7d4
Fetch minipool exit requests from the events
0xfornax Jul 15, 2026
f07e9e6
Add CheckMegapoolExitRequests
0xfornax Jul 16, 2026
062024e
Adapt to utils refactor
0xfornax Aug 7, 2026
e90e932
Review pdao params
0xfornax Aug 7, 2026
0d0a856
Add to-json to new settings
0xfornax Aug 18, 2026
2bb4267
check if saturn2 is deployed on tasks
0xfornax Aug 18, 2026
a76a8e0
Don't use challenge params pre-saturn2
0xfornax Aug 19, 2026
a51868b
Avoid changing the flow used to calculate rewards
0xfornax Aug 19, 2026
338757f
Adapt new node tasks to NetworkStateIndex
0xfornax Sep 14, 2026
a3c890c
Fix order of version checks
0xfornax Sep 14, 2026
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
29 changes: 29 additions & 0 deletions bindings/megapool/megapool-constructor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package megapool

import (
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"

"github.com/rocket-pool/smartnode/bindings/rocketpool"
)

// Create a megapool binding for the contract version deployed at the given address
func NewMegapool(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (Megapool, error) {

// Get the contract version
version, err := rocketpool.GetContractVersion(rp, address, opts)
if err != nil {
return nil, fmt.Errorf("error getting megapool contract version: %w", err)
}

switch version {
case 1:
return NewMegaPoolV1(rp, address, opts)
case 2:
return NewMegaPoolV2(rp, address, opts)
default:
return nil, fmt.Errorf("unexpected megapool contract version [%d]", version)
}
}
73 changes: 73 additions & 0 deletions bindings/megapool/megapool-contract-v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package megapool

import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"

"github.com/rocket-pool/smartnode/bindings/rocketpool"
"github.com/rocket-pool/smartnode/bindings/transactions/gaslimit"
)

type MegapoolV2 interface {
Megapool
EstimateForceExitGas(validatorIds []uint32, feeLimit *big.Int, opts *bind.TransactOpts) (gaslimit.Limits, error)
ForceExit(validatorIds []uint32, feeLimit *big.Int, opts *bind.TransactOpts) (common.Hash, error)
}

// Megapool contract from delegate version 2, which adds support for EL-triggered forced exits
type megapoolV2 struct {
megapoolV1
}

const (
megapoolV2EncodedAbi string = "eJztWktv2zgQ/isLn4M9tLs99OYmbhHATVMn3j0EgUFLY5sITaokZcco9r/vUC/rZYuyJEOtc0pkksN58ZsZDp9+DggXfLcWvhp8XBCm4GpAuedr/Hz6if+68ApuakiD5IQ97jwYfBz4+P3u7w+DqwEna/MDQUJc47fOTvjvqj4tTfFPkdJzMuErLIknBPskuDsB13eQeDIfNoCMmH1/fQFvYK5vuSOBqN9ZxN/ahveAVPRu6HmMNpFRS9+CrQ1h1CVayFv3zHL+E+88VIou+UWIegM/fPAvQ1SqlGCbi5B1xDsz6/t3FpLipDMJ+kr1ZVgUBaV8eQGSjoXzchEmfdDkMgSdctbQpn1MjyawJdJV14zgzO5l48KFYYvybQSOt0lQgl61Sc+TQgsH82si2iRra1dMF7Skc/9AeMlY8viOs4Nuud+UYHK9ixJtnCh8HZHHKUoTDV99TeaUURw2vsA9siNzlpJk4XNHU8EtuEuH8NnRGJ5iMMiJP/vcVdX8WTK3p+4Q5vgMyaAKXAxzkQ0yO1Ur2hyR/cpSX7E4E40oJE47/NaIjjlMhwmUKn1DYXuSO9R21sRab2Y6o5lOObXOijAGfAkmfWwTV1JbmPjXDWkXPAkOUuolIrph6dxP3qJaN0nBOjJQEiO7ob8EPcQJKTmuI3CqwJu0zkr1dNq5zHIW3RIhbzkvqMaNNsEiw5S5jOwLL1HJ2kfbjV49KomZ9Bimg71Q2JgonaSdfeMtqOD6aMs72CZshf2UHz6VsAYbBs+jvDtTu7muBFWRK5FkUoGleKhFloy2+qSi78GdZZ+4alSOdMbVBHBSv+LOVIG8Jh7VhPWJpdCjesZYT0C0tWQzLdItX4icRI5Ye4IjGlttyjAGhpmVAfDHwmVN0lmopGLiACi9T9CaUYlgqTYRLGKEotqaibkQbL9a5e6Ig9HKVZDrititovx7VNDUXHaPgcnwWZ9Nz4S0qarPq1ts5tmrJtNHsVuWv8A+tMrY9cNf2e0+EUa4kzM+TrJYHm5bPAVm9XPhrhMzR9/Rf0zMIh3fwj/gqSRLGJMdHso/8we1ACva97BC6yGqDLl7789fIHs9+gYvb/DyBi99hxcafmUhpkQnOw2p2seLj7tOD/cMmkox6bhc7UmUYelfqleuJFvCriW4iG6UsIqSJdi/PL2MhzrtNcwR9I739o6cmZmvYBQe8UdqXNLi0GUNsbf3fbmr1aDwgCGFaF+CLZGM40UofkM0mQihy80Qt09S9x2D/lw+V6PNbBt7KHIz8oSzOgWzZhIcdO4D61NqEpoudm33PbpXUth/u+VftkBt9FO4Mpo5pucj92uTm6M6JmJnMtBnipT2IewXMpRiIkggkcu1Vy3ondBnccba3VwZvF0u3Lq11sSRgEsKfYkm7wZaC+RxWtmB1BuQygyf/c6pyaOjApYI5t4Aw1RPwyE8qUcQg1cjgvWf88TbTb2lJG7r7+0KEi6kWLcj2llfs430yrypcIA2eiFdzk6h6NTlxVzLMmHlN8atlJ56CAblYtmAEMbTOXFeDj9TyHhYN3gS9HaTg9OLFlLMT9BMhYqeTdYF8tZvqau7WEDwXKBvmkoc0Y6xjnRlkTryTKOyRBlJQU9NeUlVtzlbruArBY5UNK9S9GnsWSCEDHHTKkt5ei7PU3IPyIKJNk/QZguAMV3T40neQkinlUdgz/8DcNqKAw=="
)

// The decoded ABI for v2 megapools
var megapoolV2Abi *abi.ABI

// Create new megapool contract
func NewMegaPoolV2(rp *rocketpool.RocketPool, address common.Address, opts *bind.CallOpts) (MegapoolV2, error) {

var contract *rocketpool.Contract
var err error
if megapoolV2Abi == nil {
// Get contract
contract, err = createMegapoolContractFromEncodedAbi(rp, address, megapoolV2EncodedAbi)
} else {
contract, err = createMegapoolContractFromAbi(rp, address, megapoolV2Abi)
}
if err != nil {
return nil, err
} else if megapoolV2Abi == nil {
megapoolV2Abi = contract.ABI
}

// Create and return
return &megapoolV2{
megapoolV1: megapoolV1{
Address: address,
Version: 2,
Contract: contract,
RocketPool: rp,
},
}, nil
}

// Estimate the gas of ForceExit
func (mp *megapoolV2) EstimateForceExitGas(validatorIds []uint32, feeLimit *big.Int, opts *bind.TransactOpts) (gaslimit.Limits, error) {
return mp.Contract.GetTransactionGasInfo(opts, "forceExit", validatorIds, feeLimit)
}

// Force exit megapool validators that failed to exit within the cooperative exit phase
func (mp *megapoolV2) ForceExit(validatorIds []uint32, feeLimit *big.Int, opts *bind.TransactOpts) (common.Hash, error) {
tx, err := mp.Contract.Transact(opts, "forceExit", validatorIds, feeLimit)
if err != nil {
return common.Hash{}, fmt.Errorf("error force exiting megapool %s validators: %w", mp.Address.Hex(), err)
}
return tx.Hash(), nil
}
8 changes: 8 additions & 0 deletions bindings/megapool/megapool-contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ import (
rptypes "github.com/rocket-pool/smartnode/bindings/types"
)

type ParticipationProof struct {
ParticipationSlot uint64
ValidatorIndex *big.Int
// ParticipationFlagsChunk is the 32-byte merkle chunk of previous_epoch_participation
ParticipationFlagsChunk [32]byte
Witnesses [][32]byte
}

type SlotProof struct {
Slot uint64 `json:"slot"`
Witnesses [][32]byte `json:"witnesses"`
Expand Down
110 changes: 110 additions & 0 deletions bindings/megapool/performance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package megapool

import (
"fmt"
"math/big"
"sync"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/rocket-pool/smartnode/bindings/rocketpool"
"github.com/rocket-pool/smartnode/bindings/transactions/gaslimit"
)

// Estimate the gas to call ChallengeMegapool
func EstimateChallengeMegapoolGas(rp *rocketpool.RocketPool, megapoolAddress common.Address, validatorId uint32, startEpoch uint64, participation []*big.Int, slotTimestamp uint64, slotProof SlotProof, opts *bind.TransactOpts) (gaslimit.Limits, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return gaslimit.Limits{}, err
}
return rocketNetworkParticipation.GetTransactionGasInfo(opts, "challengeMegapool", megapoolAddress, validatorId, startEpoch, participation, slotTimestamp, slotProof)
}

// Challenge the megapool
func ChallengeMegapool(rp *rocketpool.RocketPool, megapoolAddress common.Address, validatorId uint32, startEpoch uint64, participation []*big.Int, slotTimestamp uint64, slotProof SlotProof, opts *bind.TransactOpts) (common.Hash, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return common.Hash{}, err
}
tx, err := rocketNetworkParticipation.Transact(opts, "challengeMegapool", megapoolAddress, validatorId, startEpoch, participation, slotTimestamp, slotProof)
if err != nil {
return common.Hash{}, fmt.Errorf("error challenging megapool: %w", err)
}
return tx.Hash(), nil
}

// Estimate the gas to call RespondWithParticipation
func EstimateRespondWithParticipationGas(rp *rocketpool.RocketPool, challengeId uint64, offset uint64, challengeLeaf *big.Int, challengeWitness []common.Hash, slotTimestamp uint64, validatorProof ValidatorProof, participationProof ParticipationProof, slotProof SlotProof, opts *bind.TransactOpts) (gaslimit.Limits, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return gaslimit.Limits{}, err
}
return rocketNetworkParticipation.GetTransactionGasInfo(opts, "respondWithParticipation", challengeId, offset, challengeLeaf, challengeWitness, slotTimestamp, validatorProof, participationProof, slotProof)
}

// Respond to a performance challenge with a proof that the validator did
// participate in one of the challenged epochs
func RespondWithParticipation(rp *rocketpool.RocketPool, challengeId uint64, offset uint64, challengeLeaf *big.Int, challengeWitness []common.Hash, slotTimestamp uint64, validatorProof ValidatorProof, participationProof ParticipationProof, slotProof SlotProof, opts *bind.TransactOpts) (common.Hash, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return common.Hash{}, err
}
tx, err := rocketNetworkParticipation.Transact(opts, "respondWithParticipation", challengeId, offset, challengeLeaf, challengeWitness, slotTimestamp, validatorProof, participationProof, slotProof)
if err != nil {
return common.Hash{}, fmt.Errorf("error responding to challenge: %w", err)
}
return tx.Hash(), nil
}

// Estimate the gas to call RespondWithValidator
func EstimateRespondWithValidatorGas(rp *rocketpool.RocketPool, challengeId uint64, slotTimestamp uint64, validatorProof ValidatorProof, slotProof SlotProof, opts *bind.TransactOpts) (gaslimit.Limits, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return gaslimit.Limits{}, err
}
return rocketNetworkParticipation.GetTransactionGasInfo(opts, "respondWithValidator", challengeId, slotTimestamp, validatorProof, slotProof)
}

// Respond to a performance challenge with a proof that the validator was not
// staking for the entire challenge period
func RespondWithValidator(rp *rocketpool.RocketPool, challengeId uint64, slotTimestamp uint64, validatorProof ValidatorProof, slotProof SlotProof, opts *bind.TransactOpts) (common.Hash, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return common.Hash{}, err
}
tx, err := rocketNetworkParticipation.Transact(opts, "respondWithValidator", challengeId, slotTimestamp, validatorProof, slotProof)
if err != nil {
return common.Hash{}, fmt.Errorf("error responding to challenge: %w", err)
}
return tx.Hash(), nil
}

// Estimate the gas to call FinaliseChallenge
func EstimateFinaliseChallengeGas(rp *rocketpool.RocketPool, challengeId uint64, opts *bind.TransactOpts) (gaslimit.Limits, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return gaslimit.Limits{}, err
}
return rocketNetworkParticipation.GetTransactionGasInfo(opts, "finaliseChallenge", challengeId)
}

func FinaliseChallenge(rp *rocketpool.RocketPool, challengeId uint64, opts *bind.TransactOpts) (common.Hash, error) {
rocketNetworkParticipation, err := getRocketNetworkParticipation(rp, nil)
if err != nil {
return common.Hash{}, err
}
tx, err := rocketNetworkParticipation.Transact(opts, "finaliseChallenge", challengeId)
if err != nil {
return common.Hash{}, fmt.Errorf("error finalising challenge: %w", err)
}
return tx.Hash(), nil
}

// Get contracts
var rocketNetworkParticipationLock sync.Mutex

func getRocketNetworkParticipation(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) {
rocketNetworkParticipationLock.Lock()
defer rocketNetworkParticipationLock.Unlock()
return rp.GetContract("rocketNetworkParticipation", opts)
}
4 changes: 4 additions & 0 deletions bindings/minipool/minipool-constructor.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func NewMinipool(rp *rocketpool.RocketPool, address common.Address, opts *bind.C
return newMinipool_v2(rp, address)
case 3:
return newMinipool_v3(rp, address, opts)
case 4:
return newMinipool_v4(rp, address, opts)
default:
return nil, fmt.Errorf("unexpected minipool contract version [%d]", version)
}
Expand All @@ -45,6 +47,8 @@ func NewMinipoolFromVersion(rp *rocketpool.RocketPool, address common.Address, v
return newMinipool_v2(rp, address)
case 3:
return newMinipool_v3(rp, address, opts)
case 4:
return newMinipool_v4(rp, address, opts)
default:
return nil, fmt.Errorf("unexpected minipool contract version [%d]", version)
}
Expand Down
Loading
Loading