Skip to content

Commit

Permalink
feat(slash): Add the historical voting power to the slashing record (#…
Browse files Browse the repository at this point in the history
…271)

* add historical votin power to the slashing execution record and add some logs for slash execution

* revise the slashID construction function to ensure the queried slashes are ordered.

* rebase and fix the conflict

* rebase to develop and use AddMut in CalculateUSDValueForOperator

* fix the lint error regarding swagger
  • Loading branch information
TimmyExogenous authored Feb 19, 2025
1 parent 4b1fd01 commit 27161a9
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 124 deletions.
25 changes: 25 additions & 0 deletions client/docs/swagger-ui/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -22846,6 +22846,11 @@
"type": "string",
"format": "int64",
"title": "undelegation_filter_height records the height before which undelegations are not slashed"
},
"historical_voting_power": {
"type": "string",
"format": "int64",
"description": "the historical voting power at the time of the slash event."
}
}
}
Expand Down Expand Up @@ -44569,6 +44574,11 @@
"type": "string",
"format": "int64",
"title": "undelegation_filter_height records the height before which undelegations are not slashed"
},
"historical_voting_power": {
"type": "string",
"format": "int64",
"description": "the historical voting power at the time of the slash event."
}
}
}
Expand Down Expand Up @@ -44669,6 +44679,11 @@
"type": "string",
"format": "int64",
"title": "undelegation_filter_height records the height before which undelegations are not slashed"
},
"historical_voting_power": {
"type": "string",
"format": "int64",
"description": "the historical voting power at the time of the slash event."
}
}
}
Expand Down Expand Up @@ -45087,6 +45102,11 @@
"type": "string",
"format": "int64",
"title": "undelegation_filter_height records the height before which undelegations are not slashed"
},
"historical_voting_power": {
"type": "string",
"format": "int64",
"description": "the historical voting power at the time of the slash event."
}
}
}
Expand Down Expand Up @@ -45495,6 +45515,11 @@
"type": "string",
"format": "int64",
"title": "undelegation_filter_height records the height before which undelegations are not slashed"
},
"historical_voting_power": {
"type": "string",
"format": "int64",
"description": "the historical voting power at the time of the slash event."
}
},
"title": "SlashExecutionInfo is the actual execution state for a slash event"
Expand Down
8 changes: 4 additions & 4 deletions proto/exocore/assets/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ message QueryAllClientChainInfoResponse {
// QueryStakingAssetInfo is the query for getting the staking asset info.
message QueryStakingAssetInfo {
// asset_id is the asset for which the query is made.
string asset_id = 1 ;
string asset_id = 1;
}

// QueryAllStakingAssetsInfo is the query for getting all staking assets info.
Expand All @@ -51,7 +51,7 @@ message QueryAllStakingAssetsInfoResponse {
// QueryStakerAssetInfo is the query for getting the staker asset info.
message QueryStakerAssetInfo {
// stake_id is the staker id for which the query is made.
string staker_id = 1 ;
string staker_id = 1;
}

// QueryAssetInfoResponse is the response for the staker asset info.
Expand All @@ -63,9 +63,9 @@ message QueryAssetInfoResponse {
// QuerySpecifiedAssetAmountReq is the query for getting the staker specified asset amount.
message QuerySpecifiedAssetAmountReq {
// staker_id is the staker id for which the query is made.
string staker_id = 1 ;
string staker_id = 1;
// asset_id is the asset for which the query is made.
string asset_id = 2 ;
string asset_id = 2;
}

// QueryOperatorAssetInfos is the query for getting the operator asset info.
Expand Down
2 changes: 1 addition & 1 deletion proto/exocore/delegation/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ message UndelegationRecord {
// It will be included in the key when storing the undelegation.
uint64 undelegation_id = 8;
// amount is the amount of the asset to be undelegated.
string amount = 9[
string amount = 9 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
Expand Down
2 changes: 2 additions & 0 deletions proto/exocore/operator/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ message SlashExecutionInfo {
repeated SlashFromAssetsPool slash_assets_pool = 4 [(gogoproto.nullable) = false];
// undelegation_filter_height records the height before which undelegations are not slashed
int64 undelegation_filter_height = 5;
// the historical voting power at the time of the slash event.
int64 historical_voting_power = 6;
}

// OperatorSlashInfo is the slash info of operator
Expand Down
18 changes: 18 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"bytes"
"encoding/binary"
"sort"
"strings"

Expand Down Expand Up @@ -220,3 +221,20 @@ func AppendMany(byteses ...[]byte) (out []byte) {
}
return out
}

// Uint32ToBigEndian - marshals uint32 to a bigendian byte slice so it can be sorted
func Uint32ToBigEndian(i uint32) []byte {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, i)
return b
}

// BigEndianToUint32 returns an uint32 from big endian encoded bytes. If encoding
// is empty, zero is returned.
func BigEndianToUint32(bz []byte) uint32 {
if len(bz) == 0 {
return 0
}

return binary.BigEndian.Uint32(bz)
}
1 change: 1 addition & 0 deletions x/operator/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func (k *Keeper) QueryOperatorSlashInfo(goCtx context.Context, req *types.QueryO

slashPrefix := utils.AppendMany(types.KeyPrefixOperatorSlashInfo, assetstype.GetJoinedStoreKeyForPrefix(req.OperatorAddr, strings.ToLower(req.AvsAddress)))
store := prefix.NewStore(ctx.KVStore(k.storeKey), slashPrefix)

pageRes, err := query.Paginate(store, req.Pagination, func(key []byte, value []byte) error {
ret := &types.OperatorSlashInfo{}
// don't use MustUnmarshal to not panic for queries
Expand Down
18 changes: 13 additions & 5 deletions x/operator/keeper/slash.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package keeper

import (
"strings"
"encoding/json"

"github.com/ExocoreNetwork/exocore/utils"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
Expand All @@ -18,8 +18,10 @@ import (

// GetSlashIDForDogfood It use infractionType+'_'+'infractionHeight' as the slashID, because /* the slash */event occurs in dogfood doesn't have a TxID. It isn't submitted through an external transaction.
func GetSlashIDForDogfood(infraction stakingtypes.Infraction, infractionHeight int64) string {
// #nosec G701
return strings.Join([]string{hexutil.EncodeUint64(uint64(infraction)), hexutil.EncodeUint64(uint64(infractionHeight))}, utils.DelimiterForID)
slashIDBytes := utils.AppendMany(
utils.Uint32ToBigEndian(uint32(infraction)),
sdk.Uint64ToBigEndian(uint64(infractionHeight)))
return hexutil.Encode(slashIDBytes)
}

// SlashFromUndelegation executes the slash from an undelegation, reduce the .ActualCompletedAmount from undelegationRecords
Expand Down Expand Up @@ -87,6 +89,7 @@ func (k *Keeper) SlashAssets(ctx sdk.Context, snapshotHeight int64, parameter *t
SlashUndelegations: make([]types.SlashFromUndelegation, 0),
SlashAssetsPool: make([]types.SlashFromAssetsPool, 0),
UndelegationFilterHeight: snapshotHeight,
HistoricalVotingPower: parameter.Power,
}
// slash from the unbonding stakers
if parameter.SlashEventHeight < ctx.BlockHeight() {
Expand Down Expand Up @@ -155,10 +158,15 @@ func (k *Keeper) Slash(ctx sdk.Context, parameter *types.SlashInputInfo) error {
if err != nil {
return err
}
snapshotKeyLastHeight, snapshot, err := k.LoadVotingPowerSnapshot(ctx, parameter.AVSAddr, parameter.SlashEventHeight)
slashEventEpochStartHeight, snapshot, err := k.LoadVotingPowerSnapshot(ctx, parameter.AVSAddr, parameter.SlashEventHeight)
if err != nil {
return err
}
k.Logger(ctx).Info("execute slashing", "eventHeight", parameter.SlashEventHeight, "avsAddr", parameter.AVSAddr, "operator", parameter.Operator, "slashID", parameter.SlashID, "slashType", parameter.SlashType)
// Marshal the snapshot to improve the user experience when printing the voting power decimal through the logger
// so we don't have to address the error here.
snapshotJSON, _ := json.Marshal(snapshot)
k.Logger(ctx).Info("the voting power snapshot info is:", "filter_height", slashEventEpochStartHeight, "snapshot", string(snapshotJSON))
// get the historical voting power from the snapshot for the other AVSs
if !parameter.IsDogFood {
votingPower := types.GetSpecifiedVotingPower(parameter.Operator.String(), snapshot.OperatorVotingPowers)
Expand All @@ -178,7 +186,7 @@ func (k *Keeper) Slash(ctx sdk.Context, parameter *types.SlashInputInfo) error {
// slash assets according to the input information
// using cache context to ensure the atomicity of slash execution.
cc, writeFunc := ctx.CacheContext()
executionInfo, err := k.SlashAssets(cc, snapshotKeyLastHeight, parameter)
executionInfo, err := k.SlashAssets(cc, slashEventEpochStartHeight, parameter)
if err != nil {
return err
}
Expand Down
8 changes: 5 additions & 3 deletions x/operator/keeper/usd_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ func (k *Keeper) CalculateUSDValueForOperator(
return err
}
decimal = assetInfo.AssetBasicInfo.Decimals
ret.StakingAndWaitUnbonding = ret.StakingAndWaitUnbonding.Add(CalculateUSDValue(state.TotalAmount.Add(state.PendingUndelegationAmount), price.Value, decimal, price.Decimal))
usdValue := CalculateUSDValue(state.TotalAmount.Add(state.PendingUndelegationAmount), price.Value, decimal, price.Decimal)
ctx.Logger().Info("CalculateUSDValueForOperator: get price for slash", "assetID", assetID, "assetDecimal", decimal, "price", price, "totalAmount", state.TotalAmount, "pendingUndelegationAmount", state.PendingUndelegationAmount, "StakingAndWaitUnbonding", ret.StakingAndWaitUnbonding, "addUSDValue", usdValue)
ret.StakingAndWaitUnbonding.AddMut(usdValue)
} else {
if prices == nil {
return errorsmod.Wrap(operatortypes.ErrValueIsNilOrZero, "CalculateUSDValueForOperator prices map is nil")
Expand All @@ -403,13 +405,13 @@ func (k *Keeper) CalculateUSDValueForOperator(
if !ok {
return errorsmod.Wrap(operatortypes.ErrKeyNotExistInMap, "CalculateUSDValueForOperator map: decimals, key: assetID")
}
ret.Staking = ret.Staking.Add(CalculateUSDValue(state.TotalAmount, price.Value, decimal, price.Decimal))
ret.Staking.AddMut(CalculateUSDValue(state.TotalAmount, price.Value, decimal, price.Decimal))
// calculate the token amount from the share for the operator
selfAmount, err := delegationkeeper.TokensFromShares(state.OperatorShare, state.TotalShare, state.TotalAmount)
if err != nil {
return err
}
ret.SelfStaking = ret.SelfStaking.Add(CalculateUSDValue(selfAmount, price.Value, decimal, price.Decimal))
ret.SelfStaking.AddMut(CalculateUSDValue(selfAmount, price.Value, decimal, price.Decimal))
}
return nil
}
Expand Down
Loading

0 comments on commit 27161a9

Please sign in to comment.