Skip to content

Commit

Permalink
refactor(delegation): validate + load genesis
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxMustermann2 committed Apr 3, 2024
1 parent df2a01e commit 59f7927
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 32 deletions.
35 changes: 25 additions & 10 deletions x/delegation/keeper/cross_chain_tx_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,28 @@ import (
}, nil
}*/

// DelegateTo : It doesn't need to check the active status of the operator in middlewares when delegating assets to the operator. This is because it adds assets to the operator's amount. But it needs to check if operator has been slashed or frozen.
func (k *Keeper) DelegateTo(ctx sdk.Context, params *delegationtype.DelegationOrUndelegationParams) error {
// DelegateTo : It doesn't need to check the active status of the operator in middlewares when
// delegating assets to the operator. This is because it adds assets to the operator's amount.
// But it needs to check if operator has been slashed or frozen.
func (k Keeper) DelegateTo(ctx sdk.Context, params *delegationtype.DelegationOrUndelegationParams) error {
return k.delegateTo(ctx, params, true)
}

// delegateTo is the internal private version of DelegateTo. if the notGenesis parameter is
// false, the operator keeper and the delegation hooks are not called.
func (k *Keeper) delegateTo(
ctx sdk.Context,
params *delegationtype.DelegationOrUndelegationParams,
notGenesis bool,
) error {
// check if the delegatedTo address is an operator
if !k.operatorKeeper.IsOperator(ctx, params.OperatorAddress) {
return errorsmod.Wrap(delegationtype.ErrOperatorNotExist, fmt.Sprintf("input operatorAddr is:%s", params.OperatorAddress))
}

// check if the operator has been slashed or frozen
if k.slashKeeper.IsOperatorFrozen(ctx, params.OperatorAddress) {
// skip the check if not genesis (or chain restart)
if notGenesis && k.slashKeeper.IsOperatorFrozen(ctx, params.OperatorAddress) {
return delegationtype.ErrOperatorIsFrozen
}

Expand Down Expand Up @@ -153,14 +166,16 @@ func (k *Keeper) DelegateTo(ctx sdk.Context, params *delegationtype.DelegationOr
if err != nil {
return err
}
// call operator module to bond the increased assets to the opted-in AVS
err = k.operatorKeeper.UpdateOptedInAssetsState(ctx, stakerID, assetID, params.OperatorAddress.String(), params.OpAmount)
if err != nil {
return err
}

// call the hooks registered by the other modules
k.Hooks().AfterDelegation(ctx, params.OperatorAddress)
if notGenesis {
// call operator module to bond the increased assets to the opted-in AVS
err = k.operatorKeeper.UpdateOptedInAssetsState(ctx, stakerID, assetID, params.OperatorAddress.String(), params.OpAmount)
if err != nil {
return err
}
// call the hooks registered by the other modules
k.Hooks().AfterDelegation(ctx, params.OperatorAddress)
}
return nil
}

Expand Down
35 changes: 33 additions & 2 deletions x/delegation/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
package keeper

import (
assetstype "github.com/ExocoreNetwork/exocore/x/assets/types"
delegationtype "github.com/ExocoreNetwork/exocore/x/delegation/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,
genState delegationtype.GenesisState,
gs delegationtype.GenesisState,
) []abci.ValidatorUpdate {
// TODO
for _, level1 := range gs.Delegations {
stakerID := level1.StakerID
// #nosec G703 // already validated
stakerAddress, lzID, _ := assetstype.ParseID(stakerID)
// we have checked IsHexAddress already
stakerAddressBytes := common.HexToAddress(stakerAddress)
for _, level2 := range level1.Delegations {
assetID := level2.AssetID
// #nosec G703 // already validated
assetAddress, _, _ := assetstype.ParseID(assetID)
// we have checked IsHexAddress already
assetAddressBytes := common.HexToAddress(assetAddress)
for operator, wrappedAmount := range level2.PerOperatorAmounts {
amount := wrappedAmount.Amount
// #nosec G703 // already validated
accAddress, _ := sdk.AccAddressFromBech32(operator)
delegationParams := &delegationtype.DelegationOrUndelegationParams{
ClientChainLzID: lzID,
Action: assetstype.DelegateTo,
AssetsAddress: assetAddressBytes.Bytes(),
OperatorAddress: accAddress,
StakerAddress: stakerAddressBytes.Bytes(),
OpAmount: amount,
}
if err := k.delegateTo(ctx, delegationParams, false); err != nil {
panic(err)
}
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
}
}
return []abci.ValidatorUpdate{}
}

Expand Down
49 changes: 29 additions & 20 deletions x/delegation/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ func (gs GenesisState) Validate() error {
for _, level1 := range gs.Delegations {
stakerID := level1.StakerID
// validate staker ID
if _, _, err := assetstypes.ValidateID(stakerID, true); err != nil {
return errorsmod.Wrapf(err, "invalid staker ID %s", stakerID)
var stakerClientChainID uint64
var err error
if _, stakerClientChainID, err = assetstypes.ValidateID(stakerID, true); err != nil {
return errorsmod.Wrapf(
ErrInvalidGenesisData, "invalid staker ID %s: %s", stakerID, err,
)
}
// check for duplicate stakers
if _, ok := stakers[stakerID]; ok {
Expand All @@ -38,23 +42,35 @@ func (gs GenesisState) Validate() error {
assets := make(map[string]struct{}, len(level1.Delegations))
for _, level2 := range level1.Delegations {
assetID := level2.AssetID
// validate asset ID
if _, _, err := assetstypes.ValidateID(assetID, true); err != nil {
return errorsmod.Wrapf(err, "invalid asset ID %s", assetID)
}
// check for duplicate assets
if _, ok := assets[assetID]; ok {
return errorsmod.Wrapf(ErrInvalidGenesisData, "duplicate asset ID %s", assetID)
}
assets[assetID] = struct{}{}
// validate asset ID
var assetClientChainID uint64
if _, assetClientChainID, err = assetstypes.ValidateID(assetID, true); err != nil {
return errorsmod.Wrapf(
ErrInvalidGenesisData, "invalid asset ID %s: %s", assetID, err,
)
}
if assetClientChainID != stakerClientChainID {
// a staker from chain A is delegating an asset on chain B, which is not
// something we support right now.
return errorsmod.Wrapf(
ErrInvalidGenesisData,
"asset %s client chain ID %d does not match staker %s client chain ID %d",
assetID, assetClientChainID, stakerID, stakerClientChainID,
)
}
givenTotal := level2.TotalDelegatedAmount
if givenTotal.IsNegative() || givenTotal.IsNil() {
// in this if condition, check nil first to avoid panic
if givenTotal.IsNil() || givenTotal.IsNegative() {
return errorsmod.Wrapf(
ErrInvalidGenesisData, "invalid total delegated amount %d", givenTotal,
ErrInvalidGenesisData, "invalid total delegated amount %s", givenTotal,
)
}
calculatedTotal := sdk.ZeroInt()
operators := make(map[string]struct{}, len(level2.PerOperatorAmounts))
for operator, wrappedAmount := range level2.PerOperatorAmounts {
// check supplied amount
if wrappedAmount == nil {
Expand All @@ -63,10 +79,10 @@ func (gs GenesisState) Validate() error {
)
}
amount := wrappedAmount.Amount
if amount.IsNegative() || amount.IsNil() {
if amount.IsNil() || amount.IsNegative() {
return errorsmod.Wrapf(
ErrInvalidGenesisData,
"invalid operator amount %d for operator %s", amount, operator,
"invalid operator amount %s for operator %s", amount, operator,
)
}
// check operator address
Expand All @@ -76,20 +92,13 @@ func (gs GenesisState) Validate() error {
"invalid operator address for operator %s", operator,
)
}
// check for duplicate operators
if _, ok := operators[operator]; ok {
return errorsmod.Wrapf(
ErrInvalidGenesisData,
"duplicate operator %s for asset %s", operator, assetID,
)
}
operators[operator] = struct{}{}
// no need to check for duplicate operators, since it is already a map.
calculatedTotal = calculatedTotal.Add(amount)
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
if !givenTotal.Equal(calculatedTotal) {
return errorsmod.Wrapf(
ErrInvalidGenesisData,
"total delegated amount %d does not match calculated total %d for asset %s",
"total delegated amount %s does not match calculated total %s for asset %s",
givenTotal, calculatedTotal, assetID,
)
}
Expand Down
Loading

0 comments on commit 59f7927

Please sign in to comment.