diff --git a/app/types/app.go b/app/types/app.go index 7f4633c13e..da52c960df 100644 --- a/app/types/app.go +++ b/app/types/app.go @@ -482,10 +482,10 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino ibctable := ibcclienttypes.ParamKeyTable() ibctable.RegisterParamSet(&ibcconnectiontypes.Params{}) - paramsKeeper.Subspace(authtypes.ModuleName).WithKeyTable(authtypes.ParamKeyTable()) // nolint: staticcheck - paramsKeeper.Subspace(banktypes.ModuleName).WithKeyTable(banktypes.ParamKeyTable()) // nolint: staticcheck // SA1019 - paramsKeeper.Subspace(stakingtypes.ModuleName).WithKeyTable(stakingtypes.ParamKeyTable()) // nolint: staticcheck // SA1019 - paramsKeeper.Subspace(minttypes.ModuleName).WithKeyTable(minttypes.ParamKeyTable()) // nolint: staticcheck // SA1019 + paramsKeeper.Subspace(authtypes.ModuleName).WithKeyTable(authtypes.ParamKeyTable()) // nolint: staticcheck + paramsKeeper.Subspace(banktypes.ModuleName).WithKeyTable(banktypes.ParamKeyTable()) // nolint: staticcheck // SA1019 + paramsKeeper.Subspace(stakingtypes.ModuleName).WithKeyTable(stakingtypes.ParamKeyTable()) // nolint: staticcheck // SA1019 + //paramsKeeper.Subspace(minttypes.ModuleName).WithKeyTable(minttypes.ParamKeyTable()) // nolint: staticcheck // SA1019 paramsKeeper.Subspace(distrtypes.ModuleName).WithKeyTable(distrtypes.ParamKeyTable()) // nolint: staticcheck // SA1019 paramsKeeper.Subspace(slashingtypes.ModuleName).WithKeyTable(slashingtypes.ParamKeyTable()) // nolint: staticcheck // SA1019 paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govv1.ParamKeyTable()) // nolint: staticcheck // SA1019 diff --git a/go.mod b/go.mod index 568325f103..10b97aaec2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module pkg.akt.dev/node -go 1.25.4 +go 1.25.5 require ( cosmossdk.io/api v0.9.2 @@ -54,7 +54,7 @@ replace ( // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 - github.com/bytedance/sonic => github.com/bytedance/sonic v1.14.1 + github.com/bytedance/sonic => github.com/bytedance/sonic v1.14.2 // use akash fork of cometbft github.com/cometbft/cometbft => github.com/akash-network/cometbft v0.38.21-akash.1 @@ -113,7 +113,7 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.13.2 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect diff --git a/go.sum b/go.sum index aa5701aa7a..00be424ad8 100644 --- a/go.sum +++ b/go.sum @@ -1340,10 +1340,10 @@ github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/ github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= -github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -2183,6 +2183,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= diff --git a/make/test-upgrade.mk b/make/test-upgrade.mk index 487dc7c7e8..04faf20390 100644 --- a/make/test-upgrade.mk +++ b/make/test-upgrade.mk @@ -21,7 +21,7 @@ UPGRADE_FROM := $(shell cat $(ROOT_DIR)/meta.json | jq -r --arg name GENESIS_BINARY_VERSION := $(shell cat $(ROOT_DIR)/meta.json | jq -r --arg name $(UPGRADE_TO) '.upgrades[$$name].from_binary' | tr -d '\n') UPGRADE_BINARY_VERSION ?= local -SNAPSHOT_SOURCE ?= sandbox-2 +SNAPSHOT_SOURCE ?= sandbox ifeq ($(SNAPSHOT_SOURCE),mainnet) SNAPSHOT_NETWORK := akashnet-2 @@ -29,9 +29,6 @@ ifeq ($(SNAPSHOT_SOURCE),mainnet) else ifeq ($(SNAPSHOT_SOURCE),sandbox) SNAPSHOT_NETWORK := sandbox-2 CHAIN_METADATA_URL := https://raw.githubusercontent.com/akash-network/net/master/sandbox-2/meta.json -else ifeq ($(SNAPSHOT_SOURCE),sandbox1) - SNAPSHOT_NETWORK := sandbox-01 - CHAIN_METADATA_URL := https://raw.githubusercontent.com/akash-network/net/master/sandbox/meta.json else $(error "invalid snapshot source $(SNAPSHOT_SOURCE)") endif diff --git a/meta.json b/meta.json index d80c0038d2..f236dd16ed 100644 --- a/meta.json +++ b/meta.json @@ -49,6 +49,11 @@ "skipped": false, "from_binary": "v1.0.4", "from_version": "v1.0.0" + }, + "v1.2.0": { + "skipped": false, + "from_binary": "v1.1.1", + "from_version": "v1.1.0" } } } diff --git a/tests/upgrade/test-cases.json b/tests/upgrade/test-cases.json index 3d86cdca02..35cc0423e5 100644 --- a/tests/upgrade/test-cases.json +++ b/tests/upgrade/test-cases.json @@ -1,4 +1,16 @@ { + "v1.2.0": { + "modules": { + }, + "migrations": { + "market": [ + { + "from": "7", + "to": "8" + } + ] + } + }, "v1.1.0": { "modules": { }, diff --git a/upgrades/software/v1.0.0/market.go b/upgrades/software/v1.0.0/market.go index 6274f2caf0..fdc5fce64a 100644 --- a/upgrades/software/v1.0.0/market.go +++ b/upgrades/software/v1.0.0/market.go @@ -35,41 +35,6 @@ func (m marketMigrations) handler(ctx sdk.Context) error { cdc := m.Codec() - // order prefix does not change in this upgrade - oiter := storetypes.KVStorePrefixIterator(store, mkeys.OrderPrefix) - defer func() { - _ = oiter.Close() - }() - - var ordersTotal uint64 - var ordersOpen uint64 - var ordersActive uint64 - var ordersClosed uint64 - - for ; oiter.Valid(); oiter.Next() { - nVal := migrate.OrderFromV1beta4(cdc, oiter.Value()) - - switch nVal.State { - case mv1beta.OrderOpen: - ordersOpen++ - case mv1beta.OrderActive: - ordersActive++ - case mv1beta.OrderClosed: - ordersClosed++ - default: - return fmt.Errorf("unknown order state %d", nVal.State) - } - - ordersTotal++ - - bz := cdc.MustMarshal(&nVal) - - store.Delete(oiter.Key()) - - key := mkeys.MustOrderKey(mkeys.OrderStateToPrefix(nVal.State), nVal.ID) - store.Set(key, bz) - } - // bid prefixes do not change in this upgrade store.Delete(mkeys.BidPrefixReverse) biter := storetypes.KVStorePrefixIterator(store, mkeys.BidPrefix) @@ -175,24 +140,6 @@ func (m marketMigrations) handler(ctx sdk.Context) error { store.Set(revKey, data) } } - ctx.Logger().Info(fmt.Sprintf("[upgrade %s]: updated x/market store keys:"+ - "\n\torders total: %d"+ - "\n\torders open: %d"+ - "\n\torders active: %d"+ - "\n\torders closed: %d"+ - "\n\tbids total: %d"+ - "\n\tbids open: %d"+ - "\n\tbids active: %d"+ - "\n\tbids lost: %d"+ - "\n\tbids closed: %d"+ - "\n\tleases total: %d"+ - "\n\tleases active: %d"+ - "\n\tleases insufficient funds: %d"+ - "\n\tleases closed: %d", - UpgradeName, - ordersTotal, ordersOpen, ordersActive, ordersClosed, - bidsTotal, bidsOpen, bidsActive, bidsLost, bidsClosed, - leasesTotal, leasesActive, leasesInsufficientFunds, leasesClosed)) return nil } diff --git a/upgrades/software/v1.2.0/init.go b/upgrades/software/v1.2.0/init.go new file mode 100644 index 0000000000..f84d8783f1 --- /dev/null +++ b/upgrades/software/v1.2.0/init.go @@ -0,0 +1,15 @@ +// Package v1_2_0 +// nolint revive +package v1_2_0 + +import ( + mv1 "pkg.akt.dev/go/node/market/v1" + + utypes "pkg.akt.dev/node/upgrades/types" +) + +func init() { + utypes.RegisterUpgrade(UpgradeName, initUpgrade) + + utypes.RegisterMigration(mv1.ModuleName, 7, newMarketMigration) +} diff --git a/upgrades/software/v1.2.0/market.go b/upgrades/software/v1.2.0/market.go new file mode 100644 index 0000000000..a11aff4a3b --- /dev/null +++ b/upgrades/software/v1.2.0/market.go @@ -0,0 +1,148 @@ +// Package v1_2_0 +// nolint revive +package v1_2_0 + +import ( + "fmt" + + "cosmossdk.io/collections" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkmodule "github.com/cosmos/cosmos-sdk/types/module" + + mv1 "pkg.akt.dev/go/node/market/v1" + mtypes "pkg.akt.dev/go/node/market/v1beta5" + + utypes "pkg.akt.dev/node/upgrades/types" + mkeeper "pkg.akt.dev/node/x/market/keeper" + mkeys "pkg.akt.dev/node/x/market/keeper/keys" +) + +type marketMigrations struct { + utypes.Migrator +} + +func newMarketMigration(m utypes.Migrator) utypes.Migration { + return marketMigrations{Migrator: m} +} + +func (m marketMigrations) GetHandler() sdkmodule.MigrationHandler { + return m.handler +} + +// handler migrates market from version 7 to 8. +// Moves orders, bids, and leases from manual KVStore keys to collections.IndexedMap. +func (m marketMigrations) handler(ctx sdk.Context) error { + skey := m.StoreKey() + cdc := m.Codec() + store := ctx.KVStore(skey) + + // Build IndexedMaps locally (same construction as NewKeeper) + ssvc := runtime.NewKVStoreService(skey.(*storetypes.KVStoreKey)) + sb := collections.NewSchemaBuilder(ssvc) + + orderIndexes := mkeeper.NewOrderIndexes(sb) + orders := collections.NewIndexedMap(sb, collections.NewPrefix(mkeys.OrderPrefixNew), "orders", mkeys.OrderPrimaryKeyCodec, codec.CollValue[mtypes.Order](cdc), orderIndexes) + + bidIndexes := mkeeper.NewBidIndexes(sb) + bids := collections.NewIndexedMap(sb, collections.NewPrefix(mkeys.BidPrefixNew), "bids", mkeys.BidPrimaryKeyCodec, codec.CollValue[mtypes.Bid](cdc), bidIndexes) + + leaseIndexes := mkeeper.NewLeaseIndexes(sb) + leases := collections.NewIndexedMap(sb, collections.NewPrefix(mkeys.LeasePrefixNew), "leases", mkeys.LeasePrimaryKeyCodec, codec.CollValue[mv1.Lease](cdc), leaseIndexes) + + if _, err := sb.Build(); err != nil { + return err + } + + // === Orders === + oiter := storetypes.KVStorePrefixIterator(store, mkeys.OrderPrefix) + defer func() { + _ = oiter.Close() + }() + + var orderCount int64 + var bidCount int64 + var leaseCount int64 + + for ; oiter.Valid(); oiter.Next() { + var order mtypes.Order + cdc.MustUnmarshal(oiter.Value(), &order) + + pk := mkeys.OrderIDToKey(order.ID) + if err := orders.Set(ctx, pk, order); err != nil { + return fmt.Errorf("failed to migrate order %s: %w", order.ID, err) + } + + store.Delete(oiter.Key()) + + orderCount++ + } + + // === Bids === + biter := storetypes.KVStorePrefixIterator(store, mkeys.BidPrefix) + defer func() { + _ = biter.Close() + }() + + for ; biter.Valid(); biter.Next() { + var bid mtypes.Bid + cdc.MustUnmarshal(biter.Value(), &bid) + + pk := mkeys.BidIDToKey(bid.ID) + if err := bids.Set(ctx, pk, bid); err != nil { + return fmt.Errorf("failed to migrate bid %s: %w", bid.ID, err) + } + + store.Delete(biter.Key()) + bidCount++ + } + + // Delete old bid reverse keys + brevIter := storetypes.KVStorePrefixIterator(store, mkeys.BidPrefixReverse) + defer func() { + _ = brevIter.Close() + }() + + for ; brevIter.Valid(); brevIter.Next() { + store.Delete(brevIter.Key()) + } + + // === Leases === + liter := storetypes.KVStorePrefixIterator(store, mkeys.LeasePrefix) + defer func() { + _ = liter.Close() + }() + + for ; liter.Valid(); liter.Next() { + var lease mv1.Lease + cdc.MustUnmarshal(liter.Value(), &lease) + + pk := mkeys.LeaseIDToKey(lease.ID) + if err := leases.Set(ctx, pk, lease); err != nil { + return fmt.Errorf("failed to migrate lease %s: %w", lease.ID, err) + } + + store.Delete(liter.Key()) + leaseCount++ + } + + // Delete old lease reverse keys + lrevIter := storetypes.KVStorePrefixIterator(store, mkeys.LeasePrefixReverse) + defer func() { + _ = lrevIter.Close() + }() + + for ; lrevIter.Valid(); lrevIter.Next() { + store.Delete(lrevIter.Key()) + } + + ctx.Logger().Info("market store migration complete", + "orders_migrated", orderCount, + "bids_migrated", bidCount, + "leases_migrated", leaseCount, + ) + + return nil +} diff --git a/upgrades/software/v1.2.0/upgrade.go b/upgrades/software/v1.2.0/upgrade.go new file mode 100644 index 0000000000..54e2c0644e --- /dev/null +++ b/upgrades/software/v1.2.0/upgrade.go @@ -0,0 +1,53 @@ +// Package v1_2_0 +// nolint revive +package v1_2_0 + +import ( + "context" + "fmt" + + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/types/module" + + apptypes "pkg.akt.dev/node/app/types" + utypes "pkg.akt.dev/node/upgrades/types" +) + +const ( + UpgradeName = "v1.2.0" +) + +type upgrade struct { + *apptypes.App + log log.Logger +} + +var _ utypes.IUpgrade = (*upgrade)(nil) + +func initUpgrade(log log.Logger, app *apptypes.App) (utypes.IUpgrade, error) { + up := &upgrade{ + App: app, + log: log.With("module", fmt.Sprintf("upgrade/%s", UpgradeName)), + } + + return up, nil +} + +func (up *upgrade) StoreLoader() *storetypes.StoreUpgrades { + return &storetypes.StoreUpgrades{} +} + +func (up *upgrade) UpgradeHandler() upgradetypes.UpgradeHandler { + return func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + toVM, err := up.MM.RunMigrations(ctx, up.Configurator, fromVM) + if err != nil { + return nil, err + } + + up.log.Info(fmt.Sprintf("all migrations have been completed")) + + return toVM, err + } +} diff --git a/upgrades/upgrades.go b/upgrades/upgrades.go index 01852a09d7..eae515720c 100644 --- a/upgrades/upgrades.go +++ b/upgrades/upgrades.go @@ -2,5 +2,5 @@ package upgrades import ( // nolint: revive - _ "pkg.akt.dev/node/upgrades/software/v1.1.0" + _ "pkg.akt.dev/node/upgrades/software/v1.2.0" ) diff --git a/x/market/genesis.go b/x/market/genesis.go index 99485cd201..4787da5653 100644 --- a/x/market/genesis.go +++ b/x/market/genesis.go @@ -33,50 +33,48 @@ func DefaultGenesisState() *v1beta5.GenesisState { // InitGenesis initiate genesis state and return updated validator details func InitGenesis(ctx sdk.Context, kpr keeper.IKeeper, data *v1beta5.GenesisState) { - store := ctx.KVStore(kpr.StoreKey()) - cdc := kpr.Codec() + concreteKeeper := kpr.(*keeper.Keeper) for _, record := range data.Orders { - key := keys.MustOrderKey(keys.OrderStateToPrefix(record.State), record.ID) - - if store.Has(key) { + pk := keys.OrderIDToKey(record.ID) + has, err := concreteKeeper.Orders().Has(ctx, pk) + if err != nil { + panic(fmt.Errorf("market genesis orders init. order id %s: %w", record.ID, err)) + } + if has { panic(fmt.Errorf("market genesis orders init. order id %s: %w", record.ID, v1.ErrOrderExists)) } - - store.Set(key, cdc.MustMarshal(&record)) + if err := concreteKeeper.Orders().Set(ctx, pk, record); err != nil { + panic(fmt.Errorf("market genesis orders init. order id %s: %w", record.ID, err)) + } } for _, record := range data.Bids { - key := keys.MustBidKey(keys.BidStateToPrefix(record.State), record.ID) - revKey := keys.MustBidReverseKey(keys.BidStateToPrefix(record.State), record.ID) - - if store.Has(key) { + pk := keys.BidIDToKey(record.ID) + has, err := concreteKeeper.Bids().Has(ctx, pk) + if err != nil { + panic(fmt.Errorf("market genesis bids init. bid id %s: %w", record.ID, err)) + } + if has { panic(fmt.Errorf("market genesis bids init. bid id %s: %w", record.ID, v1.ErrBidExists)) } - if store.Has(revKey) { - panic(fmt.Errorf("market genesis bids init. reverse key for bid id %s: %w", record.ID, v1.ErrBidExists)) + if err := concreteKeeper.Bids().Set(ctx, pk, record); err != nil { + panic(fmt.Errorf("market genesis bids init. bid id %s: %w", record.ID, err)) } - - data := cdc.MustMarshal(&record) - store.Set(key, data) - store.Set(revKey, data) } for _, record := range data.Leases { - key := keys.MustLeaseKey(keys.LeaseStateToPrefix(record.State), record.ID) - revKey := keys.MustLeaseReverseKey(keys.LeaseStateToPrefix(record.State), record.ID) - - if store.Has(key) { + pk := keys.LeaseIDToKey(record.ID) + has, err := concreteKeeper.Leases().Has(ctx, pk) + if err != nil { + panic(fmt.Errorf("market genesis leases init. lease id %s: %w", record.ID, err)) + } + if has { panic(fmt.Errorf("market genesis leases init. lease id %s: lease exists", record.ID)) } - - if store.Has(revKey) { - panic(fmt.Errorf("market genesis leases init. reverse key for lease id %s: lease exists", record.ID)) + if err := concreteKeeper.Leases().Set(ctx, pk, record); err != nil { + panic(fmt.Errorf("market genesis leases init. lease id %s: %w", record.ID, err)) } - - data := cdc.MustMarshal(&record) - store.Set(key, data) - store.Set(revKey, data) } err := kpr.SetParams(ctx, data.Params) diff --git a/x/market/keeper/grpc_query.go b/x/market/keeper/grpc_query.go index 4eb06ac8ff..8f5ba7dc82 100644 --- a/x/market/keeper/grpc_query.go +++ b/x/market/keeper/grpc_query.go @@ -6,15 +6,12 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "cosmossdk.io/store/prefix" + "cosmossdk.io/collections/indexes" sdk "github.com/cosmos/cosmos-sdk/types" sdkquery "github.com/cosmos/cosmos-sdk/types/query" "pkg.akt.dev/go/node/market/v1" types "pkg.akt.dev/go/node/market/v1beta5" - - "pkg.akt.dev/node/util/query" - "pkg.akt.dev/node/x/market/keeper/keys" ) // Querier is used as Keeper will have duplicate methods if used directly, and gRPC names take precedence over keeper @@ -32,125 +29,83 @@ func (k Querier) Orders(c context.Context, req *types.QueryOrdersRequest) (*type if req.Pagination == nil { req.Pagination = &sdkquery.PageRequest{} - } else if req.Pagination != nil && req.Pagination.Offset > 0 && req.Filters.State == "" { - return nil, status.Error(codes.InvalidArgument, "invalid request parameters. if offset is set, filter.state must be provided") } if req.Pagination.Limit == 0 { req.Pagination.Limit = sdkquery.DefaultLimit } - // case 1: no filters set, iterating over entire store - // case 2: state only or state plus underlying filters like owner, iterating over state store - // case 3: state not set, underlying filters like owner are set, most complex case - - states := make([]byte, 0, 3) - var searchPrefix []byte + if len(req.Pagination.Key) > 0 || req.Pagination.Reverse { + return nil, status.Error(codes.InvalidArgument, "key-based and reverse pagination are not supported") + } - // setup for case 3 - cross-index search - // nolint: gocritic - if len(req.Pagination.Key) > 0 { - var key []byte - var err error - states, searchPrefix, key, _, err = query.DecodePaginationKey(req.Pagination.Key) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + ctx := sdk.UnwrapSDKContext(c) - req.Pagination.Key = key - } else if req.Filters.State != "" { + // Determine which states to iterate + states := []types.Order_State{types.OrderOpen, types.OrderActive, types.OrderClosed} + if req.Filters.State != "" { stateVal := types.Order_State(types.Order_State_value[req.Filters.State]) - - if req.Filters.State != "" && stateVal == types.OrderStateInvalid { + if stateVal == types.OrderStateInvalid { return nil, status.Error(codes.InvalidArgument, "invalid state value") } - - states = append(states, byte(stateVal)) - } else { - // request does not have a pagination set. Start from an open store - states = append(states, []byte{byte(types.OrderOpen), byte(types.OrderActive), byte(types.OrderClosed)}...) + states = []types.Order_State{stateVal} } var orders types.Orders - var pageRes *sdkquery.PageResponse - - ctx := sdk.UnwrapSDKContext(c) - - total := uint64(0) - - var idx int - var err error - - for idx = range states { - state := types.Order_State(states[idx]) - if idx > 0 { - req.Pagination.Key = nil + limit := req.Pagination.Limit + offset := req.Pagination.Offset + skipped := uint64(0) + countTotal := req.Pagination.CountTotal + var total uint64 + + for _, state := range states { + if limit == 0 && !countTotal { + break } - if len(req.Pagination.Key) == 0 { - req.Filters.State = state.String() - - searchPrefix, err = keys.OrderPrefixFromFilter(req.Filters) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + iter, err := k.orders.Indexes.State.MatchExact(ctx, int32(state)) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) } - searchStore := prefix.NewStore(ctx.KVStore(k.skey), searchPrefix) - - count := uint64(0) - - pageRes, err = sdkquery.FilteredPaginate(searchStore, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) { - var order types.Order + err = indexes.ScanValues(ctx, k.orders, iter, func(order types.Order) bool { + if !req.Filters.Accept(order, state) { + return false + } - err := k.cdc.Unmarshal(value, &order) - if err != nil { - return false, err + if countTotal { + total++ } - // filter orders with provided filters - if req.Filters.Accept(order, state) { - if accumulate { - orders = append(orders, order) - count++ - } + if limit == 0 { + return !countTotal + } - return true, nil + if skipped < offset { + skipped++ + return false } - return false, nil + orders = append(orders, order) + limit-- + + return false }) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - - req.Pagination.Limit -= count - total += count - - if req.Pagination.Limit == 0 { - break - } } - if pageRes != nil { - pageRes.Total = total + resp := &types.QueryOrdersResponse{ + Orders: orders, + Pagination: &sdkquery.PageResponse{}, + } - if len(pageRes.NextKey) > 0 { - pageRes.NextKey, err = query.EncodePaginationKey(states[idx:], searchPrefix, pageRes.NextKey, nil) - if err != nil { - pageRes.Total = total - return &types.QueryOrdersResponse{ - Orders: orders, - Pagination: pageRes, - }, status.Error(codes.Internal, err.Error()) - } - } + if countTotal { + resp.Pagination.Total = total } - return &types.QueryOrdersResponse{ - Orders: orders, - Pagination: pageRes, - }, nil + return resp, nil } // Bids returns bids based on filters @@ -161,151 +116,154 @@ func (k Querier) Bids(c context.Context, req *types.QueryBidsRequest) (*types.Qu if req.Pagination == nil { req.Pagination = &sdkquery.PageRequest{} - } else if req.Pagination != nil && req.Pagination.Offset > 0 && req.Filters.State == "" { - return nil, status.Error(codes.InvalidArgument, "invalid request parameters. if offset is set, filter.state must be provided") } if req.Pagination.Limit == 0 { req.Pagination.Limit = sdkquery.DefaultLimit } - reverseSearch := false - states := make([]byte, 0, 4) - var searchPrefix []byte - - // setup for case 3 - cross-index search - // nolint: gocritic - if len(req.Pagination.Key) > 0 { - var key []byte - var unsolicited []byte - var err error - states, searchPrefix, key, unsolicited, err = query.DecodePaginationKey(req.Pagination.Key) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - - if len(unsolicited) != 1 { - return nil, status.Error(codes.InvalidArgument, "invalid pagination key") - } - req.Pagination.Key = key + if len(req.Pagination.Key) > 0 || req.Pagination.Reverse { + return nil, status.Error(codes.InvalidArgument, "key-based and reverse pagination are not supported") + } - if unsolicited[0] == 1 { - reverseSearch = true - } - } else if req.Filters.State != "" { - reverseSearch = (req.Filters.Owner == "") && (req.Filters.Provider != "") + ctx := sdk.UnwrapSDKContext(c) + // Determine which states to iterate + states := []types.Bid_State{types.BidOpen, types.BidActive, types.BidLost, types.BidClosed} + if req.Filters.State != "" { stateVal := types.Bid_State(types.Bid_State_value[req.Filters.State]) - - if req.Filters.State != "" && stateVal == types.BidStateInvalid { + if stateVal == types.BidStateInvalid { return nil, status.Error(codes.InvalidArgument, "invalid state value") } - - states = append(states, byte(stateVal)) - } else { - // request does not have a pagination set. Start from an open store - states = append(states, byte(types.BidOpen), byte(types.BidActive), byte(types.BidLost), byte(types.BidClosed)) + states = []types.Bid_State{stateVal} } var bids []types.QueryBidResponse - var pageRes *sdkquery.PageResponse - ctx := sdk.UnwrapSDKContext(c) - - total := uint64(0) - - var idx int - var err error - - for idx = range states { - state := types.Bid_State(states[idx]) - - if idx > 0 { - req.Pagination.Key = nil + limit := req.Pagination.Limit + offset := req.Pagination.Offset + skipped := uint64(0) + countTotal := req.Pagination.CountTotal + var total uint64 + + // Use Provider index when filtering by provider without owner + providerSearch := req.Filters.Owner == "" && req.Filters.Provider != "" + var acctErr error + + if providerSearch { + stateSet := make(map[types.Bid_State]bool) + for _, s := range states { + stateSet[s] = true } - if len(req.Pagination.Key) == 0 { - req.Filters.State = state.String() + iter, err := k.bids.Indexes.Provider.MatchExact(ctx, req.Filters.Provider) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } - if reverseSearch { - searchPrefix, err = keys.BidReversePrefixFromFilter(req.Filters) - } else { - searchPrefix, err = keys.BidPrefixFromFilter(req.Filters) + err = indexes.ScanValues(ctx, k.bids, iter, func(bid types.Bid) bool { + if !stateSet[bid.State] { + return false } - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) + if !req.Filters.Accept(bid, bid.State) { + return false } - } - count := uint64(0) - searchStore := prefix.NewStore(ctx.KVStore(k.skey), searchPrefix) - - pageRes, err = sdkquery.FilteredPaginate(searchStore, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) { - var bid types.Bid - - err := k.cdc.Unmarshal(value, &bid) - if err != nil { - return false, err + if countTotal { + total++ } - // filter bids with provided filters - if req.Filters.Accept(bid, state) { - if accumulate { - acct, err := k.ekeeper.GetAccount(ctx, bid.ID.ToEscrowAccountID()) - if err != nil { - return true, err - } - - bids = append(bids, types.QueryBidResponse{ - Bid: bid, - EscrowAccount: acct, - }) + if limit == 0 { + return !countTotal + } - count++ - } + if skipped < offset { + skipped++ + return false + } - return true, nil + acct, acctE := k.ekeeper.GetAccount(ctx, bid.ID.ToEscrowAccountID()) + if acctE != nil { + acctErr = acctE + return true } - return false, nil + bids = append(bids, types.QueryBidResponse{ + Bid: bid, + EscrowAccount: acct, + }) + limit-- + + return false }) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } + if acctErr != nil { + return nil, status.Error(codes.Internal, acctErr.Error()) + } + } else { + for _, state := range states { + if limit == 0 && !countTotal { + break + } - req.Pagination.Limit -= count - total += count + iter, err := k.bids.Indexes.State.MatchExact(ctx, int32(state)) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } - if req.Pagination.Limit == 0 { - break - } - } + err = indexes.ScanValues(ctx, k.bids, iter, func(bid types.Bid) bool { + if !req.Filters.Accept(bid, state) { + return false + } + + if countTotal { + total++ + } - if pageRes != nil { - pageRes.Total = total + if limit == 0 { + return !countTotal + } - if len(pageRes.NextKey) > 0 { - unsolicited := make([]byte, 1) - unsolicited[0] = 0 - if reverseSearch { - unsolicited[0] = 1 - } + if skipped < offset { + skipped++ + return false + } - pageRes.NextKey, err = query.EncodePaginationKey(states[idx:], searchPrefix, pageRes.NextKey, unsolicited) + acct, acctE := k.ekeeper.GetAccount(ctx, bid.ID.ToEscrowAccountID()) + if acctE != nil { + acctErr = acctE + return true + } + + bids = append(bids, types.QueryBidResponse{ + Bid: bid, + EscrowAccount: acct, + }) + limit-- + + return false + }) if err != nil { - pageRes.Total = total - return &types.QueryBidsResponse{ - Bids: bids, - Pagination: pageRes, - }, status.Error(codes.Internal, err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } + if acctErr != nil { + return nil, status.Error(codes.Internal, acctErr.Error()) } } } - return &types.QueryBidsResponse{ + resp := &types.QueryBidsResponse{ Bids: bids, - Pagination: pageRes, - }, nil + Pagination: &sdkquery.PageResponse{}, + } + + if countTotal { + resp.Pagination.Total = total + } + + return resp, nil } // Leases returns leases based on filters @@ -316,153 +274,154 @@ func (k Querier) Leases(c context.Context, req *types.QueryLeasesRequest) (*type if req.Pagination == nil { req.Pagination = &sdkquery.PageRequest{} - } else if req.Pagination != nil && req.Pagination.Offset > 0 && req.Filters.State == "" { - return nil, status.Error(codes.InvalidArgument, "invalid request parameters. if offset is set, filter.state must be provided") } if req.Pagination.Limit == 0 { req.Pagination.Limit = sdkquery.DefaultLimit } - // setup for case 3 - cross-index search - reverseSearch := false - states := make([]byte, 0, 3) - var searchPrefix []byte - - // setup for case 3 - cross-index search - // nolint: gocritic - if len(req.Pagination.Key) > 0 { - var key []byte - var unsolicited []byte - var err error - states, searchPrefix, key, unsolicited, err = query.DecodePaginationKey(req.Pagination.Key) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - - if len(unsolicited) != 1 { - return nil, status.Error(codes.InvalidArgument, "invalid pagination key") - } - req.Pagination.Key = key + if len(req.Pagination.Key) > 0 || req.Pagination.Reverse { + return nil, status.Error(codes.InvalidArgument, "key-based and reverse pagination are not supported") + } - if unsolicited[0] == 1 { - reverseSearch = true - } - } else if req.Filters.State != "" { - reverseSearch = (req.Filters.Owner == "") && (req.Filters.Provider != "") + ctx := sdk.UnwrapSDKContext(c) + // Determine which states to iterate + states := []v1.Lease_State{v1.LeaseActive, v1.LeaseInsufficientFunds, v1.LeaseClosed} + if req.Filters.State != "" { stateVal := v1.Lease_State(v1.Lease_State_value[req.Filters.State]) - - if req.Filters.State != "" && stateVal == v1.LeaseStateInvalid { + if stateVal == v1.LeaseStateInvalid { return nil, status.Error(codes.InvalidArgument, "invalid state value") } - - states = append(states, byte(stateVal)) - } else { - // request does not have a pagination set. Start from an open store - states = append(states, byte(v1.LeaseActive), byte(v1.LeaseInsufficientFunds), byte(v1.LeaseClosed)) + states = []v1.Lease_State{stateVal} } var leases []types.QueryLeaseResponse - var pageRes *sdkquery.PageResponse - ctx := sdk.UnwrapSDKContext(c) - - total := uint64(0) - - var idx int - var err error - - for idx = range states { - state := v1.Lease_State(states[idx]) - - if idx > 0 { - req.Pagination.Key = nil + limit := req.Pagination.Limit + offset := req.Pagination.Offset + skipped := uint64(0) + countTotal := req.Pagination.CountTotal + var total uint64 + + // Use Provider index when filtering by provider without owner + providerSearch := req.Filters.Owner == "" && req.Filters.Provider != "" + var pmntErr error + + if providerSearch { + stateSet := make(map[v1.Lease_State]bool) + for _, s := range states { + stateSet[s] = true } - if len(req.Pagination.Key) == 0 { - req.Filters.State = state.String() + iter, err := k.leases.Indexes.Provider.MatchExact(ctx, req.Filters.Provider) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } - if reverseSearch { - searchPrefix, err = keys.LeaseReversePrefixFromFilter(req.Filters) - } else { - searchPrefix, err = keys.LeasePrefixFromFilter(req.Filters) + err = indexes.ScanValues(ctx, k.leases, iter, func(lease v1.Lease) bool { + if !stateSet[lease.State] { + return false } - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) + if !req.Filters.Accept(lease, lease.State) { + return false } - } - - searchedStore := prefix.NewStore(ctx.KVStore(k.skey), searchPrefix) - count := uint64(0) - - pageRes, err = sdkquery.FilteredPaginate(searchedStore, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) { - var lease v1.Lease - - err := k.cdc.Unmarshal(value, &lease) - if err != nil { - return false, err + if countTotal { + total++ } - // filter leases with provided filters - if req.Filters.Accept(lease, state) { - if accumulate { - payment, err := k.ekeeper.GetPayment(ctx, lease.ID.ToEscrowPaymentID()) - if err != nil { - return true, err - } - - leases = append(leases, types.QueryLeaseResponse{ - Lease: lease, - EscrowPayment: payment, - }) + if limit == 0 { + return !countTotal + } - count++ - } + if skipped < offset { + skipped++ + return false + } - return true, nil + payment, pmntE := k.ekeeper.GetPayment(ctx, lease.ID.ToEscrowPaymentID()) + if pmntE != nil { + pmntErr = pmntE + return true } - return false, nil + leases = append(leases, types.QueryLeaseResponse{ + Lease: lease, + EscrowPayment: payment, + }) + limit-- + + return false }) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } + if pmntErr != nil { + return nil, status.Error(codes.Internal, pmntErr.Error()) + } + } else { + for _, state := range states { + if limit == 0 && !countTotal { + break + } + + iter, err := k.leases.Indexes.State.MatchExact(ctx, int32(state)) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } - req.Pagination.Limit -= count - total += count + err = indexes.ScanValues(ctx, k.leases, iter, func(lease v1.Lease) bool { + if !req.Filters.Accept(lease, state) { + return false + } - if req.Pagination.Limit == 0 { - break - } - } + if countTotal { + total++ + } - if pageRes != nil { - pageRes.Total = total + if limit == 0 { + return !countTotal + } - if len(pageRes.NextKey) > 0 { - unsolicited := make([]byte, 1) - unsolicited[0] = 0 - if reverseSearch { - unsolicited[0] = 1 - } + if skipped < offset { + skipped++ + return false + } - pageRes.NextKey, err = query.EncodePaginationKey(states[idx:], searchPrefix, pageRes.NextKey, unsolicited) + payment, pmntE := k.ekeeper.GetPayment(ctx, lease.ID.ToEscrowPaymentID()) + if pmntE != nil { + pmntErr = pmntE + return true + } + + leases = append(leases, types.QueryLeaseResponse{ + Lease: lease, + EscrowPayment: payment, + }) + limit-- + + return false + }) if err != nil { - pageRes.Total = total - return &types.QueryLeasesResponse{ - Leases: leases, - Pagination: pageRes, - }, status.Error(codes.Internal, err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } + if pmntErr != nil { + return nil, status.Error(codes.Internal, pmntErr.Error()) } } } - return &types.QueryLeasesResponse{ + resp := &types.QueryLeasesResponse{ Leases: leases, - Pagination: pageRes, - }, nil + Pagination: &sdkquery.PageResponse{}, + } + + if countTotal { + resp.Pagination.Total = total + } + + return resp, nil } // Order returns order details based on OrderID diff --git a/x/market/keeper/indexes.go b/x/market/keeper/indexes.go new file mode 100644 index 0000000000..545e833206 --- /dev/null +++ b/x/market/keeper/indexes.go @@ -0,0 +1,168 @@ +package keeper + +import ( + "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" + + mv1 "pkg.akt.dev/go/node/market/v1" + types "pkg.akt.dev/go/node/market/v1beta5" + + "pkg.akt.dev/node/x/market/keeper/keys" +) + +// OrderIndexes defines the secondary indexes for the order IndexedMap +type OrderIndexes struct { + // State indexes orders by their state (Open, Active, Closed) + State *indexes.Multi[int32, keys.OrderPrimaryKey, types.Order] + + // GroupState indexes orders by (owner, dseq, gseq, state) for WithOrdersForGroup queries + GroupState *indexes.Multi[collections.Pair[keys.GroupPartKey, int32], keys.OrderPrimaryKey, types.Order] +} + +// BidIndexes defines the secondary indexes for the bid IndexedMap +type BidIndexes struct { + // State indexes bids by their state (Open, Active, Lost, Closed) + State *indexes.Multi[int32, keys.BidPrimaryKey, types.Bid] + + // Provider indexes bids by provider address (covers all states) + Provider *indexes.Multi[string, keys.BidPrimaryKey, types.Bid] + + // OrderState indexes bids by (owner, dseq, gseq, oseq, state) for WithBidsForOrder queries + OrderState *indexes.Multi[collections.Pair[keys.OrderPrimaryKey, int32], keys.BidPrimaryKey, types.Bid] +} + +// LeaseIndexes defines the secondary indexes for the lease IndexedMap +type LeaseIndexes struct { + // State indexes leases by their state (Active, InsufficientFunds, Closed) + State *indexes.Multi[int32, keys.LeasePrimaryKey, mv1.Lease] + + // Provider indexes leases by provider address (covers all states, replaces old reverse keys) + Provider *indexes.Multi[string, keys.LeasePrimaryKey, mv1.Lease] +} + +func (b BidIndexes) IndexesList() []collections.Index[keys.BidPrimaryKey, types.Bid] { + return []collections.Index[keys.BidPrimaryKey, types.Bid]{ + b.State, + b.Provider, + b.OrderState, + } +} + +func (l LeaseIndexes) IndexesList() []collections.Index[keys.LeasePrimaryKey, mv1.Lease] { + return []collections.Index[keys.LeasePrimaryKey, mv1.Lease]{ + l.State, + l.Provider, + } +} + +func (o OrderIndexes) IndexesList() []collections.Index[keys.OrderPrimaryKey, types.Order] { + return []collections.Index[keys.OrderPrimaryKey, types.Order]{ + o.State, + o.GroupState, + } +} + +// NewOrderIndexes creates all secondary indexes for the order IndexedMap +func NewOrderIndexes(sb *collections.SchemaBuilder) OrderIndexes { + return OrderIndexes{ + State: indexes.NewMulti( + sb, + collections.NewPrefix(keys.OrderIndexStatePrefix), + "orders_by_state", + collections.Int32Key, + keys.OrderPrimaryKeyCodec, + func(_ keys.OrderPrimaryKey, order types.Order) (int32, error) { + return int32(order.State), nil + }, + ), + GroupState: indexes.NewMulti( + sb, + collections.NewPrefix(keys.OrderIndexGroupStatePrefix), + "orders_by_group_state", + collections.PairKeyCodec( + collections.TripleKeyCodec( + collections.StringKey, + collections.Uint64Key, + collections.Uint32Key, + ), + collections.Int32Key, + ), + keys.OrderPrimaryKeyCodec, + func(_ keys.OrderPrimaryKey, order types.Order) (collections.Pair[keys.GroupPartKey, int32], error) { + groupPart := collections.Join3(order.ID.Owner, order.ID.DSeq, order.ID.GSeq) + return collections.Join(groupPart, int32(order.State)), nil + }, + ), + } +} + +// NewBidIndexes creates all secondary indexes for the bid IndexedMap +func NewBidIndexes(sb *collections.SchemaBuilder) BidIndexes { + return BidIndexes{ + State: indexes.NewMulti( + sb, + collections.NewPrefix(keys.BidIndexStatePrefix), + "bids_by_state", + collections.Int32Key, + keys.BidPrimaryKeyCodec, + func(_ keys.BidPrimaryKey, bid types.Bid) (int32, error) { + return int32(bid.State), nil + }, + ), + Provider: indexes.NewMulti( + sb, + collections.NewPrefix(keys.BidIndexProviderPrefix), + "bids_by_provider", + collections.StringKey, + keys.BidPrimaryKeyCodec, + func(_ keys.BidPrimaryKey, bid types.Bid) (string, error) { + return bid.ID.Provider, nil + }, + ), + OrderState: indexes.NewMulti( + sb, + collections.NewPrefix(keys.BidIndexOrderStatePrefix), + "bids_by_order_state", + collections.PairKeyCodec( + collections.QuadKeyCodec( + collections.StringKey, + collections.Uint64Key, + collections.Uint32Key, + collections.Uint32Key, + ), + collections.Int32Key, + ), + keys.BidPrimaryKeyCodec, + func(_ keys.BidPrimaryKey, bid types.Bid) (collections.Pair[keys.OrderPrimaryKey, int32], error) { + orderPart := collections.Join4(bid.ID.Owner, bid.ID.DSeq, bid.ID.GSeq, bid.ID.OSeq) + return collections.Join(orderPart, int32(bid.State)), nil + }, + ), + } +} + +// NewLeaseIndexes creates all secondary indexes for the lease IndexedMap +func NewLeaseIndexes(sb *collections.SchemaBuilder) LeaseIndexes { + return LeaseIndexes{ + State: indexes.NewMulti( + sb, + collections.NewPrefix(keys.LeaseIndexStatePrefix), + "leases_by_state", + collections.Int32Key, + keys.LeasePrimaryKeyCodec, + func(_ keys.LeasePrimaryKey, lease mv1.Lease) (int32, error) { + return int32(lease.State), nil + }, + ), + Provider: indexes.NewMulti( + sb, + collections.NewPrefix(keys.LeaseIndexProviderPrefix), + "leases_by_provider", + collections.StringKey, + keys.LeasePrimaryKeyCodec, + func(_ keys.LeasePrimaryKey, lease mv1.Lease) (string, error) { + return lease.ID.Provider, nil + }, + ), + } +} diff --git a/x/market/keeper/keeper.go b/x/market/keeper/keeper.go index b9a418eb4b..e6a7f48ee1 100644 --- a/x/market/keeper/keeper.go +++ b/x/market/keeper/keeper.go @@ -3,8 +3,11 @@ package keeper import ( "fmt" + "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" dtypes "pkg.akt.dev/go/node/deployment/v1" @@ -52,16 +55,43 @@ type Keeper struct { // The address capable of executing a MsgUpdateParams message. // This should be the x/gov module account. authority string + + schema collections.Schema + bids *collections.IndexedMap[keys.BidPrimaryKey, types.Bid, BidIndexes] + orders *collections.IndexedMap[keys.OrderPrimaryKey, types.Order, OrderIndexes] + leases *collections.IndexedMap[keys.LeasePrimaryKey, mv1.Lease, LeaseIndexes] } // NewKeeper creates and returns an instance for Market keeper -func NewKeeper(cdc codec.BinaryCodec, skey storetypes.StoreKey, ekeeper EscrowKeeper, authority string) IKeeper { - return Keeper{ +func NewKeeper(cdc codec.BinaryCodec, skey *storetypes.KVStoreKey, ekeeper EscrowKeeper, authority string) IKeeper { + ssvc := runtime.NewKVStoreService(skey) + sb := collections.NewSchemaBuilder(ssvc) + + bidIndexes := NewBidIndexes(sb) + orderIndexes := NewOrderIndexes(sb) + leaseIndexes := NewLeaseIndexes(sb) + + bids := collections.NewIndexedMap(sb, collections.NewPrefix(keys.BidPrefixNew), "bids", keys.BidPrimaryKeyCodec, codec.CollValue[types.Bid](cdc), bidIndexes) + orders := collections.NewIndexedMap(sb, collections.NewPrefix(keys.OrderPrefixNew), "orders", keys.OrderPrimaryKeyCodec, codec.CollValue[types.Order](cdc), orderIndexes) + leases := collections.NewIndexedMap(sb, collections.NewPrefix(keys.LeasePrefixNew), "leases", keys.LeasePrimaryKeyCodec, codec.CollValue[mv1.Lease](cdc), leaseIndexes) + + schema, err := sb.Build() + if err != nil { + panic(err) + } + + res := &Keeper{ skey: skey, cdc: cdc, ekeeper: ekeeper, authority: authority, + schema: schema, + bids: bids, + orders: orders, + leases: leases, } + + return res } func (k Keeper) NewQuerier() Querier { @@ -83,6 +113,21 @@ func (k Keeper) GetAuthority() string { return k.authority } +// Bids returns the bid IndexedMap for direct access (used by genesis and migration) +func (k Keeper) Bids() *collections.IndexedMap[keys.BidPrimaryKey, types.Bid, BidIndexes] { + return k.bids +} + +// Orders returns the order IndexedMap for direct access (used by genesis and migration) +func (k Keeper) Orders() *collections.IndexedMap[keys.OrderPrimaryKey, types.Order, OrderIndexes] { + return k.orders +} + +// Leases returns the lease IndexedMap for direct access (used by genesis and migration) +func (k Keeper) Leases() *collections.IndexedMap[keys.LeasePrimaryKey, mv1.Lease, LeaseIndexes] { + return k.leases +} + // SetParams sets the x/market module parameters. func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { if err := p.Validate(); err != nil { @@ -110,8 +155,6 @@ func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { // CreateOrder creates a new order with given group id and specifications. It returns created order func (k Keeper) CreateOrder(ctx sdk.Context, gid dtypes.GroupID, spec dtypesBeta.GroupSpec) (types.Order, error) { - store := ctx.KVStore(k.skey) - oseq := uint32(1) var err error @@ -136,19 +179,25 @@ func (k Keeper) CreateOrder(ctx sdk.Context, gid dtypes.GroupID, spec dtypesBeta orderID := mv1.MakeOrderID(gid, oseq) - if res := k.findOrder(ctx, orderID); len(res) > 0 { + pk := keys.OrderIDToKey(orderID) + has, err := k.orders.Has(ctx, pk) + if err != nil { + return types.Order{}, err + } + if has { return types.Order{}, mv1.ErrOrderExists } order := types.Order{ - ID: mv1.MakeOrderID(gid, oseq), + ID: orderID, Spec: spec, State: types.OrderOpen, CreatedAt: ctx.BlockHeight(), } - key := keys.MustOrderKey(keys.OrderStateOpenPrefix, order.ID) - store.Set(key, k.cdc.MustMarshal(&order)) + if err := k.orders.Set(ctx, pk, order); err != nil { + return types.Order{}, fmt.Errorf("failed to create order: %w", err) + } ctx.Logger().Info("created order", "order", order.ID) @@ -164,9 +213,13 @@ func (k Keeper) CreateOrder(ctx sdk.Context, gid dtypes.GroupID, spec dtypesBeta // CreateBid creates a bid for a order with given orderID, price for bid and provider func (k Keeper) CreateBid(ctx sdk.Context, id mv1.BidID, price sdk.DecCoin, roffer types.ResourcesOffer) (types.Bid, error) { - store := ctx.KVStore(k.skey) + pk := keys.BidIDToKey(id) - if key := k.findBid(ctx, id); len(key) > 0 { + has, err := k.bids.Has(ctx, pk) + if err != nil { + return types.Bid{}, err + } + if has { return types.Bid{}, mv1.ErrBidExists } @@ -178,18 +231,11 @@ func (k Keeper) CreateBid(ctx sdk.Context, id mv1.BidID, price sdk.DecCoin, roff ResourcesOffer: roffer, } - data := k.cdc.MustMarshal(&bid) - - key := keys.MustBidKey(keys.BidStateToPrefix(bid.State), id) - revKey := keys.MustBidStateRevereKey(bid.State, id) - - store.Set(key, data) - - if len(revKey) > 0 { - store.Set(revKey, data) + if err := k.bids.Set(ctx, pk, bid); err != nil { + return types.Bid{}, err } - err := ctx.EventManager().EmitTypedEvent( + err = ctx.EventManager().EmitTypedEvent( &mv1.EventBidCreated{ ID: bid.ID, Price: price, @@ -205,8 +251,6 @@ func (k Keeper) CreateBid(ctx sdk.Context, id mv1.BidID, price sdk.DecCoin, roff // CreateLease creates lease for bid with given bidID. // Should only be called by the EndBlock handler or unit tests. func (k Keeper) CreateLease(ctx sdk.Context, bid types.Bid) error { - store := ctx.KVStore(k.skey) - lease := mv1.Lease{ ID: mv1.LeaseID(bid.ID), State: mv1.LeaseActive, @@ -214,15 +258,9 @@ func (k Keeper) CreateLease(ctx sdk.Context, bid types.Bid) error { CreatedAt: ctx.BlockHeight(), } - data := k.cdc.MustMarshal(&lease) - - // create (active) lease in store - key := keys.MustLeaseKey(keys.LeaseStateToPrefix(lease.State), lease.ID) - revKey := keys.MustLeaseStateReverseKey(lease.State, lease.ID) - - store.Set(key, data) - if len(revKey) > 0 { - store.Set(revKey, data) + pk := keys.LeaseIDToKey(lease.ID) + if err := k.leases.Set(ctx, pk, lease); err != nil { + return fmt.Errorf("failed to create lease: %w", err) } err := ctx.EventManager().EmitTypedEvent( @@ -315,25 +353,15 @@ func (k Keeper) OnLeaseClosed(ctx sdk.Context, lease mv1.Lease, state mv1.Lease_ return nil } - currState := lease.State - lease.State = state lease.ClosedOn = ctx.BlockHeight() lease.Reason = reason - store := ctx.KVStore(k.skey) - - key := keys.MustLeaseKey(keys.LeaseStateToPrefix(currState), lease.ID) - revKey := keys.MustLeaseStateReverseKey(currState, lease.ID) - - store.Delete(key) - if len(revKey) > 0 { - store.Delete(revKey) + // IndexedMap.Set automatically updates all indexes + if err := k.leases.Set(ctx, keys.LeaseIDToKey(lease.ID), lease); err != nil { + return fmt.Errorf("failed to update lease: %w", err) } - key = keys.MustLeaseKey(keys.LeaseStateToPrefix(lease.State), lease.ID) - store.Set(key, k.cdc.MustMarshal(&lease)) - err := ctx.EventManager().EmitTypedEvent( &mv1.EventLeaseClosed{ ID: lease.ID, @@ -410,123 +438,31 @@ func (k Keeper) OnGroupClosed(ctx sdk.Context, id dtypes.GroupID, state dtypesBe return nil } -func (k Keeper) findOrder(ctx sdk.Context, id mv1.OrderID) []byte { - store := ctx.KVStore(k.skey) - - aKey := keys.MustOrderKey(keys.OrderStateActivePrefix, id) - oKey := keys.MustOrderKey(keys.OrderStateOpenPrefix, id) - cKey := keys.MustOrderKey(keys.OrderStateClosedPrefix, id) - - var key []byte - - // nolint: gocritic - if store.Has(aKey) { - key = aKey - } else if store.Has(oKey) { - key = oKey - } else if store.Has(cKey) { - key = cKey - } - - return key -} - // GetOrder returns order with given orderID from market store func (k Keeper) GetOrder(ctx sdk.Context, id mv1.OrderID) (types.Order, bool) { - key := k.findOrder(ctx, id) - - if len(key) == 0 { + order, err := k.orders.Get(ctx, keys.OrderIDToKey(id)) + if err != nil { return types.Order{}, false } - - store := ctx.KVStore(k.skey) - - buf := store.Get(key) - - var val types.Order - k.cdc.MustUnmarshal(buf, &val) - - return val, true -} - -func (k Keeper) findBid(ctx sdk.Context, id mv1.BidID) []byte { - store := ctx.KVStore(k.skey) - - aKey := keys.MustBidKey(keys.BidStateActivePrefix, id) - oKey := keys.MustBidKey(keys.BidStateOpenPrefix, id) - lKey := keys.MustBidKey(keys.BidStateLostPrefix, id) - cKey := keys.MustBidKey(keys.BidStateClosedPrefix, id) - - var key []byte - - // nolint: gocritic - if store.Has(aKey) { - key = aKey - } else if store.Has(oKey) { - key = oKey - } else if store.Has(lKey) { - key = lKey - } else if store.Has(cKey) { - key = cKey - } - - return key + return order, true } // GetBid returns bid with given bidID from market store func (k Keeper) GetBid(ctx sdk.Context, id mv1.BidID) (types.Bid, bool) { - store := ctx.KVStore(k.skey) - - key := k.findBid(ctx, id) - - if len(key) == 0 { + bid, err := k.bids.Get(ctx, keys.BidIDToKey(id)) + if err != nil { return types.Bid{}, false } - - buf := store.Get(key) - - var val types.Bid - k.cdc.MustUnmarshal(buf, &val) - - return val, true -} - -func (k Keeper) findLease(ctx sdk.Context, id mv1.LeaseID) []byte { - store := ctx.KVStore(k.skey) - - aKey := keys.MustLeaseKey(keys.LeaseStateActivePrefix, id) - iKey := keys.MustLeaseKey(keys.LeaseStateInsufficientFundsPrefix, id) - cKey := keys.MustLeaseKey(keys.LeaseStateClosedPrefix, id) - - var key []byte - - // nolint: gocritic - if store.Has(aKey) { - key = aKey - } else if store.Has(iKey) { - key = iKey - } else if store.Has(cKey) { - key = cKey - } - - return key + return bid, true } // GetLease returns lease with given leaseID from market store func (k Keeper) GetLease(ctx sdk.Context, id mv1.LeaseID) (mv1.Lease, bool) { - store := ctx.KVStore(k.skey) - key := k.findLease(ctx, id) - - if len(key) == 0 { + lease, err := k.leases.Get(ctx, keys.LeaseIDToKey(id)) + if err != nil { return mv1.Lease{}, false } - - buf := store.Get(key) - - var val mv1.Lease - k.cdc.MustUnmarshal(buf, &val) - - return val, true + return lease, true } // LeaseForOrder returns lease for order with given ID and lease found status @@ -544,183 +480,103 @@ func (k Keeper) LeaseForOrder(ctx sdk.Context, bs types.Bid_State, oid mv1.Order // WithOrders iterates all orders in market func (k Keeper) WithOrders(ctx sdk.Context, fn func(types.Order) bool) { - store := ctx.KVStore(k.skey) - iter := storetypes.KVStorePrefixIterator(store, keys.OrderPrefix) - defer func() { - _ = iter.Close() - }() - - for ; iter.Valid(); iter.Next() { - var val types.Order - k.cdc.MustUnmarshal(iter.Value(), &val) - if stop := fn(val); stop { - break - } + err := k.orders.Walk(ctx, nil, func(_ keys.OrderPrimaryKey, order types.Order) (bool, error) { + return fn(order), nil + }) + if err != nil { + panic(fmt.Sprintf("WithOrders iteration failed: %v", err)) } } // WithBids iterates all bids in market func (k Keeper) WithBids(ctx sdk.Context, fn func(types.Bid) bool) { - store := ctx.KVStore(k.skey) - iter := storetypes.KVStorePrefixIterator(store, keys.BidPrefix) - - defer func() { - _ = iter.Close() - }() - - defer func() { - _ = iter.Close() - }() - - for ; iter.Valid(); iter.Next() { - var val types.Bid - k.cdc.MustUnmarshal(iter.Value(), &val) - if stop := fn(val); stop { - break - } + err := k.bids.Walk(ctx, nil, func(_ keys.BidPrimaryKey, bid types.Bid) (bool, error) { + return fn(bid), nil + }) + if err != nil { + panic(fmt.Sprintf("WithBids iteration failed: %v", err)) } } // WithLeases iterates all leases in market func (k Keeper) WithLeases(ctx sdk.Context, fn func(mv1.Lease) bool) { - store := ctx.KVStore(k.skey) - iter := storetypes.KVStorePrefixIterator(store, keys.LeasePrefix) - - defer func() { - _ = iter.Close() - }() - - for ; iter.Valid(); iter.Next() { - var val mv1.Lease - k.cdc.MustUnmarshal(iter.Value(), &val) - if stop := fn(val); stop { - break - } + err := k.leases.Walk(ctx, nil, func(_ keys.LeasePrimaryKey, lease mv1.Lease) (bool, error) { + return fn(lease), nil + }) + if err != nil { + panic(fmt.Sprintf("WithLeases iteration failed: %v", err)) } } // WithOrdersForGroup iterates all orders of a group in market with given GroupID func (k Keeper) WithOrdersForGroup(ctx sdk.Context, id dtypes.GroupID, state types.Order_State, fn func(types.Order) bool) { - store := ctx.KVStore(k.skey) - iter := storetypes.KVStorePrefixIterator(store, keys.OrdersForGroupPrefix(keys.OrderStateToPrefix(state), id)) + groupPart := collections.Join3(id.Owner, id.DSeq, id.GSeq) + refKey := collections.Join(groupPart, int32(state)) - defer func() { - _ = iter.Close() - }() + iter, err := k.orders.Indexes.GroupState.MatchExact(ctx, refKey) + if err != nil { + panic(fmt.Sprintf("WithOrdersForGroup iteration failed: %v", err)) + } - for ; iter.Valid(); iter.Next() { - var val types.Order - k.cdc.MustUnmarshal(iter.Value(), &val) - if stop := fn(val); stop { - break - } + err = indexes.ScanValues(ctx, k.orders, iter, func(order types.Order) bool { + return fn(order) + }) + if err != nil { + panic(fmt.Sprintf("WithOrdersForGroup scan failed: %v", err)) } } // WithBidsForOrder iterates all bids of an order in market with given OrderID func (k Keeper) WithBidsForOrder(ctx sdk.Context, id mv1.OrderID, state types.Bid_State, fn func(types.Bid) bool) { - store := ctx.KVStore(k.skey) - iter := storetypes.KVStorePrefixIterator(store, keys.BidsForOrderPrefix(keys.BidStateToPrefix(state), id)) + orderPart := collections.Join4(id.Owner, id.DSeq, id.GSeq, id.OSeq) + refKey := collections.Join(orderPart, int32(state)) - defer func() { - _ = iter.Close() - }() + iter, err := k.bids.Indexes.OrderState.MatchExact(ctx, refKey) + if err != nil { + panic(fmt.Sprintf("WithBidsForOrder iteration failed: %v", err)) + } - for ; iter.Valid(); iter.Next() { - var val types.Bid - k.cdc.MustUnmarshal(iter.Value(), &val) - if stop := fn(val); stop { - break - } + err = indexes.ScanValues(ctx, k.bids, iter, func(bid types.Bid) bool { + return fn(bid) + }) + if err != nil { + panic(fmt.Sprintf("WithBidsForOrder scan failed: %v", err)) } } func (k Keeper) BidCountForOrder(ctx sdk.Context, id mv1.OrderID) uint32 { - store := ctx.KVStore(k.skey) - oiter := storetypes.KVStorePrefixIterator(store, keys.BidsForOrderPrefix(keys.BidStateOpenPrefix, id)) - aiter := storetypes.KVStorePrefixIterator(store, keys.BidsForOrderPrefix(keys.BidStateActivePrefix, id)) - citer := storetypes.KVStorePrefixIterator(store, keys.BidsForOrderPrefix(keys.BidStateClosedPrefix, id)) - - defer func() { - _ = oiter.Close() - _ = aiter.Close() - _ = citer.Close() - }() - + orderPart := collections.Join4(id.Owner, id.DSeq, id.GSeq, id.OSeq) count := uint32(0) - for ; oiter.Valid(); oiter.Next() { - count++ - } - - for ; aiter.Valid(); aiter.Next() { - count++ - } - for ; citer.Valid(); citer.Next() { - count++ + for _, state := range []types.Bid_State{types.BidOpen, types.BidActive, types.BidClosed} { + refKey := collections.Join(orderPart, int32(state)) + iter, err := k.bids.Indexes.OrderState.MatchExact(ctx, refKey) + if err != nil { + panic(fmt.Sprintf("BidCountForOrder failed: %v", err)) + } + for ; iter.Valid(); iter.Next() { + count++ + } + _ = iter.Close() } return count } -func (k Keeper) updateOrder(ctx sdk.Context, order types.Order, currState types.Order_State) { - store := ctx.KVStore(k.skey) - - switch currState { - case types.OrderOpen: - case types.OrderActive: - default: - panic(fmt.Sprintf("unexpected current state of the order: %d", currState)) - } - - key := keys.MustOrderKey(keys.OrderStateToPrefix(currState), order.ID) - store.Delete(key) - - switch order.State { - case types.OrderActive: - case types.OrderClosed: - default: - panic(fmt.Sprintf("unexpected new state of the order: %d", order.State)) +func (k Keeper) updateOrder(ctx sdk.Context, order types.Order, _ types.Order_State) { + // IndexedMap.Set automatically updates all indexes: + // - removes old index references via lazyOldValue + // - creates new index references for the updated order + if err := k.orders.Set(ctx, keys.OrderIDToKey(order.ID), order); err != nil { + panic(fmt.Sprintf("failed to update order: %v", err)) } - - data := k.cdc.MustMarshal(&order) - - key = keys.MustOrderKey(keys.OrderStateToPrefix(order.State), order.ID) - store.Set(key, data) } -func (k Keeper) updateBid(ctx sdk.Context, bid types.Bid, currState types.Bid_State) { - store := ctx.KVStore(k.skey) - - switch currState { - case types.BidOpen: - case types.BidActive: - default: - panic(fmt.Sprintf("unexpected current state of the bid: %d", currState)) - } - - key := keys.MustBidKey(keys.BidStateToPrefix(currState), bid.ID) - revKey := keys.MustBidStateRevereKey(currState, bid.ID) - store.Delete(key) - if revKey != nil { - store.Delete(revKey) - } - - switch bid.State { - case types.BidActive: - case types.BidLost: - case types.BidClosed: - default: - panic(fmt.Sprintf("unexpected new state of the bid: %d", bid.State)) - } - - data := k.cdc.MustMarshal(&bid) - - key = keys.MustBidKey(keys.BidStateToPrefix(bid.State), bid.ID) - revKey = keys.MustBidStateRevereKey(bid.State, bid.ID) - - store.Set(key, data) - if len(revKey) > 0 { - store.Set(revKey, data) +func (k Keeper) updateBid(ctx sdk.Context, bid types.Bid, _ types.Bid_State) { + // IndexedMap.Set automatically updates all indexes: + // - removes old index references via lazyOldValue + // - creates new index references for the updated bid + if err := k.bids.Set(ctx, keys.BidIDToKey(bid.ID), bid); err != nil { + panic(fmt.Sprintf("failed to update bid: %v", err)) } } diff --git a/x/market/keeper/keys/bid.go b/x/market/keeper/keys/bid.go new file mode 100644 index 0000000000..b5784b7bd3 --- /dev/null +++ b/x/market/keeper/keys/bid.go @@ -0,0 +1,46 @@ +package keys + +import ( + "cosmossdk.io/collections" + + mv1 "pkg.akt.dev/go/node/market/v1" +) + +// BidPrimaryKey is the full composite primary key for a bid in the IndexedMap +type BidPrimaryKey = collections.Pair[OrderPrimaryKey, ProviderPartKey] + +// BidPrimaryKeyCodec is the key codec for BidPrimaryKey, composed from stdlib codecs +var BidPrimaryKeyCodec = collections.PairKeyCodec( + collections.QuadKeyCodec( + collections.StringKey, + collections.Uint64Key, + collections.Uint32Key, + collections.Uint32Key, + ), + collections.PairKeyCodec( + collections.StringKey, + collections.Uint32Key, + ), +) + +// BidIDToKey converts a mv1.BidID to a BidPrimaryKey for use with the IndexedMap +func BidIDToKey(id mv1.BidID) BidPrimaryKey { + return collections.Join( + collections.Join4(id.Owner, id.DSeq, id.GSeq, id.OSeq), + collections.Join(id.Provider, id.BSeq), + ) +} + +// KeyToBidID converts a BidPrimaryKey back to a mv1.BidID +func KeyToBidID(key BidPrimaryKey) mv1.BidID { + orderPart := key.K1() + providerPart := key.K2() + return mv1.BidID{ + Owner: orderPart.K1(), + DSeq: orderPart.K2(), + GSeq: orderPart.K3(), + OSeq: orderPart.K4(), + Provider: providerPart.K1(), + BSeq: providerPart.K2(), + } +} diff --git a/x/market/keeper/keys/key.go b/x/market/keeper/keys/key.go index 038a0387da..cfdf937692 100644 --- a/x/market/keeper/keys/key.go +++ b/x/market/keeper/keys/key.go @@ -29,17 +29,27 @@ const ( var ( OrderPrefix = []byte{0x11, 0x00} + OrderPrefixNew = []byte{0x11, 0x01} + OrderIndexStatePrefix = []byte{0x11, 0x02} + OrderIndexGroupStatePrefix = []byte{0x11, 0x03} OrderStateOpenPrefix = []byte{OrderStateOpenPrefixID} OrderStateActivePrefix = []byte{OrderStateActivePrefixID} OrderStateClosedPrefix = []byte{OrderStateClosedPrefixID} BidPrefix = []byte{0x12, 0x00} BidPrefixReverse = []byte{0x12, 0x01} + BidPrefixNew = []byte{0x12, 0x02} + BidIndexStatePrefix = []byte{0x12, 0x03} + BidIndexProviderPrefix = []byte{0x12, 0x04} + BidIndexOrderStatePrefix = []byte{0x12, 0x05} BidStateOpenPrefix = []byte{BidStateOpenPrefixID} BidStateActivePrefix = []byte{BidStateActivePrefixID} BidStateLostPrefix = []byte{BidStateLostPrefixID} BidStateClosedPrefix = []byte{BidStateClosedPrefixID} LeasePrefix = []byte{0x13, 0x00} LeasePrefixReverse = []byte{0x13, 0x01} + LeasePrefixNew = []byte{0x13, 0x02} + LeaseIndexStatePrefix = []byte{0x13, 0x03} + LeaseIndexProviderPrefix = []byte{0x13, 0x04} LeaseStateActivePrefix = []byte{LeaseStateActivePrefixID} LeaseStateInsufficientFundsPrefix = []byte{LeaseStateInsufficientFundsPrefixID} LeaseStateClosedPrefix = []byte{LeaseStateClosedPrefixID} diff --git a/x/market/keeper/keys/lease.go b/x/market/keeper/keys/lease.go new file mode 100644 index 0000000000..e8e09c2556 --- /dev/null +++ b/x/market/keeper/keys/lease.go @@ -0,0 +1,45 @@ +package keys + +import ( + "cosmossdk.io/collections" + mv1 "pkg.akt.dev/go/node/market/v1" +) + +// LeasePrimaryKey is the full composite primary key for a lease in the IndexedMap +type LeasePrimaryKey = collections.Pair[OrderPrimaryKey, ProviderPartKey] + +// LeasePrimaryKeyCodec is the key codec for LeasePrimaryKey, composed from stdlib codecs +var LeasePrimaryKeyCodec = collections.PairKeyCodec( + collections.QuadKeyCodec( + collections.StringKey, + collections.Uint64Key, + collections.Uint32Key, + collections.Uint32Key, + ), + collections.PairKeyCodec( + collections.StringKey, + collections.Uint32Key, + ), +) + +// LeaseIDToKey converts a mv1.LeaseID to a LeasePrimaryKey for use with the IndexedMap +func LeaseIDToKey(id mv1.LeaseID) LeasePrimaryKey { + return collections.Join( + collections.Join4(id.Owner, id.DSeq, id.GSeq, id.OSeq), + collections.Join(id.Provider, id.BSeq), + ) +} + +// KeyToLeaseID converts a LeasePrimaryKey back to a mv1.LeaseID +func KeyToLeaseID(key LeasePrimaryKey) mv1.LeaseID { + orderPart := key.K1() + providerPart := key.K2() + return mv1.LeaseID{ + Owner: orderPart.K1(), + DSeq: orderPart.K2(), + GSeq: orderPart.K3(), + OSeq: orderPart.K4(), + Provider: providerPart.K1(), + BSeq: providerPart.K2(), + } +} diff --git a/x/market/keeper/keys/order.go b/x/market/keeper/keys/order.go new file mode 100644 index 0000000000..2747b9bc9e --- /dev/null +++ b/x/market/keeper/keys/order.go @@ -0,0 +1,22 @@ +package keys + +import ( + "cosmossdk.io/collections" + + mv1 "pkg.akt.dev/go/node/market/v1" +) + +// OrderIDToKey converts a mv1.OrderID to an OrderPrimaryKey for use with the IndexedMap +func OrderIDToKey(id mv1.OrderID) OrderPrimaryKey { + return collections.Join4(id.Owner, id.DSeq, id.GSeq, id.OSeq) +} + +// KeyToOrderID converts an OrderPrimaryKey back to a mv1.OrderID +func KeyToOrderID(key OrderPrimaryKey) mv1.OrderID { + return mv1.OrderID{ + Owner: key.K1(), + DSeq: key.K2(), + GSeq: key.K3(), + OSeq: key.K4(), + } +} diff --git a/x/market/keeper/keys/types.go b/x/market/keeper/keys/types.go new file mode 100644 index 0000000000..df1cfa220c --- /dev/null +++ b/x/market/keeper/keys/types.go @@ -0,0 +1,22 @@ +package keys + +import ( + "cosmossdk.io/collections" +) + +// OrderPrimaryKey represents the order portion of a BidID: (owner, dseq, gseq, oseq) +type OrderPrimaryKey = collections.Quad[string, uint64, uint32, uint32] + +// ProviderPartKey represents the provider portion of a BidID: (provider, bseq) +type ProviderPartKey = collections.Pair[string, uint32] + +// GroupPartKey represents (owner, dseq, gseq) for group-based index lookups +type GroupPartKey = collections.Triple[string, uint64, uint32] + +// OrderPrimaryKeyCodec is the key codec for OrderPrimaryKey +var OrderPrimaryKeyCodec = collections.QuadKeyCodec( + collections.StringKey, + collections.Uint64Key, + collections.Uint32Key, + collections.Uint32Key, +) diff --git a/x/market/module.go b/x/market/module.go index cad5c0f872..c2d9dd455e 100644 --- a/x/market/module.go +++ b/x/market/module.go @@ -179,7 +179,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // ConsensusVersion implements module.AppModule#ConsensusVersion func (am AppModule) ConsensusVersion() uint64 { - return 7 + return 8 } // AppModuleSimulation functions