From 5d82c7212f8deabfb2ecdc30df73612aeb9f7761 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Sun, 30 Aug 2026 17:06:20 -0400 Subject: [PATCH 1/3] Update json tags on queue capacity struct --- bindings/minipool/queue.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/minipool/queue.go b/bindings/minipool/queue.go index e002bf7d9..7735cc0a3 100644 --- a/bindings/minipool/queue.go +++ b/bindings/minipool/queue.go @@ -14,8 +14,8 @@ import ( // Minipool queue capacity type QueueCapacity struct { - Total *big.Int - Effective *big.Int + Total *big.Int `json:"total"` + Effective *big.Int `json:"effective"` } // Minipools queue status details From e0adb85653e1afda8c4aa7e00077f3ef2d4e1601 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Sun, 30 Aug 2026 17:11:18 -0400 Subject: [PATCH 2/3] Rename megapool validator field on NetworkState --- bindings/megapool/megapool-contract.go | 2 +- bindings/megapool/megapool-manager.go | 12 +++---- bindings/utils/state/megapool.go | 20 ++++++------ rocketpool/node/collectors/node-collector.go | 2 +- rocketpool/watchtower/challenge-exit.go | 2 +- .../dissolve-invalid-credentials.go | 4 +-- .../dissolve-timed-out-megapool-validators.go | 4 +-- .../submit-network-balances-state_test.go | 2 +- shared/services/rewards/mock_v11_test.go | 2 +- shared/services/rewards/test/beacon.go | 2 +- shared/services/rewards/test/mock.go | 10 +++--- shared/services/rewards/types.go | 2 +- shared/services/state/cli/cli.go | 4 +-- shared/services/state/network-state.go | 32 +++++++++---------- shared/services/state/network-state_test.go | 12 +++---- shared/services/state/static_provider_test.go | 10 +++--- 16 files changed, 61 insertions(+), 61 deletions(-) diff --git a/bindings/megapool/megapool-contract.go b/bindings/megapool/megapool-contract.go index add563694..2e4a783d0 100644 --- a/bindings/megapool/megapool-contract.go +++ b/bindings/megapool/megapool-contract.go @@ -87,7 +87,7 @@ type ValidatorInfo struct { LockedTime uint64 `abi:"lockedTime"` } -type ValidatorInfoFromGlobalIndex struct { +type MegapoolValidatorInfo struct { Pubkey []byte `abi:"pubkey"` ValidatorInfo ValidatorInfo `abi:"validatorInfo"` MegapoolAddress common.Address `abi:"megapoolAddress"` diff --git a/bindings/megapool/megapool-manager.go b/bindings/megapool/megapool-manager.go index 1e17f33b0..c0a28d5a4 100644 --- a/bindings/megapool/megapool-manager.go +++ b/bindings/megapool/megapool-manager.go @@ -40,31 +40,31 @@ func GetValidatorCount(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint32, return uint32((*validatorCount).Uint64()), nil } -func GetValidatorInfo(rp *rocketpool.RocketPool, index uint32, opts *bind.CallOpts) (ValidatorInfoFromGlobalIndex, error) { +func GetValidatorInfo(rp *rocketpool.RocketPool, index uint32, opts *bind.CallOpts) (MegapoolValidatorInfo, error) { megapoolManager, err := getRocketMegapoolManager(rp, opts) if err != nil { - return ValidatorInfoFromGlobalIndex{}, err + return MegapoolValidatorInfo{}, err } - validator := new(ValidatorInfoFromGlobalIndex) + validator := new(MegapoolValidatorInfo) indexBig := new(big.Int).SetUint64(uint64(index)) callData, err := megapoolManager.ABI.Pack("getValidatorInfo", indexBig) if err != nil { - return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error creating calldata for getValidatorInfo: %w", err) + return MegapoolValidatorInfo{}, fmt.Errorf("error creating calldata for getValidatorInfo: %w", err) } response, err := megapoolManager.Client.CallContract(context.Background(), ethereum.CallMsg{To: megapoolManager.Address, Data: callData}, opts.BlockNumber) if err != nil { - return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error calling getValidatorInfo: %w", err) + return MegapoolValidatorInfo{}, fmt.Errorf("error calling getValidatorInfo: %w", err) } // Both Call and UnpackIntoStruct were not working with this response (which contains a struct inside a struct) // For the moment this was the only way for it to work. We should investigate further. iface, err := megapoolManager.ABI.Unpack("getValidatorInfo", response) if err != nil { - return ValidatorInfoFromGlobalIndex{}, fmt.Errorf("error unpacking getValidatorInfo response: %w", err) + return MegapoolValidatorInfo{}, fmt.Errorf("error unpacking getValidatorInfo response: %w", err) } src := iface[1].(struct { diff --git a/bindings/utils/state/megapool.go b/bindings/utils/state/megapool.go index 84f378537..fb7936c98 100644 --- a/bindings/utils/state/megapool.go +++ b/bindings/utils/state/megapool.go @@ -52,7 +52,7 @@ func (m *NativeMegapoolDetails) GetMegapoolBondNormalized() *big.Int { } // Get all megapool validators using batched multicalls -func GetAllMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]megapool.ValidatorInfoFromGlobalIndex, error) { +func GetAllMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContracts) ([]megapool.MegapoolValidatorInfo, error) { opts := &bind.CallOpts{ BlockNumber: contracts.ElBlockNumber, } @@ -74,7 +74,7 @@ func GetAllMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContr } count := int(megapoolValidatorsCount) - validators := make([]megapool.ValidatorInfoFromGlobalIndex, count) + validators := make([]megapool.MegapoolValidatorInfo, count) var wg errgroup.Group wg.SetLimit(threadLimit) @@ -118,10 +118,10 @@ func GetAllMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContr } // Manually unpack a getValidatorInfo response (nested structs don't work with UnpackIntoInterface) -func unpackValidatorInfoFromGlobalIndex(contract *rocketpool.Contract, data []byte) (megapool.ValidatorInfoFromGlobalIndex, error) { +func unpackValidatorInfoFromGlobalIndex(contract *rocketpool.Contract, data []byte) (megapool.MegapoolValidatorInfo, error) { iface, err := contract.ABI.Unpack("getValidatorInfo", data) if err != nil { - return megapool.ValidatorInfoFromGlobalIndex{}, err + return megapool.MegapoolValidatorInfo{}, err } src := iface[1].(struct { @@ -143,7 +143,7 @@ func unpackValidatorInfoFromGlobalIndex(contract *rocketpool.Contract, data []by LockedTime uint64 `json:"lockedTime"` }) - var validator megapool.ValidatorInfoFromGlobalIndex + var validator megapool.MegapoolValidatorInfo validator.Pubkey = iface[0].([]byte) validator.ValidatorInfo.LastAssignmentTime = src.LastAssignmentTime validator.ValidatorInfo.LastRequestedValue = src.LastRequestedValue @@ -167,7 +167,7 @@ func unpackValidatorInfoFromGlobalIndex(contract *rocketpool.Contract, data []by // Get all validators for a single megapool, via its own local index -- unlike // GetAllMegapoolValidators, which walks the network-wide global index on RocketMegapoolManager. -func GetNodeMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContracts, megapoolAddress common.Address) ([]megapool.ValidatorInfoFromGlobalIndex, error) { +func GetNodeMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkContracts, megapoolAddress common.Address) ([]megapool.MegapoolValidatorInfo, error) { opts := &bind.CallOpts{ BlockNumber: contracts.ElBlockNumber, } @@ -187,7 +187,7 @@ func GetNodeMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkCont } count := int(validatorCount) - validators := make([]megapool.ValidatorInfoFromGlobalIndex, count) + validators := make([]megapool.MegapoolValidatorInfo, count) if count == 0 { return validators, nil } @@ -241,10 +241,10 @@ func GetNodeMegapoolValidators(rp *rocketpool.RocketPool, contracts *NetworkCont } // Manually unpack a getValidatorInfoAndPubkey response (nested structs don't work with UnpackIntoInterface) -func unpackValidatorInfoAndPubkey(contract *rocketpool.Contract, data []byte) (megapool.ValidatorInfoFromGlobalIndex, error) { +func unpackValidatorInfoAndPubkey(contract *rocketpool.Contract, data []byte) (megapool.MegapoolValidatorInfo, error) { iface, err := contract.ABI.Unpack("getValidatorInfoAndPubkey", data) if err != nil { - return megapool.ValidatorInfoFromGlobalIndex{}, err + return megapool.MegapoolValidatorInfo{}, err } src := iface[0].(struct { @@ -266,7 +266,7 @@ func unpackValidatorInfoAndPubkey(contract *rocketpool.Contract, data []byte) (m LockedTime uint64 `json:"lockedTime"` }) - var validator megapool.ValidatorInfoFromGlobalIndex + var validator megapool.MegapoolValidatorInfo validator.Pubkey = iface[1].([]byte) validator.ValidatorInfo.LastAssignmentTime = src.LastAssignmentTime validator.ValidatorInfo.LastRequestedValue = src.LastRequestedValue diff --git a/rocketpool/node/collectors/node-collector.go b/rocketpool/node/collectors/node-collector.go index 314348508..15d849f6f 100644 --- a/rocketpool/node/collectors/node-collector.go +++ b/rocketpool/node/collectors/node-collector.go @@ -634,7 +634,7 @@ func (collector *NodeCollector) Collect(channel chan<- prometheus.Metric) { // state.MegapoolValidatorGlobalIndex is scoped to this node's own megapool, since the // daemon builds its state via GetHeadStateForNode wg.Go(func() error { - for _, validator := range state.MegapoolValidatorGlobalIndex { + for _, validator := range state.MegapoolValidators { if validator.ValidatorInfo.Staked { megapoolStakedCount++ } diff --git a/rocketpool/watchtower/challenge-exit.go b/rocketpool/watchtower/challenge-exit.go index a2f881c31..f050a31b6 100644 --- a/rocketpool/watchtower/challenge-exit.go +++ b/rocketpool/watchtower/challenge-exit.go @@ -99,7 +99,7 @@ func (t *challengeValidatorsExiting) challengeValidatorsExiting(state *state.Net challengeMegapoolAddressToIds := make(map[common.Address][]uint32) batched := 0 - for _, validator := range state.MegapoolValidatorGlobalIndex { + for _, validator := range state.MegapoolValidators { if batched >= batchSize { t.log.Printlnf("Batched %d validators, exiting...", batched) break diff --git a/rocketpool/watchtower/dissolve-invalid-credentials.go b/rocketpool/watchtower/dissolve-invalid-credentials.go index f085dad0a..7f57cf0bf 100644 --- a/rocketpool/watchtower/dissolve-invalid-credentials.go +++ b/rocketpool/watchtower/dissolve-invalid-credentials.go @@ -91,7 +91,7 @@ func (t *dissolveInvalidCredentials) run(state *state.NetworkStateIndex) error { // Get megapool validators that can be dissolved due to using invalid credentials func (t *dissolveInvalidCredentials) dissolveInvalidCredentialValidators(state *state.NetworkStateIndex) error { - for _, validator := range state.MegapoolValidatorGlobalIndex { + for _, validator := range state.MegapoolValidators { if validator.ValidatorInfo.InPrestake { expectedWithdrawalAddress := services.CalculateMegapoolWithdrawalCredentials(validator.MegapoolAddress) // Fetch the validator from the beacon state to compare credentials @@ -144,7 +144,7 @@ func (t *dissolveInvalidCredentials) dissolveInvalidCredentialValidators(state * return nil } -func (t *dissolveInvalidCredentials) dissolveMegapoolValidator(validator megapool.ValidatorInfoFromGlobalIndex) { +func (t *dissolveInvalidCredentials) dissolveMegapoolValidator(validator megapool.MegapoolValidatorInfo) { // Log t.log.Printlnf("Dissolving megapool validator ID: %d from megapool %s...", validator.ValidatorId, validator.MegapoolAddress) diff --git a/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go b/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go index 85189dcd8..65aeac6eb 100644 --- a/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go +++ b/rocketpool/watchtower/dissolve-timed-out-megapool-validators.go @@ -87,7 +87,7 @@ func (t *dissolveTimedOutMegapoolValidators) dissolveMegapoolValidators(state *s return err } - for _, validator := range state.MegapoolValidatorGlobalIndex { + for _, validator := range state.MegapoolValidators { if validator.ValidatorInfo.InPrestake { assignTime := time.Unix(int64(validator.ValidatorInfo.LastAssignmentTime), 0) if time.Since(assignTime) >= time.Duration(timeBeforeDissolve)*time.Second { @@ -104,7 +104,7 @@ func (t *dissolveTimedOutMegapoolValidators) dissolveMegapoolValidators(state *s return nil } -func (t *dissolveTimedOutMegapoolValidators) dissolveMegapoolValidator(validator megapool.ValidatorInfoFromGlobalIndex) error { +func (t *dissolveTimedOutMegapoolValidators) dissolveMegapoolValidator(validator megapool.MegapoolValidatorInfo) error { // Log t.log.Printlnf("Dissolving megapool validator ID: %d from megapool %s...", validator.ValidatorId, validator.MegapoolAddress) diff --git a/rocketpool/watchtower/submit-network-balances-state_test.go b/rocketpool/watchtower/submit-network-balances-state_test.go index d42438bc1..88a933713 100644 --- a/rocketpool/watchtower/submit-network-balances-state_test.go +++ b/rocketpool/watchtower/submit-network-balances-state_test.go @@ -233,7 +233,7 @@ func TestMegapoolBalanceWithDuplicatePubkey(t *testing.T) { MegapoolValidatorDetails: state.ValidatorDetailsMap{ pubkey: {Pubkey: pubkey, Index: "4", Exists: true, Balance: 32000000000, ActivationEpoch: 0, ExitEpoch: ^uint64(0)}, }, - MegapoolValidatorGlobalIndex: []megapool.ValidatorInfoFromGlobalIndex{ + MegapoolValidators: []megapool.MegapoolValidatorInfo{ { Pubkey: pubkey[:], MegapoolAddress: megapoolAddrA, diff --git a/shared/services/rewards/mock_v11_test.go b/shared/services/rewards/mock_v11_test.go index 72b12f5dc..5bf2f6628 100644 --- a/shared/services/rewards/mock_v11_test.go +++ b/shared/services/rewards/mock_v11_test.go @@ -54,7 +54,7 @@ func TestMockIntervalDefaultsTreegenv11(tt *testing.T) { for _, validator := range state.MinipoolValidatorDetails { t.bc.SetMinipoolPerformance(validator.Index, make([]uint64, 0)) } - for _, validator := range state.MegapoolValidatorGlobalIndex { + for _, validator := range state.MegapoolValidators { pubkey := rptypes.BytesToValidatorPubkey(validator.Pubkey) details := state.MegapoolValidatorDetails[pubkey] t.bc.SetMinipoolPerformance(details.Index, make([]uint64, 0)) diff --git a/shared/services/rewards/test/beacon.go b/shared/services/rewards/test/beacon.go index cae629b13..c95343234 100644 --- a/shared/services/rewards/test/beacon.go +++ b/shared/services/rewards/test/beacon.go @@ -119,7 +119,7 @@ func (bc *MockBeaconClient) SetState(state *state.NetworkStateIndex) { } bc.validatorPubkeys[validatorIndex(v.Index)] = v.Pubkey } - for _, v := range state.MegapoolValidatorGlobalIndex { + for _, v := range state.MegapoolValidators { pubkey := types.BytesToValidatorPubkey(v.Pubkey) details, ok := state.MegapoolValidatorDetails[pubkey] if !ok { diff --git a/shared/services/rewards/test/mock.go b/shared/services/rewards/test/mock.go index cd4e99ac1..d92a6103b 100644 --- a/shared/services/rewards/test/mock.go +++ b/shared/services/rewards/test/mock.go @@ -681,9 +681,9 @@ func (h *MockHistory) GetEndNetworkState() *state.NetworkState { OracleDaoMemberDetails: []rpstate.OracleDaoMemberDetails{}, ProtocolDaoProposalDetails: nil, - MegapoolValidatorGlobalIndex: []megapool.ValidatorInfoFromGlobalIndex{}, - MegapoolDetails: make(map[common.Address]rpstate.NativeMegapoolDetails), - MegapoolValidatorDetails: make(state.ValidatorDetailsMap), + MegapoolValidators: []megapool.MegapoolValidatorInfo{}, + MegapoolDetails: make(map[common.Address]rpstate.NativeMegapoolDetails), + MegapoolValidatorDetails: make(state.ValidatorDetailsMap), } // Add nodes @@ -851,7 +851,7 @@ func (h *MockHistory) GetEndNetworkState() *state.NetworkState { if err != nil { panic(err) } - vifgi := megapool.ValidatorInfoFromGlobalIndex{ + vifgi := megapool.MegapoolValidatorInfo{ Pubkey: pubkey.Bytes(), ValidatorInfo: megapool.ValidatorInfo{ Staked: true, @@ -859,7 +859,7 @@ func (h *MockHistory) GetEndNetworkState() *state.NetworkState { MegapoolAddress: node.MegapoolAddress(), ValidatorId: uint32(intIdx), } - out.MegapoolValidatorGlobalIndex = append(out.MegapoolValidatorGlobalIndex, vifgi) + out.MegapoolValidators = append(out.MegapoolValidators, vifgi) out.MegapoolValidatorDetails[pubkey] = beacon.ValidatorStatus{ Pubkey: pubkey, Index: idx, diff --git a/shared/services/rewards/types.go b/shared/services/rewards/types.go index 968d0629b..7c7650fb8 100644 --- a/shared/services/rewards/types.go +++ b/shared/services/rewards/types.go @@ -238,7 +238,7 @@ type MegapoolValidatorInfo struct { CompletedAttestations map[uint64]bool `json:"-"` AttestationCount int `json:"attestationCount"` - NativeValidatorInfo *megapool.ValidatorInfoFromGlobalIndex `json:"nativeValidatorInfo"` + NativeValidatorInfo *megapool.MegapoolValidatorInfo `json:"nativeValidatorInfo"` // Amount of eth earned by this validator in the smoothing pool MegapoolValidatorShare *big.Int `json:"megapoolValidatorShare"` diff --git a/shared/services/state/cli/cli.go b/shared/services/state/cli/cli.go index 4f013f4bb..861a7159d 100644 --- a/shared/services/state/cli/cli.go +++ b/shared/services/state/cli/cli.go @@ -39,8 +39,8 @@ func truncateNetworkState(ns *state.NetworkStateIndex) { if len(ns.MinipoolDetails) > 1 { ns.MinipoolDetails = ns.MinipoolDetails[:1] } - if len(ns.MegapoolValidatorGlobalIndex) > 1 { - ns.MegapoolValidatorGlobalIndex = ns.MegapoolValidatorGlobalIndex[:1] + if len(ns.MegapoolValidators) > 1 { + ns.MegapoolValidators = ns.MegapoolValidators[:1] } if len(ns.OracleDaoMemberDetails) > 1 { ns.OracleDaoMemberDetails = ns.OracleDaoMemberDetails[:1] diff --git a/shared/services/state/network-state.go b/shared/services/state/network-state.go index 0da522ab8..6b0b9576b 100644 --- a/shared/services/state/network-state.go +++ b/shared/services/state/network-state.go @@ -89,9 +89,9 @@ type NetworkState struct { MinipoolDetails []rpstate.NativeMinipoolDetails `json:"minipool_details"` // Stores validator details from all megapools - MegapoolValidatorGlobalIndex []megapool.ValidatorInfoFromGlobalIndex `json:"megapool_validator_global_index"` - - MegapoolDetails map[common.Address]rpstate.NativeMegapoolDetails `json:"megapool_details"` + // The json tag "megapool_validator_global_index" is kept for backwards compatibility + MegapoolValidators []megapool.MegapoolValidatorInfo `json:"megapool_validator_global_index"` + MegapoolDetails map[common.Address]rpstate.NativeMegapoolDetails `json:"megapool_details"` // Validator details // NetworkState was updated to support megapools, so the old json tag "validator_details" is needed to decode rp-network-state-mainnet-20.json.gz @@ -129,7 +129,7 @@ type NetworkStateIndex struct { MegapoolToPubkeysMap map[common.Address][]types.ValidatorPubkey MinipoolDetailsByAddress map[common.Address]*rpstate.NativeMinipoolDetails MinipoolDetailsByNode map[common.Address][]*rpstate.NativeMinipoolDetails - MegapoolValidatorInfo map[MegapoolValidatorKey]*megapool.ValidatorInfoFromGlobalIndex + MegapoolValidatorInfo map[MegapoolValidatorKey]*megapool.MegapoolValidatorInfo NodeFeeDetailsByAddress map[common.Address]*rpstate.NodeFeeDetails } @@ -170,9 +170,9 @@ func (s *NetworkState) ToIndexedNetworkState() *NetworkStateIndex { } out.MegapoolToPubkeysMap = make(map[common.Address][]types.ValidatorPubkey) - out.MegapoolValidatorInfo = make(map[MegapoolValidatorKey]*megapool.ValidatorInfoFromGlobalIndex) - for i := range s.MegapoolValidatorGlobalIndex { - validator := &s.MegapoolValidatorGlobalIndex[i] + out.MegapoolValidatorInfo = make(map[MegapoolValidatorKey]*megapool.MegapoolValidatorInfo) + for i := range s.MegapoolValidators { + validator := &s.MegapoolValidators[i] if len(validator.Pubkey) > 0 { pubkey := types.ValidatorPubkey(validator.Pubkey) out.MegapoolToPubkeysMap[validator.MegapoolAddress] = append( @@ -213,9 +213,9 @@ func (s *NetworkStateIndex) UnmarshalJSON(data []byte) error { } func (s *NetworkState) GetUniqueMegapoolPubkeys() []types.ValidatorPubkey { - pubkeys := make([]types.ValidatorPubkey, 0, len(s.MegapoolValidatorGlobalIndex)) - seen := make(map[types.ValidatorPubkey]bool, len(s.MegapoolValidatorGlobalIndex)) - for _, validator := range s.MegapoolValidatorGlobalIndex { + pubkeys := make([]types.ValidatorPubkey, 0, len(s.MegapoolValidators)) + seen := make(map[types.ValidatorPubkey]bool, len(s.MegapoolValidators)) + for _, validator := range s.MegapoolValidators { if len(validator.Pubkey) > 0 { pubkey := types.ValidatorPubkey(validator.Pubkey) if !seen[pubkey] { @@ -240,8 +240,8 @@ func (s *NetworkState) GetMinipoolPubkeys() []types.ValidatorPubkey { func (s *NetworkState) getMegapoolAddresses() []common.Address { seen := make(map[common.Address]bool) - addresses := make([]common.Address, 0, len(s.MegapoolValidatorGlobalIndex)) - for _, megapool := range s.MegapoolValidatorGlobalIndex { + addresses := make([]common.Address, 0, len(s.MegapoolValidators)) + for _, megapool := range s.MegapoolValidators { if len(megapool.Pubkey) == 0 { continue } @@ -278,7 +278,7 @@ func (s *NetworkState) Validate() error { } // Returns the validator info for the given megapool and pubkey. -func (s *NetworkStateIndex) GetMegapoolValidatorInfo(megapoolAddress common.Address, pubkey types.ValidatorPubkey) (*megapool.ValidatorInfoFromGlobalIndex, bool) { +func (s *NetworkStateIndex) GetMegapoolValidatorInfo(megapoolAddress common.Address, pubkey types.ValidatorPubkey) (*megapool.MegapoolValidatorInfo, bool) { info, exists := s.MegapoolValidatorInfo[MegapoolValidatorKey{MegapoolAddress: megapoolAddress, Pubkey: pubkey}] return info, exists } @@ -377,12 +377,12 @@ func (m *NetworkStateManager) createNetworkState(slotNumber uint64, nodeAddresse m.logLine("%d/%d - Retrieved minipool details (%s so far)", currentStep, steps, time.Since(start)) if allNodes { - state.MegapoolValidatorGlobalIndex, err = rpstate.GetAllMegapoolValidators(m.rp, contracts) + state.MegapoolValidators, err = rpstate.GetAllMegapoolValidators(m.rp, contracts) if err != nil { return nil, fmt.Errorf("error getting all megapool validator details: %w", err) } } else { - state.MegapoolValidatorGlobalIndex = []megapool.ValidatorInfoFromGlobalIndex{} + state.MegapoolValidators = []megapool.MegapoolValidatorInfo{} for _, nd := range state.NodeDetails { if !nd.MegapoolDeployed { continue @@ -391,7 +391,7 @@ func (m *NetworkStateManager) createNetworkState(slotNumber uint64, nodeAddresse if err != nil { return nil, fmt.Errorf("error getting megapool validator details for %s: %w", nd.MegapoolAddress.Hex(), err) } - state.MegapoolValidatorGlobalIndex = append(state.MegapoolValidatorGlobalIndex, validators...) + state.MegapoolValidators = append(state.MegapoolValidators, validators...) } } currentStep++ diff --git a/shared/services/state/network-state_test.go b/shared/services/state/network-state_test.go index 36b68c524..f33f3e8cc 100644 --- a/shared/services/state/network-state_test.go +++ b/shared/services/state/network-state_test.go @@ -190,7 +190,7 @@ func buildTestState() *NetworkState { }, } - megapoolValidatorGlobalIndex := []megapool.ValidatorInfoFromGlobalIndex{ + megapoolValidatorGlobalIndex := []megapool.MegapoolValidatorInfo{ { Pubkey: megapoolPubkey[:], MegapoolAddress: megapoolAddrA, @@ -245,8 +245,8 @@ func buildTestState() *NetworkState { MegapoolValidatorDetails: ValidatorDetailsMap{ megapoolPubkey: {Pubkey: megapoolPubkey, Index: "4", Exists: true, Balance: 32000000000, ActivationEpoch: 0, ExitEpoch: ^uint64(0)}, }, - MegapoolValidatorGlobalIndex: megapoolValidatorGlobalIndex, - MegapoolDetails: megapoolDetails, + MegapoolValidators: megapoolValidatorGlobalIndex, + MegapoolDetails: megapoolDetails, OracleDaoMemberDetails: []rpstate.OracleDaoMemberDetails{ { Address: nodeAddrA, @@ -365,9 +365,9 @@ func TestNetworkStateJSONRoundtrip(t *testing.T) { } // Megapool validator global index - if len(restored.MegapoolValidatorGlobalIndex) != len(original.MegapoolValidatorGlobalIndex) { + if len(restored.MegapoolValidators) != len(original.MegapoolValidators) { t.Errorf("MegapoolValidatorGlobalIndex count: got %d, want %d", - len(restored.MegapoolValidatorGlobalIndex), len(original.MegapoolValidatorGlobalIndex)) + len(restored.MegapoolValidators), len(original.MegapoolValidators)) } // Oracle DAO member details @@ -475,7 +475,7 @@ func TestDuplicatePubkeyAcrossMegapools(t *testing.T) { state := &NetworkState{ MinipoolValidatorDetails: ValidatorDetailsMap{}, MegapoolValidatorDetails: ValidatorDetailsMap{}, - MegapoolValidatorGlobalIndex: []megapool.ValidatorInfoFromGlobalIndex{ + MegapoolValidators: []megapool.MegapoolValidatorInfo{ { Pubkey: pubkey[:], MegapoolAddress: megapoolAddrA, diff --git a/shared/services/state/static_provider_test.go b/shared/services/state/static_provider_test.go index b7c96005f..7ffa43a56 100644 --- a/shared/services/state/static_provider_test.go +++ b/shared/services/state/static_provider_test.go @@ -52,8 +52,8 @@ func TestStaticProviderFromFile(t *testing.T) { if len(nsi.MegapoolValidatorDetails) != 1 { t.Errorf("MegapoolValidatorDetails count: got %d, want 1", len(nsi.MegapoolValidatorDetails)) } - if len(nsi.MegapoolValidatorGlobalIndex) != 1 { - t.Errorf("MegapoolValidatorGlobalIndex count: got %d, want 1", len(nsi.MegapoolValidatorGlobalIndex)) + if len(nsi.MegapoolValidators) != 1 { + t.Errorf("MegapoolValidatorGlobalIndex count: got %d, want 1", len(nsi.MegapoolValidators)) } if len(nsi.OracleDaoMemberDetails) != 1 { t.Errorf("OracleDaoMemberDetails count: got %d, want 1", len(nsi.OracleDaoMemberDetails)) @@ -200,7 +200,7 @@ func TestStaticProviderMegapoolToPubkeysMap(t *testing.T) { } expectedCount := 0 - for _, v := range nsi.MegapoolValidatorGlobalIndex { + for _, v := range nsi.MegapoolValidators { if len(v.Pubkey) > 0 { expectedCount++ } @@ -230,8 +230,8 @@ func TestStaticProviderMegapoolValidatorInfo(t *testing.T) { // Every entry in MegapoolValidatorInfo must point back into MegapoolValidatorGlobalIndex for key, info := range nsi.MegapoolValidatorInfo { found := false - for i := range nsi.MegapoolValidatorGlobalIndex { - candidate := &nsi.MegapoolValidatorGlobalIndex[i] + for i := range nsi.MegapoolValidators { + candidate := &nsi.MegapoolValidators[i] if candidate == info { found = true break From a131484c4e24660dbb826839ebaf166c4e21e847 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Tue, 1 Sep 2026 15:35:42 -0400 Subject: [PATCH 3/3] Move NodeFeeDetails into state module, make calculateAverageFeeAndDistributorShares non-exported --- bindings/utils/state/node.go | 53 ----------------------- shared/services/state/network-state.go | 60 ++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/bindings/utils/state/node.go b/bindings/utils/state/node.go index 381e7cec2..4a692e0a6 100644 --- a/bindings/utils/state/node.go +++ b/bindings/utils/state/node.go @@ -13,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/bindings/megapool" "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/multicall" ) @@ -62,12 +61,6 @@ type NativeNodeDetails struct { MegapoolDeployed bool `json:"megapool_deployed"` } -type NodeFeeDetails struct { - DistributorBalanceUserETH *big.Int `json:"distributor_balance_user_eth"` - DistributorBalanceNodeETH *big.Int `json:"distributor_balance_node_eth"` - AverageNodeFee *big.Int `json:"average_node_fee"` -} - func timeMax(a, b time.Time) time.Time { if a.After(b) { return a @@ -255,52 +248,6 @@ func (node *NativeNodeDetails) WasOptedInAt(t time.Time) bool { return t.Before(time.Unix(node.SmoothingPoolRegistrationChanged.Int64(), 0)) } -// Calculate the average node fee and user/node shares of the distributor's balance -func (nfd *NodeFeeDetails) CalculateAverageFeeAndDistributorShares(nnd *NativeNodeDetails, minipoolDetails []*NativeMinipoolDetails) { - - // Calculate the total of all fees for staking minipools that aren't finalized - totalFee := big.NewInt(0) - eligibleMinipools := int64(0) - for _, mpd := range minipoolDetails { - if mpd.Status == types.Staking && !mpd.Finalised { - totalFee.Add(totalFee, mpd.NodeFee) - eligibleMinipools++ - } - } - - // Get the average fee (0 if there aren't any minipools) - if eligibleMinipools > 0 { - nfd.AverageNodeFee.Div(totalFee, big.NewInt(eligibleMinipools)) - } - - // Get the user and node portions of the distributor balance - distributorBalance := big.NewInt(0).Set(nnd.DistributorBalance) - if distributorBalance.Cmp(big.NewInt(0)) > 0 { - nodeBalance := big.NewInt(0) - nodeBalance.Mul(distributorBalance, big.NewInt(1e18)) - nodeBalance.Div(nodeBalance, nnd.CollateralisationRatio) - - userBalance := big.NewInt(0) - userBalance.Sub(distributorBalance, nodeBalance) - - if eligibleMinipools == 0 { - // Split it based solely on the collateralisation ratio if there are no minipools (and hence no average fee) - nfd.DistributorBalanceNodeETH = big.NewInt(0).Set(nodeBalance) - nfd.DistributorBalanceUserETH = big.NewInt(0).Sub(distributorBalance, nodeBalance) - } else { - // Amount of ETH given to the NO as a commission - commissionEth := big.NewInt(0) - commissionEth.Mul(userBalance, nfd.AverageNodeFee) - commissionEth.Div(commissionEth, big.NewInt(1e18)) - - nfd.DistributorBalanceNodeETH.Add(nodeBalance, commissionEth) // Node gets their portion + commission on user portion - nfd.DistributorBalanceUserETH.Sub(distributorBalance, nfd.DistributorBalanceNodeETH) // User gets balance - node share - } - - } - -} - // Get all node addresses using the multicaller func getNodeAddressesFast(rp *rocketpool.RocketPool, contracts *NetworkContracts, opts *bind.CallOpts) ([]common.Address, error) { // Get minipool count diff --git a/shared/services/state/network-state.go b/shared/services/state/network-state.go index 6b0b9576b..7dc2b70b9 100644 --- a/shared/services/state/network-state.go +++ b/shared/services/state/network-state.go @@ -123,6 +123,58 @@ func (s *NetworkState) UnmarshalJSON(data []byte) error { return s.Validate() } +type NodeFeeDetails struct { + DistributorBalanceUserETH *big.Int `json:"distributor_balance_user_eth"` + DistributorBalanceNodeETH *big.Int `json:"distributor_balance_node_eth"` + AverageNodeFee *big.Int `json:"average_node_fee"` +} + +// Calculate the average node fee and user/node shares of the distributor's balance +func (nfd *NodeFeeDetails) calculateAverageFeeAndDistributorShares(nnd *rpstate.NativeNodeDetails, minipoolDetails []*rpstate.NativeMinipoolDetails) { + + // Calculate the total of all fees for staking minipools that aren't finalized + totalFee := big.NewInt(0) + eligibleMinipools := int64(0) + for _, mpd := range minipoolDetails { + if mpd.Status == types.Staking && !mpd.Finalised { + totalFee.Add(totalFee, mpd.NodeFee) + eligibleMinipools++ + } + } + + // Get the average fee (0 if there aren't any minipools) + if eligibleMinipools > 0 { + nfd.AverageNodeFee.Div(totalFee, big.NewInt(eligibleMinipools)) + } + + // Get the user and node portions of the distributor balance + distributorBalance := big.NewInt(0).Set(nnd.DistributorBalance) + if distributorBalance.Cmp(big.NewInt(0)) > 0 { + nodeBalance := big.NewInt(0) + nodeBalance.Mul(distributorBalance, big.NewInt(1e18)) + nodeBalance.Div(nodeBalance, nnd.CollateralisationRatio) + + userBalance := big.NewInt(0) + userBalance.Sub(distributorBalance, nodeBalance) + + if eligibleMinipools == 0 { + // Split it based solely on the collateralisation ratio if there are no minipools (and hence no average fee) + nfd.DistributorBalanceNodeETH = big.NewInt(0).Set(nodeBalance) + nfd.DistributorBalanceUserETH = big.NewInt(0).Sub(distributorBalance, nodeBalance) + } else { + // Amount of ETH given to the NO as a commission + commissionEth := big.NewInt(0) + commissionEth.Mul(userBalance, nfd.AverageNodeFee) + commissionEth.Div(commissionEth, big.NewInt(1e18)) + + nfd.DistributorBalanceNodeETH.Add(nodeBalance, commissionEth) // Node gets their portion + commission on user portion + nfd.DistributorBalanceUserETH.Sub(distributorBalance, nfd.DistributorBalanceNodeETH) // User gets balance - node share + } + + } + +} + type NetworkStateIndex struct { *NetworkState NodeDetailsByAddress map[common.Address]*rpstate.NativeNodeDetails @@ -130,7 +182,7 @@ type NetworkStateIndex struct { MinipoolDetailsByAddress map[common.Address]*rpstate.NativeMinipoolDetails MinipoolDetailsByNode map[common.Address][]*rpstate.NativeMinipoolDetails MegapoolValidatorInfo map[MegapoolValidatorKey]*megapool.MegapoolValidatorInfo - NodeFeeDetailsByAddress map[common.Address]*rpstate.NodeFeeDetails + NodeFeeDetailsByAddress map[common.Address]*NodeFeeDetails } func (s *NetworkState) ToIndexedNetworkState() *NetworkStateIndex { @@ -183,14 +235,14 @@ func (s *NetworkState) ToIndexedNetworkState() *NetworkStateIndex { } // Calculate avg node fees and distributor shares - out.NodeFeeDetailsByAddress = make(map[common.Address]*rpstate.NodeFeeDetails) + out.NodeFeeDetailsByAddress = make(map[common.Address]*NodeFeeDetails) for _, details := range s.NodeDetails { - out.NodeFeeDetailsByAddress[details.NodeAddress] = &rpstate.NodeFeeDetails{ + out.NodeFeeDetailsByAddress[details.NodeAddress] = &NodeFeeDetails{ DistributorBalanceNodeETH: big.NewInt(0), DistributorBalanceUserETH: big.NewInt(0), AverageNodeFee: big.NewInt(0), } - out.NodeFeeDetailsByAddress[details.NodeAddress].CalculateAverageFeeAndDistributorShares(&details, out.MinipoolDetailsByNode[details.NodeAddress]) + out.NodeFeeDetailsByAddress[details.NodeAddress].calculateAverageFeeAndDistributorShares(&details, out.MinipoolDetailsByNode[details.NodeAddress]) } return out