Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix find-stale-pods logic #157

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Changes from all commits
Commits
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
75 changes: 57 additions & 18 deletions cli/core/findStalePods.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ type Cache struct {
PodOwnerShares map[string]PodOwnerShare
}

// multiply by a fraction
func FracMul(a *big.Int, x *big.Int, y *big.Int) *big.Int {
_a := new(big.Int).Mul(a, x)
return _a.Div(_a, y)
}

func keys[A comparable, B any](coll map[A]B) []A {
if len(coll) == 0 {
return []A{}
Expand All @@ -42,8 +48,8 @@ func keys[A comparable, B any](coll map[A]B) []A {
}

type PodOwnerShare struct {
SharesWei uint64
ExecutionLayerBalanceWei uint64
SharesWei *big.Int
ExecutionLayerBalanceWei *big.Int
IsEigenpod bool
}

Expand All @@ -60,8 +66,8 @@ func isEigenpod(eth *ethclient.Client, chainId uint64, eigenpodAddress string) (

// default to false
cache.PodOwnerShares[eigenpodAddress] = PodOwnerShare{
SharesWei: 0,
ExecutionLayerBalanceWei: 0,
SharesWei: big.NewInt(0),
ExecutionLayerBalanceWei: big.NewInt(0),
IsEigenpod: false,
}

Expand Down Expand Up @@ -109,8 +115,8 @@ func isEigenpod(eth *ethclient.Client, chainId uint64, eigenpodAddress string) (
// Simulate fetching from contracts
// Implement contract fetching logic here
cache.PodOwnerShares[eigenpodAddress] = PodOwnerShare{
SharesWei: podOwnerShares.Uint64(),
ExecutionLayerBalanceWei: balance.Uint64(),
SharesWei: podOwnerShares,
ExecutionLayerBalanceWei: balance,
IsEigenpod: true,
}

Expand Down Expand Up @@ -205,7 +211,7 @@ func FindStaleEigenpods(ctx context.Context, eth *ethclient.Client, nodeUrl stri

if verbose {
log.Printf("%d EigenValidators have been slashed\n", len(allSlashedValidatorsBelongingToEigenpods))
log.Printf("%d EigenValidators have been slashed AND were active\n", len(allActiveSlashedValidatorsBelongingToEigenpods))
log.Printf("%d EigenValidators have been slashed + active\n", len(allActiveSlashedValidatorsBelongingToEigenpods))
}

slashedEigenpods := utils.Reduce(allActiveSlashedValidatorsBelongingToEigenpods, func(pods map[string][]ValidatorWithIndex, validator ValidatorWithIndex) map[string][]ValidatorWithIndex {
Expand All @@ -224,42 +230,75 @@ func FindStaleEigenpods(ctx context.Context, eth *ethclient.Client, nodeUrl stri
return nil, err
}

totalAssetsWeiByEigenpod := utils.Reduce(keys(slashedEigenpods), func(allBalances map[string]uint64, eigenpod string) map[string]uint64 {
totalAssetsWeiByEigenpod := utils.Reduce(keys(slashedEigenpods), func(allBalances map[string]*big.Int, eigenpod string) map[string]*big.Int {
// total assets of an eigenpod are determined as;
// SUM(
// - native ETH in the pod
// - any active validators and their associated balances
// )
allActiveValidatorsForEigenpod := utils.Filter(allValidatorsWithIndices, func(v ValidatorWithIndex) bool {
if allValidatorInfo[v.Index].Status != 1 {
validatorsForEigenpod := utils.Filter(allValidatorsWithIndices, func(v ValidatorWithIndex) bool {
withdrawal := executionWithdrawalAddress(v.Validator.WithdrawalCredentials)
return withdrawal != nil && strings.EqualFold(*withdrawal, eigenpod)
})

podValidatorInfo, err := FetchMultipleOnchainValidatorInfo(ctx, eth, eigenpod, validatorsForEigenpod)
Copy link
Contributor Author

@jbrower95 jbrower95 Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we explicitly refetch validatorInfo, since we only fetch validator info re: slashed validators in the beginning.

if err != nil {
return allBalances
}

allActiveValidatorsForEigenpod := utils.Filter(podValidatorInfo, func(v ValidatorWithOnchainInfo) bool {
if v.Info.Status != 1 { // ignore any inactive validators
return false
}

withdrawal := executionWithdrawalAddress(v.Validator.WithdrawalCredentials)
return withdrawal != nil && strings.EqualFold(*withdrawal, eigenpod)
})

allActiveValidatorBalancesSummedGwei := utils.Reduce(allActiveValidatorsForEigenpod, func(accum phase0.Gwei, validator ValidatorWithIndex) phase0.Gwei {
allActiveValidatorBalancesSummedGwei := utils.Reduce(allActiveValidatorsForEigenpod, func(accum phase0.Gwei, validator ValidatorWithOnchainInfo) phase0.Gwei {
return accum + allValidatorBalances[validator.Index]
}, phase0.Gwei(0))
// converting gwei to wei
allBalances[eigenpod] = cache.PodOwnerShares[eigenpod].ExecutionLayerBalanceWei + (uint64(allActiveValidatorBalancesSummedGwei) * params.GWei)
activeValidatorBalancesSum := new(big.Int).Mul(
new(big.Int).SetUint64(uint64(allActiveValidatorBalancesSummedGwei)),
new(big.Int).SetUint64(params.GWei),
)

if verbose {
log.Printf("[%s] podOwnerShares(%sETH), anticipated balance = beacon(%s across %d validators) + executionBalance(%sETH)\n",
eigenpod,
IweiToEther(cache.PodOwnerShares[eigenpod].SharesWei).String(),
IweiToEther(activeValidatorBalancesSum).String(),
len(allActiveValidatorsForEigenpod),
IweiToEther(cache.PodOwnerShares[eigenpod].ExecutionLayerBalanceWei).String(),
) // converting gwei to wei
}

allBalances[eigenpod] = new(big.Int).Add(cache.PodOwnerShares[eigenpod].ExecutionLayerBalanceWei, activeValidatorBalancesSum)
return allBalances
}, map[string]uint64{})
}, map[string]*big.Int{})

if verbose {
log.Printf("%d EigenPods were slashed\n", len(slashedEigenpods))
}

unhealthyEigenpods := utils.Filter(keys(slashedEigenpods), func(eigenpod string) bool {
balance, ok := totalAssetsWeiByEigenpod[eigenpod]
currentTotalAssets, ok := totalAssetsWeiByEigenpod[eigenpod]
if !ok {
return false
}
executionBalance := cache.PodOwnerShares[eigenpod].SharesWei
if balance <= uint64(float64(executionBalance)*(1-(tolerance/100))) {
currentShares := cache.PodOwnerShares[eigenpod].SharesWei

delta := new(big.Int).Sub(currentShares, currentTotalAssets)
allowableDelta := FracMul(currentShares, big.NewInt(int64(tolerance)), big.NewInt(100))
if delta.Cmp(allowableDelta) >= 0 {
if verbose {
log.Printf("[%s] %.2f%% deviation (beacon: %d -> execution: %d)\n", eigenpod, 100*(float64(executionBalance)-float64(balance))/float64(executionBalance), balance, executionBalance)
log.Printf("[%s] %sETH drop in assets (max drop allowed: %sETH, current shares: %sETH, anticipated shares: %sETH)\n",
eigenpod,
IweiToEther(delta).String(),
IweiToEther(allowableDelta).String(),
IweiToEther(currentShares).String(),
IweiToEther(currentTotalAssets).String(),
)
}
return true
}
Expand Down
Loading