Skip to content

Commit e0aa0f1

Browse files
authored
Pectra changes. Consolidate + new reward calculation (#238)
1 parent e4573bb commit e0aa0f1

File tree

7 files changed

+1231
-140
lines changed

7 files changed

+1231
-140
lines changed

api/api.go

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,29 @@ func (m *ApiService) handleMemoryStatistics(w http.ResponseWriter, req *http.Req
365365
avgBlockRewardWei = big.NewInt(0).Div(totalRewardsSentWei, big.NewInt(0).SetUint64(totalOkPoolProposalBlocks))
366366
}
367367

368+
// Total and average effective balance
369+
totalEffectiveBalance := uint64(0)
370+
effectiveBalances := m.Onchain.Validators()
371+
372+
for _, validator := range m.oracle.State().Validators {
373+
if validator.ValidatorStatus == oracle.Active ||
374+
validator.ValidatorStatus == oracle.YellowCard ||
375+
validator.ValidatorStatus == oracle.RedCard {
376+
377+
beaconState, found := effectiveBalances[phase0.ValidatorIndex(validator.ValidatorIndex)]
378+
if !found {
379+
log.Warn("could not find validator in beacon state: ", validator.ValidatorIndex)
380+
continue
381+
}
382+
totalEffectiveBalance += uint64(beaconState.Validator.EffectiveBalance)
383+
}
384+
}
385+
386+
avgEffectiveBalance := uint64(0)
387+
if totalSubscribed > 0 {
388+
avgEffectiveBalance = totalEffectiveBalance / totalSubscribed
389+
}
390+
368391
m.respondOK(w, httpOkMemoryStatistics{
369392
TotalSubscribed: totalSubscribed,
370393
TotalActive: totalActive,
@@ -384,6 +407,8 @@ func (m *ApiService) handleMemoryStatistics(w http.ResponseWriter, req *http.Req
384407
TotalProposedBlocks: totalProposedBlocks,
385408
TotalMissedBlocks: uint64(len(m.oracle.State().MissedBlocks)),
386409
TotalWrongFeeBlocks: uint64(len(m.oracle.State().WrongFeeBlocks)),
410+
TotalEffectiveBalanceGwei: fmt.Sprintf("%d", totalEffectiveBalance),
411+
AvgEffectiveBalanceGwei: fmt.Sprintf("%d", avgEffectiveBalance),
387412
})
388413
}
389414

@@ -515,15 +540,17 @@ func (m *ApiService) handleMemoryValidators(w http.ResponseWriter, req *http.Req
515540
continue
516541
}
517542
validatorsResp = append(validatorsResp, httpOkValidatorInfo{
518-
ValidatorStatus: v.ValidatorStatus.String(),
519-
BeaconValidatorStatus: beaconState.Status.String(),
520-
AccumulatedRewardsWei: v.AccumulatedRewardsWei.String(),
521-
PendingRewardsWei: v.PendingRewardsWei.String(),
522-
CollateralWei: v.CollateralWei.String(),
523-
WithdrawalAddress: v.WithdrawalAddress,
524-
ValidatorIndex: v.ValidatorIndex,
525-
ValidatorKey: v.ValidatorKey,
526-
SubscriptionType: v.SubscriptionType.String(),
543+
ValidatorStatus: v.ValidatorStatus.String(),
544+
BeaconValidatorBalanceGwei: fmt.Sprintf("%d", beaconState.Balance),
545+
BeaconValidatorEffectiveBalanceGwei: fmt.Sprintf("%d", beaconState.Validator.EffectiveBalance),
546+
BeaconValidatorStatus: beaconState.Status.String(),
547+
AccumulatedRewardsWei: v.AccumulatedRewardsWei.String(),
548+
PendingRewardsWei: v.PendingRewardsWei.String(),
549+
CollateralWei: v.CollateralWei.String(),
550+
WithdrawalAddress: v.WithdrawalAddress,
551+
ValidatorIndex: v.ValidatorIndex,
552+
ValidatorKey: v.ValidatorKey,
553+
SubscriptionType: v.SubscriptionType.String(),
527554
})
528555
}
529556

@@ -601,15 +628,17 @@ func (m *ApiService) handleMemoryValidatorsByIndex(w http.ResponseWriter, req *h
601628
continue
602629
}
603630
foundValidator := httpOkValidatorInfo{
604-
ValidatorStatus: validator.ValidatorStatus.String(),
605-
BeaconValidatorStatus: beaconState.Status.String(),
606-
AccumulatedRewardsWei: validator.AccumulatedRewardsWei.String(),
607-
PendingRewardsWei: validator.PendingRewardsWei.String(),
608-
CollateralWei: validator.CollateralWei.String(),
609-
WithdrawalAddress: validator.WithdrawalAddress,
610-
ValidatorIndex: validator.ValidatorIndex,
611-
ValidatorKey: validator.ValidatorKey,
612-
SubscriptionType: validator.SubscriptionType.String(),
631+
ValidatorStatus: validator.ValidatorStatus.String(),
632+
BeaconValidatorBalanceGwei: fmt.Sprintf("%d", beaconState.Balance),
633+
BeaconValidatorEffectiveBalanceGwei: fmt.Sprintf("%d", beaconState.Validator.EffectiveBalance),
634+
BeaconValidatorStatus: beaconState.Status.String(),
635+
AccumulatedRewardsWei: validator.AccumulatedRewardsWei.String(),
636+
PendingRewardsWei: validator.PendingRewardsWei.String(),
637+
CollateralWei: validator.CollateralWei.String(),
638+
WithdrawalAddress: validator.WithdrawalAddress,
639+
ValidatorIndex: validator.ValidatorIndex,
640+
ValidatorKey: validator.ValidatorKey,
641+
SubscriptionType: validator.SubscriptionType.String(),
613642
}
614643
foundValidators = append(foundValidators, foundValidator)
615644
} else {
@@ -754,15 +783,17 @@ func (m *ApiService) handleMemoryValidatorsByWithdrawal(w http.ResponseWriter, r
754783
continue
755784
}
756785
validatorsResp = append(validatorsResp, httpOkValidatorInfo{
757-
ValidatorStatus: v.ValidatorStatus.String(),
758-
BeaconValidatorStatus: beaconState.Status.String(),
759-
AccumulatedRewardsWei: v.AccumulatedRewardsWei.String(),
760-
PendingRewardsWei: v.PendingRewardsWei.String(),
761-
CollateralWei: v.CollateralWei.String(),
762-
WithdrawalAddress: v.WithdrawalAddress,
763-
ValidatorIndex: v.ValidatorIndex,
764-
ValidatorKey: v.ValidatorKey,
765-
SubscriptionType: v.SubscriptionType.String(),
786+
ValidatorStatus: v.ValidatorStatus.String(),
787+
BeaconValidatorBalanceGwei: fmt.Sprintf("%d", beaconState.Balance),
788+
BeaconValidatorEffectiveBalanceGwei: fmt.Sprintf("%d", beaconState.Validator.EffectiveBalance),
789+
BeaconValidatorStatus: beaconState.Status.String(),
790+
AccumulatedRewardsWei: v.AccumulatedRewardsWei.String(),
791+
PendingRewardsWei: v.PendingRewardsWei.String(),
792+
CollateralWei: v.CollateralWei.String(),
793+
WithdrawalAddress: v.WithdrawalAddress,
794+
ValidatorIndex: v.ValidatorIndex,
795+
ValidatorKey: v.ValidatorKey,
796+
SubscriptionType: v.SubscriptionType.String(),
766797
})
767798
}
768799
m.respondOK(w, validatorsResp)

api/types.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ type httpOkMemoryStatistics struct {
7979
TotalProposedBlocks uint64 `json:"total_proposed_blocks"`
8080
TotalMissedBlocks uint64 `json:"total_missed_blocks"`
8181
TotalWrongFeeBlocks uint64 `json:"total_wrongfee_blocks"`
82+
TotalEffectiveBalanceGwei string `json:"total_effective_balance_gwei"`
83+
AvgEffectiveBalanceGwei string `json:"avg_effective_balance_gwei"`
8284
}
8385

8486
type httpOkProofs struct {
@@ -130,15 +132,17 @@ type httpOkBlock struct {
130132
}
131133

132134
type httpOkValidatorInfo struct {
133-
ValidatorStatus string `json:"status"`
134-
BeaconValidatorStatus string `json:"beacon_status"`
135-
AccumulatedRewardsWei string `json:"accumulated_rewards_wei"`
136-
PendingRewardsWei string `json:"pending_rewards_wei"`
137-
CollateralWei string `json:"collateral_wei"`
138-
WithdrawalAddress string `json:"withdrawal_address"`
139-
ValidatorIndex uint64 `json:"validator_index"`
140-
ValidatorKey string `json:"validator_key"`
141-
SubscriptionType string `json:"subscription_type"`
135+
ValidatorStatus string `json:"status"`
136+
BeaconValidatorStatus string `json:"beacon_status"`
137+
BeaconValidatorBalanceGwei string `json:"beacon_balance_gwei"`
138+
BeaconValidatorEffectiveBalanceGwei string `json:"beacon_effective_balance_gwei"`
139+
AccumulatedRewardsWei string `json:"accumulated_rewards_wei"`
140+
PendingRewardsWei string `json:"pending_rewards_wei"`
141+
CollateralWei string `json:"collateral_wei"`
142+
WithdrawalAddress string `json:"withdrawal_address"`
143+
ValidatorIndex uint64 `json:"validator_index"`
144+
ValidatorKey string `json:"validator_key"`
145+
SubscriptionType string `json:"subscription_type"`
142146
}
143147

144148
type httpOkValidatorsByIndex struct {

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func main() {
129129
// Create the oracle instance
130130
oracleInstance := oracle.NewOracle(cfg)
131131
oracleInstance.SetGetSetOfValidatorsFunc(onchain.GetSetOfValidators)
132+
oracleInstance.GetPendingConsolidationsFunc(onchain.GetPendingConsolidations)
132133

133134
// If checkpoint sync url is provided, load state from it
134135
if cliCfg.CheckPointSyncUrl != "" {
@@ -309,7 +310,7 @@ func mainLoop(oracleInstance *oracle.Oracle, onchain *oracle.Onchain, cfg *oracl
309310
continue
310311
}
311312

312-
// Every X slots we update the onchain validators and cleanup any stranded oracle validators
313+
// Every X slots we update the onchain validators
313314
if oracleInstance.State().LatestProcessedSlot%UpdateValidatorsIntervalSlots == 0 {
314315
onchain.RefreshBeaconValidators()
315316
}

oracle/onchain.go

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ import (
44
"context"
55
"crypto/ecdsa"
66
"encoding/hex"
7+
"encoding/json"
78
"fmt"
9+
"io"
810
"math/big"
11+
normalHttp "net/http"
912
"strconv"
1013
"strings"
1114
"time"
@@ -40,6 +43,12 @@ var GoerliChainId = uint64(5)
4043
var HoleskyChainId = uint64(17000)
4144
var HoodiChainId = uint64(560048)
4245

46+
// Network names
47+
var Mainnet = "mainnet"
48+
var Goerli = "goerli"
49+
var Holesky = "holesky"
50+
var Hoodi = "hoodi"
51+
4352
// This file provides different functions to access the blockchain state from both consensus and
4453
// execution layer and modifying the its state via smart contract calls.
4554
type EpochDuties struct {
@@ -1063,13 +1072,13 @@ func (onchain *Onchain) GetConfigFromContract(
10631072

10641073
network := ""
10651074
if depositContract.Data.ChainID == MainnetChainId {
1066-
network = "mainnet"
1075+
network = Mainnet
10671076
} else if depositContract.Data.ChainID == GoerliChainId {
1068-
network = "goerli"
1077+
network = Goerli
10691078
} else if depositContract.Data.ChainID == HoleskyChainId {
1070-
network = "holesky"
1079+
network = Holesky
10711080
} else if depositContract.Data.ChainID == HoodiChainId {
1072-
network = "hoodi"
1081+
network = Hoodi
10731082
} else {
10741083
log.Fatal("ChainID not supported: ", depositContract.Data.ChainID)
10751084
}
@@ -1763,3 +1772,62 @@ func (o *Onchain) GetRetryOpts(opts []retry.Option) []retry.Option {
17631772
return opts
17641773
}
17651774
}
1775+
1776+
// TODO: Manual implementation of pending consolidations beacon API call
1777+
// Remove once attestantio eth2 library is updated to support it
1778+
type PendingConsolidation struct {
1779+
SourceIndex phase0.ValidatorIndex `json:"source_index"`
1780+
TargetIndex phase0.ValidatorIndex `json:"target_index"`
1781+
}
1782+
1783+
type PendingConsolidationsResponse struct {
1784+
Version string `json:"version"`
1785+
ExecutionOptimistic bool `json:"execution_optimistic"`
1786+
Finalized bool `json:"finalized"`
1787+
Data []PendingConsolidation `json:"data"`
1788+
}
1789+
1790+
func (o *Onchain) GetPendingConsolidations(stateID string, opts ...retry.Option) (*PendingConsolidationsResponse, error) {
1791+
var (
1792+
consolidations *PendingConsolidationsResponse
1793+
err error
1794+
)
1795+
1796+
err = retry.Do(func() error {
1797+
url := fmt.Sprintf("%s/eth/v1/beacon/states/%s/pending_consolidations", o.ConsensusClient.Address(), stateID)
1798+
1799+
req, reqErr := normalHttp.NewRequest(normalHttp.MethodGet, url, nil)
1800+
if reqErr != nil {
1801+
return fmt.Errorf("creating request failed: %w", reqErr)
1802+
}
1803+
req.Header.Set("Accept", "application/json")
1804+
1805+
resp, httpErr := normalHttp.DefaultClient.Do(req)
1806+
if httpErr != nil {
1807+
log.Warn("Failed attempt to call pending consolidations endpoint: ", httpErr.Error(), " Retrying...")
1808+
return fmt.Errorf("HTTP request failed: %w", httpErr)
1809+
}
1810+
defer resp.Body.Close()
1811+
1812+
if resp.StatusCode != normalHttp.StatusOK {
1813+
bodyBytes, _ := io.ReadAll(resp.Body)
1814+
log.Warn("Received non-200 response: ", resp.StatusCode, " Body: ", string(bodyBytes))
1815+
return fmt.Errorf("non-200 response: %d - %s", resp.StatusCode, string(bodyBytes))
1816+
}
1817+
1818+
var result PendingConsolidationsResponse
1819+
if decodeErr := json.NewDecoder(resp.Body).Decode(&result); decodeErr != nil {
1820+
log.Warn("Failed attempt to decode pending consolidations response: ", decodeErr.Error(), " Retrying...")
1821+
return fmt.Errorf("decoding response failed: %w", decodeErr)
1822+
}
1823+
1824+
consolidations = &result
1825+
return nil
1826+
}, o.GetRetryOpts(opts)...)
1827+
1828+
if err != nil {
1829+
return nil, fmt.Errorf("failed to fetch pending consolidations: %w", err)
1830+
}
1831+
1832+
return consolidations, nil
1833+
}

oracle/onchain_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,27 @@ func Test_EndToEnd_Mainnet(t *testing.T) {
470470
}
471471
}
472472

473+
func Test_GetPendingConsolidations(t *testing.T) {
474+
// Note it works only for hoodi
475+
t.Skip("Skipping test")
476+
477+
var cfgOnchain = &config.CliConfig{
478+
ConsensusEndpoint: "http://127.0.0.1:3500",
479+
ExecutionEndpoint: "http://127.0.0.1:8545",
480+
PoolAddress: "0xAdFb8D27671F14f297eE94135e266aAFf8752e35",
481+
}
482+
483+
onchain, err := NewOnchain(cfgOnchain, nil)
484+
require.NoError(t, err)
485+
486+
consolidations, err := onchain.GetPendingConsolidations("100000")
487+
require.NoError(t, err)
488+
489+
for _, c := range consolidations.Data {
490+
fmt.Println(c.SourceIndex, c.TargetIndex)
491+
}
492+
}
493+
473494
func Test_Mainnet_BeaverIssue(t *testing.T) {
474495
t.Skip("Skipping test")
475496

0 commit comments

Comments
 (0)