2020-04-13 16:01:54 +00:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2022-01-08 00:39:27 +00:00
|
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
2020-04-13 16:01:54 +00:00
|
|
|
|
2024-05-01 03:17:24 +00:00
|
|
|
"github.com/0glabs/0g-chain/x/auction/types"
|
2020-04-13 16:01:54 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// RegisterInvariants registers all staking invariants
|
|
|
|
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
|
|
|
|
ir.RegisterRoute(types.ModuleName, "module-account",
|
|
|
|
ModuleAccountInvariants(k))
|
|
|
|
ir.RegisterRoute(types.ModuleName, "valid-auctions",
|
|
|
|
ValidAuctionInvariant(k))
|
|
|
|
ir.RegisterRoute(types.ModuleName, "valid-index",
|
|
|
|
ValidIndexInvariant(k))
|
|
|
|
}
|
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
// ModuleAccountInvariants checks that the module account's coins matches those stored in auctions
|
2020-04-13 16:01:54 +00:00
|
|
|
func ModuleAccountInvariants(k Keeper) sdk.Invariant {
|
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
|
|
|
totalAuctionCoins := sdk.NewCoins()
|
|
|
|
k.IterateAuctions(ctx, func(auction types.Auction) bool {
|
|
|
|
a, ok := auction.(types.GenesisAuction)
|
|
|
|
if !ok {
|
|
|
|
panic("stored auction type does not fulfill GenesisAuction interface")
|
|
|
|
}
|
2020-04-23 16:35:58 +00:00
|
|
|
totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...)
|
2020-04-13 16:01:54 +00:00
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
2022-01-08 00:39:27 +00:00
|
|
|
moduleAccCoins := k.bankKeeper.GetAllBalances(ctx, authtypes.NewModuleAddress(types.ModuleName))
|
2020-04-13 16:01:54 +00:00
|
|
|
broken := !moduleAccCoins.IsEqual(totalAuctionCoins)
|
|
|
|
|
|
|
|
invariantMessage := sdk.FormatInvariant(
|
|
|
|
types.ModuleName,
|
|
|
|
"module account",
|
|
|
|
fmt.Sprintf(
|
|
|
|
"\texpected ModuleAccount coins: %s\n"+
|
|
|
|
"\tactual ModuleAccount coins: %s\n",
|
|
|
|
totalAuctionCoins, moduleAccCoins),
|
|
|
|
)
|
|
|
|
return invariantMessage, broken
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidAuctionInvariant verifies that all auctions in the store are independently valid
|
|
|
|
func ValidAuctionInvariant(k Keeper) sdk.Invariant {
|
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
|
|
|
var validationErr error
|
|
|
|
var invalidAuction types.Auction
|
|
|
|
k.IterateAuctions(ctx, func(auction types.Auction) bool {
|
|
|
|
a, ok := auction.(types.GenesisAuction)
|
|
|
|
if !ok {
|
|
|
|
panic("stored auction type does not fulfill GenesisAuction interface")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.Validate(); err != nil {
|
|
|
|
validationErr = err
|
|
|
|
invalidAuction = a
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
broken := validationErr != nil
|
|
|
|
invariantMessage := sdk.FormatInvariant(
|
|
|
|
types.ModuleName,
|
|
|
|
"valid auctions",
|
|
|
|
fmt.Sprintf(
|
|
|
|
"\tfound invalid auction, reason: %s\n"+
|
|
|
|
"\tauction:\n\t%s\n",
|
|
|
|
validationErr, invalidAuction),
|
|
|
|
)
|
|
|
|
return invariantMessage, broken
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidIndexInvariant checks that all auctions in the store are also in the index and vice versa.
|
|
|
|
func ValidIndexInvariant(k Keeper) sdk.Invariant {
|
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
|
|
|
/* Method:
|
|
|
|
- check all the auction IDs in the index have a corresponding auction in the store
|
|
|
|
- index is now valid but there could be extra auction in the store
|
|
|
|
- check for these extra auctions by checking num items in the store equals that of index (store keys are always unique)
|
|
|
|
- doesn't check the IDs in the auction structs match the IDs in the keys
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Check all auction IDs in the index are in the auction store
|
|
|
|
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AuctionKeyPrefix)
|
|
|
|
|
|
|
|
indexIterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.AuctionByTimeKeyPrefix)
|
|
|
|
defer indexIterator.Close()
|
|
|
|
|
|
|
|
var indexLength int
|
|
|
|
for ; indexIterator.Valid(); indexIterator.Next() {
|
|
|
|
indexLength++
|
|
|
|
|
|
|
|
idBytes := indexIterator.Value()
|
|
|
|
auctionBytes := store.Get(idBytes)
|
|
|
|
if auctionBytes == nil {
|
|
|
|
invariantMessage := sdk.FormatInvariant(
|
|
|
|
types.ModuleName,
|
|
|
|
"valid index",
|
|
|
|
fmt.Sprintf("\tauction with ID '%d' found in index but not in store", types.Uint64FromBytes(idBytes)))
|
|
|
|
return invariantMessage, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check length of auction store matches the length of the index
|
|
|
|
storeIterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.AuctionKeyPrefix)
|
|
|
|
defer storeIterator.Close()
|
|
|
|
var storeLength int
|
|
|
|
for ; storeIterator.Valid(); storeIterator.Next() {
|
|
|
|
storeLength++
|
|
|
|
}
|
|
|
|
|
|
|
|
if storeLength != indexLength {
|
|
|
|
invariantMessage := sdk.FormatInvariant(
|
|
|
|
types.ModuleName,
|
|
|
|
"valid index",
|
|
|
|
fmt.Sprintf("\tmismatched number of items in auction store (%d) and index (%d)", storeLength, indexLength))
|
|
|
|
return invariantMessage, true
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
}
|