diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index b928c1a4ec..491f094a3b 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -623,8 +623,11 @@ func (k Keeper) appendToContractHistory(ctx sdk.Context, contractAddr sdk.AccAdd iter := prefixStore.ReverseIterator(nil, nil) defer iter.Close() - if iter.Valid() { - pos = sdk.BigEndianToUint64(iter.Key()) + for ; iter.Valid(); iter.Next() { + if len(iter.Key()) == 8 { // add extra safety in a mixed contract length environment + pos = sdk.BigEndianToUint64(iter.Key()) + break + } } // then store with incrementing position for _, e := range newEntries { @@ -641,6 +644,10 @@ func (k Keeper) GetContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress) defer iter.Close() for ; iter.Valid(); iter.Next() { + if len(iter.Key()) != 8 { // add extra safety in a mixed contract length environment + continue + } + var e types.ContractCodeHistoryEntry k.cdc.MustUnmarshal(iter.Value(), &e) r = append(r, e) @@ -655,12 +662,14 @@ func (k Keeper) getLastContractHistoryEntry(ctx sdk.Context, contractAddr sdk.Ac defer iter.Close() var r types.ContractCodeHistoryEntry - if !iter.Valid() { - // all contracts have a history - panic(fmt.Sprintf("no history for %s", contractAddr.String())) + for ; iter.Valid(); iter.Next() { + if len(iter.Key()) == 8 { // add extra safety in a mixed contract length environment + k.cdc.MustUnmarshal(iter.Value(), &r) + return r + } } - k.cdc.MustUnmarshal(iter.Value(), &r) - return r + // all contracts have a history + panic(fmt.Sprintf("no history for %s", contractAddr.String())) } // QuerySmart queries the smart contract itself. diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index ffa87aa0dd..d59b62ce4f 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -6,16 +6,17 @@ import ( "encoding/json" "errors" "fmt" + stdrand "math/rand" "os" "strings" "testing" "time" errorsmod "cosmossdk.io/errors" - abci "github.com/cometbft/cometbft/abci/types" wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/rand" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" @@ -2161,20 +2162,48 @@ func TestSetAccessConfig(t *testing.T) { } func TestAppendToContractHistory(t *testing.T) { - ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) - var contractAddr sdk.AccAddress = rand.Bytes(types.ContractAddrLen) - var orderedEntries []types.ContractCodeHistoryEntry - f := fuzz.New().Funcs(ModelFuzzers...) - for i := 0; i < 10; i++ { - var entry types.ContractCodeHistoryEntry - f.Fuzz(&entry) - keepers.WasmKeeper.appendToContractHistory(ctx, contractAddr, entry) - orderedEntries = append(orderedEntries, entry) + pCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) + k := keepers.WasmKeeper + + variableLengthAddresses := []sdk.AccAddress{ + bytes.Repeat([]byte{0x1}, types.ContractAddrLen), + append([]byte{0x00}, bytes.Repeat([]byte{0x1}, types.ContractAddrLen-1)...), + append(bytes.Repeat([]byte{0x1}, types.ContractAddrLen-1), 0x00), + append([]byte{0xff}, bytes.Repeat([]byte{0x1}, types.ContractAddrLen-1)...), + append(bytes.Repeat([]byte{0x1}, types.ContractAddrLen-1), 0xff), + bytes.Repeat([]byte{0x1}, types.SDKAddrLen), + append([]byte{0x00}, bytes.Repeat([]byte{0x1}, types.SDKAddrLen-1)...), + append(bytes.Repeat([]byte{0x1}, types.SDKAddrLen-1), 0x00), + append([]byte{0xff}, bytes.Repeat([]byte{0x1}, types.SDKAddrLen-1)...), + append(bytes.Repeat([]byte{0x1}, types.SDKAddrLen-1), 0xff), + } + sRandom := stdrand.New(stdrand.NewSource(0)) + for n := 0; n < 100; n++ { + t.Run(fmt.Sprintf("iteration %d", n), func(t *testing.T) { + sRandom.Seed(int64(n)) + sRandom.Shuffle(len(variableLengthAddresses), func(i, j int) { + variableLengthAddresses[i], variableLengthAddresses[j] = variableLengthAddresses[j], variableLengthAddresses[i] + }) + orderedEntries := make([][]types.ContractCodeHistoryEntry, len(variableLengthAddresses)) + + ctx, _ := pCtx.CacheContext() + for j, addr := range variableLengthAddresses { + for i := 0; i < 10; i++ { + var entry types.ContractCodeHistoryEntry + f.RandSource(sRandom).Fuzz(&entry) + k.appendToContractHistory(ctx, addr, entry) + orderedEntries[j] = append(orderedEntries[j], entry) + } + } + // when + for j, addr := range variableLengthAddresses { + gotHistory := k.GetContractHistory(ctx, addr) + assert.Equal(t, orderedEntries[j], gotHistory, "%d: %X", j, addr) + assert.Equal(t, orderedEntries[j][len(orderedEntries[j])-1], k.getLastContractHistoryEntry(ctx, addr)) + } + }) } - // when - gotHistory := keepers.WasmKeeper.GetContractHistory(ctx, contractAddr) - assert.Equal(t, orderedEntries, gotHistory) } func TestCoinBurnerPruneBalances(t *testing.T) {