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

feat(genesis export): genesis exporting for avs,distribution modules #209

Merged
merged 11 commits into from
Nov 6, 2024
2 changes: 1 addition & 1 deletion precompiles/avs/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ func (p Precompile) OperatorSubmitTask(
BlsSignature: resultParams.BlsSignature,
Phase: phaseEnum,
}
err := p.avsKeeper.SetTaskResultInfo(ctx, resultParams.CallerAddress.String(), result)
err := p.avsKeeper.SubmitTaskResult(ctx, resultParams.CallerAddress.String(), result)
if err != nil {
return nil, err
}
Expand Down
61 changes: 61 additions & 0 deletions proto/exocore/avs/v1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
syntax = "proto3";
package exocore.avs.v1;

import "exocore/avs/v1/tx.proto";
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
import "gogoproto/gogo.proto";

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

// GenesisState defines the avs module's state. It needs to encompass
// all of the state that is required to start the chain from the genesis
// or in the event of a restart.
message GenesisState {
// avs_infos is the list of registered avs infos,
// that are supported at chain genesis (or restart).
repeated AVSInfo avs_infos = 1 [(gogoproto.nullable) = false];
// task_infos is the tasks issued by avs owner, indexed by
// task address and task id
// that are supported at chain genesis (or restart).
repeated TaskInfo task_infos = 2 [(gogoproto.nullable) = false];
// bls_pub_keys is the list of operator pubKey info, indexed by operator address
// The struct is the `BlsPubKeyInfo`
// which contains blsPubKey, operator address.
repeated BlsPubKeyInfo bls_pub_keys = 3 [(gogoproto.nullable) = false];

// task_result_infos is the task result informations, indexed
// by the operator address ,task address and the task id. The struct is the `TaskResultInfo`
repeated TaskResultInfo task_result_infos = 4 [(gogoproto.nullable) = false];
// challenge_infos is the task challenge informations, indexed
// by the operator address ,task address and the task id. The struct is the `ChallengeInfo`
repeated ChallengeInfo challenge_infos = 5 [(gogoproto.nullable) = false];
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
// task_nums is the task id, indexed
// by the task address. The struct is the `TaskID`
repeated TaskID task_nums = 6 [(gogoproto.nullable) = false];
// chain_id_infos is the dogfood chain id informations, indexed
// by the avs address. The struct is the `ChainIDInfo`
repeated ChainIDInfo chain_id_infos = 7 [(gogoproto.nullable) = false];

}
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved

// TaskID is helper structure to store the task id information for the genesis state.
message TaskID {
// task_addr is the address of task as a hex string
string task_addr = 1;
// id of task.
uint64 task_id = 2;
}
// ChallengeInfo is helper structure to store the task challenge information for the genesis state.
message ChallengeInfo {
// key is used for storing the ChallengeInfos,
// which is a combination of the operator address ,task address and task id.
string key = 1;
// challenge_addr is the address of the challenger
string challenge_addr = 2;
}
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
// ChainIDInfo is helper structure to store the dogfood ChainID information for the genesis state.
message ChainIDInfo {
// avs_address is the address of avs as a hex string.
string avs_address = 1;
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
// chain_id is an optional parameter to specify the chain_id of the AVS, if any
string chain_id = 2;
}
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
59 changes: 59 additions & 0 deletions proto/exocore/feedistribution/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";
package exocore.feedistribution.v1;

import "amino/amino.proto";
import "exocore/feedistribution/v1/distribution.proto";
import "exocore/feedistribution/v1/params.proto";
import "gogoproto/gogo.proto";

Expand All @@ -14,4 +15,62 @@ message GenesisState {
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true
];
// fee_pool is the global fee pool for distribution.
// It holds decimal coins. Once whole those coins can be burned or distributed to the community pool.
FeePool fee_pool = 2 [(gogoproto.nullable) = false];
// validator_accumulated_commissions represents accumulated commission
// for a validator kept as a running counter, can be withdrawn at any time.
repeated ValidatorAccumulatedCommissions validator_accumulated_commissions = 3 [(gogoproto.nullable) = false];
// validator_current_rewards_list represents current rewards and current
// period for a validator kept as a running counter and incremented
// each block as long as the validator's tokens remain constant.
repeated ValidatorCurrentRewardsList validator_current_rewards_list = 4 [(gogoproto.nullable) = false];

// validator_outstanding_rewards_list represents outstanding (un-withdrawn) rewards
// for a validator inexpensive to track, allows simple sanity checks.
repeated ValidatorOutstandingRewardsList validator_outstanding_rewards_list = 5 [(gogoproto.nullable) = false];
// staker_outstanding_rewards_list represents outstanding (un-withdrawn) rewards
// for a staker inexpensive to track, allows simple sanity checks.
repeated StakerOutstandingRewardsList staker_outstanding_rewards_list = 6 [(gogoproto.nullable) = false];

}

// ValidatorAccumulatedCommissions is helper structure to store
// the validator accumulated commissions for the genesis state.
message ValidatorAccumulatedCommissions {
// val_addr is the address of validator
string val_addr = 1;

// ValidatorAccumulatedCommission represents accumulated commission
// for a validator kept as a running counter, can be withdrawn at any time.
ValidatorAccumulatedCommission commission = 2;
}
// ValidatorCurrentRewardsList is helper structure to store the validator current rewards for the genesis state.
message ValidatorCurrentRewardsList {
// val_addr is the address of validator
string val_addr = 1;

// ValidatorCurrentRewards represents current rewards and current
// period for a validator kept as a running counter and incremented
// each block as long as the validator's tokens remain constant.
ValidatorCurrentRewards current_rewards = 2;
}
// ValidatorOutstandingRewardsList is a helper structure to store
// the validator outstanding rewards for the genesis state.
message ValidatorOutstandingRewardsList {
// val_addr is the address of validator
string val_addr = 1;

// ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards
// for a validator inexpensive to track, allows simple sanity checks.
ValidatorOutstandingRewards outstanding_rewards = 2;
}
// StakerOutstandingRewardsList is helper structure to store the staker outstanding rewards for the genesis state.
message StakerOutstandingRewardsList {
// staker_addr is the address of staker
string staker_addr = 1;

// StakerOutstandingRewards represents outstanding (un-withdrawn) rewards
// for a staker inexpensive to track, allows simple sanity checks.
StakerOutstandingRewards staker_outstanding_rewards = 2;
}
2 changes: 1 addition & 1 deletion x/avs/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func QuerySubmitTaskResult() *cobra.Command {
Use: "SubmitTaskResult <task-address-in-hex> <task-id> <operator-addreess>",
Short: "Query the SubmitTaskResult by taskAddr taskID operatorAddr",
Long: "Query the currently submitted Task Result",
Example: "exocored query avs ChallengeInfo 0x96949787E6a209AFb4dE035754F79DC9982D3F2a 2 exo1mq6pj6f5tafmgkk6lehew5radfq3w20gpegzs5",
Example: "exocored query avs SubmitTaskResult 0x96949787E6a209AFb4dE035754F79DC9982D3F2a 2 exo1mq6pj6f5tafmgkk6lehew5radfq3w20gpegzs5",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
if !common.IsHexAddress(args[0]) {
Expand Down
22 changes: 22 additions & 0 deletions x/avs/keeper/avs.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ func (k Keeper) RegisterAVSWithChainID(
}
avsAddrStr := types.GenerateAVSAddr(params.ChainID)
avsAddr = common.HexToAddress(avsAddrStr)
// check that the AVS is registered
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
if isAvs, _ := k.IsAVS(ctx, avsAddrStr); isAvs {
return avsAddr, nil
}
defer func() {
if err == nil {
// store the reverse lookup from AVSAddress to ChainID
Expand Down Expand Up @@ -194,3 +198,21 @@ func (k Keeper) GetChainIDByAVSAddr(ctx sdk.Context, avsAddr string) (string, bo
}
return string(bz), true
}

func (k *Keeper) GetAllChainIDInfos(ctx sdk.Context) ([]types.ChainIDInfo, error) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixAVSAddressToChainID)
iterator := sdk.KVStorePrefixIterator(store, nil)
defer iterator.Close()

ret := make([]types.ChainIDInfo, 0)
for ; iterator.Valid(); iterator.Next() {
avsAddr := strings.ToLower(common.BytesToAddress(iterator.Key()).Hex())
MaxMustermann2 marked this conversation as resolved.
Show resolved Hide resolved
chainID := string(iterator.Value())

ret = append(ret, types.ChainIDInfo{
AvsAddress: avsAddr,
ChainId: chainID,
})
}
return ret, nil
}
99 changes: 89 additions & 10 deletions x/avs/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,104 @@
package keeper

import (
"strings"

errorsmod "cosmossdk.io/errors"
"github.com/ExocoreNetwork/exocore/x/avs/types"
abci "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
)

// InitGenesis initializes the module's state from a provided genesis state.
// Since this action typically occurs on chain starts, this function is allowed to panic.
func (k Keeper) InitGenesis(
ctx sdk.Context,
_ types.GenesisState,
) []abci.ValidatorUpdate {
func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add nil state validation.

The function should validate that the input state is not nil before processing.

Add validation at the start of the function:

 func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
+    if state.AvsInfos == nil || state.TaskInfos == nil || state.BlsPubKeys == nil ||
+       state.TaskNums == nil || state.TaskResultInfos == nil ||
+       state.ChallengeInfos == nil || state.ChainIdInfos == nil {
+        panic(errorsmod.Wrap(types.ErrInvalidGenesis, "nil state components"))
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) {
if state.AvsInfos == nil || state.TaskInfos == nil || state.BlsPubKeys == nil ||
state.TaskNums == nil || state.TaskResultInfos == nil ||
state.ChallengeInfos == nil || state.ChainIdInfos == nil {
panic(errorsmod.Wrap(types.ErrInvalidGenesis, "nil state components"))
}

// Store a lookup from codeHash to code. Since these are static parameters,
// such a lookup is stored at genesis and never updated.
k.evmKeeper.SetCode(ctx, types.ChainIDCodeHash.Bytes(), types.ChainIDCode)
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
return []abci.ValidatorUpdate{}
// Set all the avs infos
for _, avs := range state.AvsInfos {
avs.AvsAddress = strings.ToLower(avs.AvsAddress)
avs.TaskAddr = strings.ToLower(avs.TaskAddr)
avs.RewardAddr = strings.ToLower(avs.RewardAddr)
avs.SlashAddr = strings.ToLower(avs.SlashAddr)
err := k.SetAVSInfo(ctx, &avs) //nolint:gosec
if err != nil {
panic(errorsmod.Wrap(err, "failed to set all avs info"))
}
}
Comment on lines +19 to +28
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance address validation for AVS info.

While converting addresses to lowercase helps with case sensitivity, additional validation should be performed to ensure addresses are valid Ethereum addresses.

Consider adding address validation:

 for _, avs := range state.AvsInfos {
+    if !common.IsHexAddress(avs.AvsAddress) || !common.IsHexAddress(avs.TaskAddr) ||
+       !common.IsHexAddress(avs.RewardAddr) || !common.IsHexAddress(avs.SlashAddr) {
+        panic(errorsmod.Wrap(types.ErrInvalidAddress, "invalid address in AVS info"))
+    }
     avs.AvsAddress = strings.ToLower(avs.AvsAddress)
     avs.TaskAddr = strings.ToLower(avs.TaskAddr)
     avs.RewardAddr = strings.ToLower(avs.RewardAddr)
     avs.SlashAddr = strings.ToLower(avs.SlashAddr)

Committable suggestion skipped: line range outside the PR's diff.

// Set all the task infos
for _, elem := range state.TaskInfos {
elem.TaskContractAddress = strings.ToLower(elem.TaskContractAddress)
err := k.SetTaskInfo(ctx, &elem) //nolint:gosec
if err != nil {
panic(errorsmod.Wrap(err, "failed to set all task info"))
}
}
// Set all the bls infos
for _, elem := range state.BlsPubKeys {
err := k.SetOperatorPubKey(ctx, &elem) //nolint:gosec
if err != nil {
panic(errorsmod.Wrap(err, "failed to set all bls info"))
}
}
// Set all the taskNum infos
for _, elem := range state.TaskNums {
elem.TaskAddr = strings.ToLower(elem.TaskAddr)
k.SetTaskID(ctx, common.HexToAddress(elem.TaskAddr), elem.TaskId)
trestinlsd marked this conversation as resolved.
Show resolved Hide resolved
}
// Set all the task result infos
for _, elem := range state.TaskResultInfos {
elem.TaskContractAddress = strings.ToLower(elem.TaskContractAddress)
k.SetTaskResultInfo(ctx, &elem) //nolint:gosec
}
// Set all the task challenge infos
err := k.SetAllTaskChallengedInfo(ctx, state.ChallengeInfos)
if err != nil {
panic(errorsmod.Wrap(err, "failed to set all challenge info"))
}
// Set all the chainID infos
for _, elem := range state.ChainIdInfos {
elem.AvsAddress = strings.ToLower(elem.AvsAddress)
k.SetAVSAddrToChainID(ctx, common.HexToAddress(elem.AvsAddress), elem.ChainId)
}
}

// ExportGenesis returns the module's exported genesis
func (Keeper) ExportGenesis(sdk.Context) *types.GenesisState {
// TODO
return types.DefaultGenesis()
func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
res := types.GenesisState{}
var err error
res.AvsInfos, err = k.GetAllAVSInfos(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all avs infos"))
}
res.TaskInfos, err = k.GetAllTaskInfos(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all task infos").Error())
}

res.BlsPubKeys, err = k.GetAllBlsPubKeys(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all bls key info").Error())
}

res.TaskNums, err = k.GetAllTaskNums(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all TaskNums").Error())
}

res.TaskResultInfos, err = k.GetAllTaskResultInfos(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all TaskResultInfos").Error())
}

res.ChallengeInfos, err = k.GetAllChallengeInfos(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all ChallengeInfos").Error())
}

res.ChainIdInfos, err = k.GetAllChainIDInfos(ctx)
if err != nil {
panic(errorsmod.Wrap(err, "failed to get all ChainIdInfos").Error())
}

return &res
}
Loading
Loading