Skip to content

Commit

Permalink
add query clis, update ut, use 32 base to calculate balance change
Browse files Browse the repository at this point in the history
  • Loading branch information
leonz789 committed Sep 20, 2024
1 parent b968eeb commit d546ace
Show file tree
Hide file tree
Showing 16 changed files with 1,356 additions and 231 deletions.
5 changes: 4 additions & 1 deletion proto/exocore/oracle/v1/native_token.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "gogoproto/gogo.proto";

option go_package = "github.com/ExocoreNetwork/exocore/x/oracle/types";

// BalanceInfo tells effective-balance for native-restaking asset
message BalanceInfo {
uint64 round_id = 1 [(gogoproto.customname)="RoundID"];
uint64 block = 2;
Expand All @@ -25,13 +26,15 @@ message BalanceInfo {
Action change = 5;
}

// StakerInfo represents all related information for a staker of native-restaking
message StakerInfo {
string staker_addr = 1;
int64 staker_index = 2;
repeated uint64 validator_indexs = 3;
repeated uint64 validator_indexes = 3;
repeated BalanceInfo balance_list = 5;
}

// StakerList tells which stakers are active for one specific native-restaking asset
message StakerList {
repeated string staker_addrs = 1;
}
26 changes: 25 additions & 1 deletion proto/exocore/oracle/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ import "exocore/oracle/v1/prices.proto";
import "exocore/oracle/v1/recent_msg.proto";
import "exocore/oracle/v1/recent_params.proto";
import "exocore/oracle/v1/validator_update_block.proto";
import "exocore/oracle/v1/native_token.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";

option go_package = "github.com/ExocoreNetwork/exocore/x/oracle/types";

// Query defines the gRPC querier service.
service Query {

rpc StakerList (QueryStakerListRequest) returns (QueryStakerListResponse) {
option (google.api.http).get = "/ExocoreNetwork/exocore/oracle/staker_list";
}

rpc StakerInfos (QueryStakerInfosRequest) returns (QueryStakerInfosResponse) {
option (google.api.http).get = "/ExocoreNetwork/exocore/oracle/staker_infos";
}

// Parameters queries the parameters of the module.
rpc Params (QueryParamsRequest) returns (QueryParamsResponse) {
Expand Down Expand Up @@ -84,6 +93,22 @@ service Query {

}
}

message QueryStakerListRequest {
string asset_id = 1[(gogoproto.customname) = "AssetID"];
}

message QueryStakerListResponse {
StakerList staker_list = 1;
}

message QueryStakerInfosRequest {
string asset_id = 1[(gogoproto.customname) = "AssetID"];
}

message QueryStakerInfosResponse {
repeated StakerInfo staker_infos = 1;
}
// QueryParamsRequest is request type for the Query/Params RPC method.
message QueryParamsRequest {}

Expand Down Expand Up @@ -211,4 +236,3 @@ message QueryAllRecentParamsResponse {
// info of pagination
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

21 changes: 10 additions & 11 deletions x/assets/types/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,8 @@ const (

// TODO: update before merge
NativeETHAssetID = "0x01_0x01"
ETHAssetID = "0x02_0x01"
)

var NativeTokenBaseMap = map[string]string{
NativeETHAssetID: ETHAssetID,
}

const (
Deposit CrossChainOpType = iota
WithdrawPrincipal
Expand All @@ -46,6 +41,10 @@ const (
Slash
)

var NativeAssets = []string{
NativeETHAssetID,
}

var MaxAssetTotalSupply = math.NewIntWithDecimal(1, MaxDecForTotalSupply)

type GeneralAssetsAddr [32]byte
Expand Down Expand Up @@ -172,15 +171,15 @@ func UpdateAssetDecValue(valueToUpdate *math.LegacyDec, changeValue *math.Legacy
}

func IsNativeToken(assetID string) bool {
// TODO: native token should be a list which able to config, we currently only support ETH, so it's fine for now
return assetID == NativeETHAssetID
for _, aID := range NativeAssets {
if assetID == aID {
return true
}
}
return false
}

func GetNativeTokenAssetIDs() []string {
// TODO: we currently have native_eth only
return []string{NativeETHAssetID}
}

func GetBaseTokenForNativeToken(assetID string) string {
return NativeTokenBaseMap[assetID]
}
4 changes: 0 additions & 4 deletions x/operator/keeper/usd_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package keeper
import (
"errors"
"fmt"
"strings"

assetstype "github.com/ExocoreNetwork/exocore/x/assets/types"
delegationkeeper "github.com/ExocoreNetwork/exocore/x/delegation/keeper"
Expand Down Expand Up @@ -300,9 +299,6 @@ func (k *Keeper) CalculateUSDValueForOperator(
if isForSlash {
// when calculated the USD value for slashing, the input prices map is null
// so the price needs to be retrieved here
if assetstype.IsNativeToken(assetID) {
assetID = strings.Join([]string{assetID, operator}, "_")
}
price, err = k.oracleKeeper.GetSpecifiedAssetsPrice(ctx, assetID)
if err != nil {
// TODO: when assetID is not registered in oracle module, this error will finally lead to panic
Expand Down
2 changes: 2 additions & 0 deletions x/oracle/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func GetQueryCmd(_ string) *cobra.Command {
cmd.AddCommand(CmdShowRecentMsg())
cmd.AddCommand(CmdListRecentParams())
cmd.AddCommand(CmdShowRecentParams())
cmd.AddCommand(CmdQueryStakerInfos())
cmd.AddCommand(CmdQueryStakerList())
// this line is used by starport scaffolding # 1

return cmd
Expand Down
69 changes: 69 additions & 0 deletions x/oracle/client/cli/query_native_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cli

import (
"github.com/ExocoreNetwork/exocore/x/oracle/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/spf13/cobra"
)

func CmdQueryStakerInfos() *cobra.Command {
cmd := &cobra.Command{
Use: "show-staker-infos",
Short: "shows all staker infos including stakerAddr, validators of that staker, latest balance...",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

assetID := args[0]

request := &types.QueryStakerInfosRequest{
AssetID: assetID,
}

res, err := queryClient.StakerInfos(cmd.Context(), request)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

func CmdQueryStakerList() *cobra.Command {
cmd := &cobra.Command{
Use: "show-staker-list",
Short: "shows staker list including all staker addresses",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

res, err := queryClient.StakerList(cmd.Context(), &types.QueryStakerListRequest{})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd

}
57 changes: 46 additions & 11 deletions x/oracle/keeper/native_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
// undelegate: update operator's price, operator's totalAmount, operator's totalShare, staker's share
// msg(refund or slash on beaconChain): update staker's price, operator's price

const maxEffectiveBalance = 32

var stakerList types.StakerList

// GetStakerInfo returns details about staker for native-restaking under asset of assetID
Expand All @@ -30,9 +32,6 @@ func (k Keeper) GetStakerInfo(ctx sdk.Context, assetID, stakerAddr string) types
return stakerInfo
}

// TODO, NOTE: price changes will effect reward/slash calculation, every time one staker's price changed, it's reward/slash amount(LST) should be cleaned or recalculated immediately
// TODO: validatorIndex
// amount: represents for originalToken
func (k Keeper) UpdateNativeTokenByDepositOrWithdraw(ctx sdk.Context, assetID, stakerAddr string, amount sdkmath.Int, validatorIndex uint64) sdkmath.Int {
// emit an event to tell that a staker's validator list has changed
ctx.EventManager().EmitEvent(sdk.NewEvent(
Expand All @@ -49,7 +48,7 @@ func (k Keeper) UpdateNativeTokenByDepositOrWithdraw(ctx sdk.Context, assetID, s
k.cdc.MustUnmarshal(value, stakerInfo)
if amount.IsPositive() {
// deopsit add a new validator into staker's validatorList
stakerInfo.ValidatorIndexs = append(stakerInfo.ValidatorIndexs, validatorIndex)
stakerInfo.ValidatorIndexes = append(stakerInfo.ValidatorIndexes, validatorIndex)
}
}

Expand All @@ -68,9 +67,9 @@ func (k Keeper) UpdateNativeTokenByDepositOrWithdraw(ctx sdk.Context, assetID, s
// TODO: check if this validator has withdraw all its asset and then we can move it out from the staker's validatorList
// currently when withdraw happened we assume this validator has left the staker's validatorList (deposit/withdraw all of that validator's staking ETH(<=32))
newBalance.Change = types.BalanceInfo_ACTION_WITHDRAW
for i, vIdx := range stakerInfo.ValidatorIndexs {
for i, vIdx := range stakerInfo.ValidatorIndexes {
if vIdx == validatorIndex {
stakerInfo.ValidatorIndexs = append(stakerInfo.ValidatorIndexs[:i], stakerInfo.ValidatorIndexs[i+1:]...)
stakerInfo.ValidatorIndexes = append(stakerInfo.ValidatorIndexes[:i], stakerInfo.ValidatorIndexes[i+1:]...)
break
}
}
Expand Down Expand Up @@ -126,6 +125,23 @@ func (k Keeper) UpdateNativeTokenByDepositOrWithdraw(ctx sdk.Context, assetID, s
return amount
}

// GetStakerInfos returns all stakers information
func (k Keeper) GetStakerInfos(ctx sdk.Context, assetID string) (ret []*types.StakerInfo) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.NativeTokenStakerKeyPrefix(assetID))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
sInfo := types.StakerInfo{}
k.cdc.MustUnmarshal(iterator.Value(), &sInfo)
// keep only the latest effective-balance
sInfo.BalanceList = sInfo.BalanceList[:len(sInfo.BalanceList)-1]
// this is mainly used by price feeder, so we remove the stakerAddr to reduce the size of return value
sInfo.StakerAddr = ""
ret = append(ret, &sInfo)
}
return ret
}

// GetstakerList return stakerList for native-restaking asset of assetID
func (k Keeper) GetStakerList(ctx sdk.Context, assetID string) types.StakerList {
store := ctx.KVStore(k.storeKey)
Expand All @@ -152,7 +168,9 @@ func (k Keeper) UpdateNativeTokenByBalanceChange(ctx sdk.Context, assetID string
return err
}
store := ctx.KVStore(k.storeKey)
for stakerAddr, change := range stakerChanges {
for _, stakerAddr := range sl.StakerAddrs {
// if stakerAddr is not in stakerChanges, then the change would be set to 0 which is expected
change := stakerChanges[stakerAddr]
key := types.NativeTokenStakerKey(assetID, stakerAddr)
value := store.Get(key)
if value == nil {
Expand All @@ -172,11 +190,21 @@ func (k Keeper) UpdateNativeTokenByBalanceChange(ctx sdk.Context, assetID string
newBalance.Index = 0
}
newBalance.Change = types.BalanceInfo_ACTION_SLASH_REFUND
newBalance.Balance += int64(change)
// balance update are based on initial/max effective balance: 32
maxBalance := maxEffectiveBalance * (len(stakerInfo.ValidatorIndexes))
balance := maxBalance + change
if balance > maxBalance {
return errors.New("effective balance should never exceeds 32")
}
if delta := int64(balance) - newBalance.Balance; delta != 0 {
// TODO: call assetsmodule. func(k Keeper) UpdateNativeRestakingBalance(ctx sdk.Context, stakerID, assetID string, amount sdkmath.Int) error
_ = balance
newBalance.Balance = int64(balance)
}
// newBalance.Balance += int64(change)
stakerInfo.Append(&newBalance)
bz := k.cdc.MustMarshal(stakerInfo)
store.Set(key, bz)
// TODO: call assetsmodule. func(k Keeper) UpdateNativeRestakingBalance(ctx sdk.Context, stakerID, assetID string, amount sdkmath.Int) error
}
return nil
}
Expand All @@ -191,14 +219,21 @@ func (k Keeper) getStakerList(ctx sdk.Context, assetID string) types.StakerList

// parseBalanceChange parses rawData to details of amount change for all stakers relative to native restaking
func parseBalanceChange(rawData []byte, sl types.StakerList) (map[string]int, error) {
indexs := rawData[:32]
// eg. 0100-000011
// first part 0100 tells that the effective-balance of staker corresponding to index 2 in StakerList
// the lenft part 000011. we use the first 4 bits to tell the length of this number, and it shows as 1 here, the 5th bit is used to tell symbol of the number, 1 means negative, then we can get the abs number indicate by the length. It's -1 here, means effective-balane is 32-1 on beacon chain for now
// the first 32 bytes are information to indicates effective-balance of which staker has changed, 1 means changed, 0 means not. 32 bytes can represents changes for at most 256 stakers
indexes := rawData[:32]
// bytes after first 32 are details of effective-balance change for each staker which has been marked with 1 in the first 32 bytes, for those who are marked with 0 will just be ignored
// For each staker we support at most 256 validators to join, so the biggest effective-balance change we would have is 256*16, then we need 12 bits to represents the number for each staker. And for compression we use 4 bits to tell then length of bits without leading 0 this number has.
// Then with the symbol we need at most 17 bits for each staker's effective-balance change: 0000.0.0000-0000-0000 (the leading 0 will be ignored for the last 12 bits)
changes := rawData[32:]
index := -1
byteIndex := 0
bitOffset := 0
lengthBits := 5
stakerChanges := make(map[string]int)
for _, b := range indexs {
for _, b := range indexes {
for i := 7; i >= 0; i-- {
index++
if (b>>i)&1 == 1 {
Expand Down
Loading

0 comments on commit d546ace

Please sign in to comment.