Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Bug Fixes

- (storage) [#298](https://github.com/mocachain/moca/pull/298) Close the object iterator in `isNonEmptyBucket` to fix a per-call store-iterator leak (MOCA-413)
- (app/upgrades) [#289](https://github.com/mocachain/moca/pull/289) Pin v2 feemarket `min_gas_price` to 20 gwei (moca's intended floor) so upgraded chains match genesis.
- (x/challenge) [#286](https://github.com/mocachain/moca/pull/286) Retire a challenge from the active set once it is attested, making attestation idempotent so duplicate submissions (e.g. redundant relayers or resubmissions by the in-turn submitter) are rejected instead of re-running heartbeat rewards and re-emitting attestation events.
- (ci) [#65](https://github.com/mocachain/moca/pull/65) Resolve goreleaser CI failures for arm64 docker builds
Expand Down
1 change: 1 addition & 0 deletions x/storage/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,7 @@ func (k Keeper) isNonEmptyBucket(ctx sdk.Context, bucketName string) bool {
objectStore := prefix.NewStore(store, storagetypes.GetObjectKeyOnlyBucketPrefix(bucketName))

iter := objectStore.Iterator(nil, nil)
defer iter.Close()
return iter.Valid()
}

Expand Down
47 changes: 47 additions & 0 deletions x/storage/keeper/keeper_isnonemptybucket_whitebox_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package keeper

// MOCA-413: behavior regression test for (Keeper).isNonEmptyBucket.
//
// The fix under test rewrote isNonEmptyBucket to acquire an object-store
// iterator, defer iter.Close(), and return iter.Valid() so the iterator is
// always released. A leak-catching test is impractical here: moca's KVStore
// does NOT panic on a write-while-iterator-open, so the dangling iterator from
// the pre-fix code is not directly observable from a test. Instead, this is a
// BEHAVIOR test that guards the function's correctness (emptiness detection and
// per-bucket isolation) alongside the defer iter.Close() fix.
//
// isNonEmptyBucket is unexported, so this is a true whitebox test in
// package keeper. We build a minimal Keeper inline (only storeKey is used) and
// avoid testutil/keeper, which imports package keeper and would create an
// import cycle for a package-keeper test.

import (
"testing"

"cosmossdk.io/store/prefix"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/stretchr/testify/require"

storagetypes "github.com/mocachain/moca/v2/x/storage/types"
)

func TestIsNonEmptyBucket(t *testing.T) {
key := storetypes.NewKVStoreKey(storagetypes.StoreKey)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx

// Only storeKey is exercised by isNonEmptyBucket; a partial literal is fine.
k := Keeper{storeKey: key}

// 1. An empty bucket has no objects.
require.False(t, k.isNonEmptyBucket(ctx, "empty-bucket"))

// 2. A bucket with an object under its object prefix is non-empty.
prefix.NewStore(ctx.KVStore(key), storagetypes.GetObjectKeyOnlyBucketPrefix("full-bucket")).
Set([]byte("obj-1"), []byte{1})
require.True(t, k.isNonEmptyBucket(ctx, "full-bucket"))

// 3. Isolation: the object in full-bucket must not leak into another bucket.
require.False(t, k.isNonEmptyBucket(ctx, "other-bucket"))
}
Loading