Skip to content

Commit

Permalink
Merge pull request #142 from CosmWasm/genesis_io_tests
Browse files Browse the repository at this point in the history
Add genesis ex-/import for `lastContractId` sequence
  • Loading branch information
ethanfrey authored Jun 29, 2020
2 parents 7f7afbf + 005c8bf commit 8aad0c9
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect
github.com/cosmos/cosmos-sdk v0.38.3
github.com/golang/mock v1.4.3 // indirect
github.com/google/gofuzz v1.0.0
github.com/gorilla/mux v1.7.4
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
Expand Down
11 changes: 11 additions & 0 deletions x/wasm/internal/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
keeper.setContractState(ctx, contract.ContractAddress, contract.ContractState)
}

for _, seq := range data.Sequences {
keeper.setAutoIncrementID(ctx, seq.IDKey, seq.Value)
}
}

// ExportGenesis returns a GenesisState for a given context and keeper.
Expand Down Expand Up @@ -67,5 +70,13 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
return false
})

// types.KeyLastCodeID is updated via keeper create
for _, k := range [][]byte{types.KeyLastInstanceID} {
genState.Sequences = append(genState.Sequences, types.Sequence{
IDKey: k,
Value: keeper.peekAutoIncrementID(ctx, k),
})
}

return genState
}
92 changes: 92 additions & 0 deletions x/wasm/internal/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package keeper

import (
"io/ioutil"
"os"
"testing"
"time"

"github.com/CosmWasm/wasmd/x/wasm/internal/types"
wasmTypes "github.com/CosmWasm/wasmd/x/wasm/internal/types"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/staking"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)

func TestGenesisExportImport(t *testing.T) {
srcKeeper, srcCtx, srcCleanup := setupKeeper(t)
defer srcCleanup()
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
require.NoError(t, err)

// store some test data
f := fuzz.New().Funcs(FuzzAddr, FuzzAbsoluteTxPosition, FuzzContractInfo, FuzzStateModel)
for i := 0; i < 25; i++ {
var (
codeInfo types.CodeInfo
contract types.ContractInfo
stateModels []types.Model
)
f.Fuzz(&codeInfo)
f.Fuzz(&contract)
f.Fuzz(&stateModels)

codeID, err := srcKeeper.Create(srcCtx, codeInfo.Creator, wasmCode, codeInfo.Source, codeInfo.Builder)
require.NoError(t, err)
contract.CodeID = codeID
contractAddr := srcKeeper.generateContractAddress(srcCtx, codeID)
srcKeeper.setContractInfo(srcCtx, contractAddr, &contract)
srcKeeper.setContractState(srcCtx, contractAddr, stateModels)
}

// export
genesisState := ExportGenesis(srcCtx, srcKeeper)

// re-import
dstKeeper, dstCtx, dstCleanup := setupKeeper(t)
defer dstCleanup()
InitGenesis(dstCtx, dstKeeper, genesisState)

// compare whole DB
srcIT := srcCtx.KVStore(srcKeeper.storeKey).Iterator(nil, nil)
dstIT := dstCtx.KVStore(dstKeeper.storeKey).Iterator(nil, nil)

for i := 0; srcIT.Valid(); i++ {
require.True(t, dstIT.Valid(), "destination DB has less elements than source. Missing: %q", srcIT.Key())
require.Equal(t, srcIT.Key(), dstIT.Key(), i)
require.Equal(t, srcIT.Value(), dstIT.Value(), "element (%d): %s", i, srcIT.Key())
srcIT.Next()
dstIT.Next()
}
require.False(t, dstIT.Valid())
}

func setupKeeper(t *testing.T) (Keeper, sdk.Context, func()) {
tempDir, err := ioutil.TempDir("", "wasm")
require.NoError(t, err)
cleanup := func() { os.RemoveAll(tempDir) }
//t.Cleanup(cleanup) todo: add with Go 1.14

keyContract := sdk.NewKVStoreKey(wasmTypes.StoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyContract, sdk.StoreTypeIAVL, db)
require.NoError(t, ms.LoadLatestVersion())

ctx := sdk.NewContext(ms, abci.Header{
Height: 1234567,
Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC),
}, false, log.NewNopLogger())

cdc := MakeTestCodec()
wasmConfig := wasmTypes.DefaultWasmConfig()

srcKeeper := NewKeeper(cdc, keyContract, auth.AccountKeeper{}, nil, staking.Keeper{}, nil, tempDir, wasmConfig, "", nil, nil)
return srcKeeper, ctx, cleanup
}
17 changes: 17 additions & 0 deletions x/wasm/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,23 @@ func (k Keeper) autoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 {
return id
}

// peekAutoIncrementID reads the current value without incrementing it.
func (k Keeper) peekAutoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 {
store := ctx.KVStore(k.storeKey)
bz := store.Get(lastIDKey)
id := uint64(1)
if bz != nil {
id = binary.BigEndian.Uint64(bz)
}
return id
}

func (k Keeper) setAutoIncrementID(ctx sdk.Context, lastIDKey []byte, val uint64) {
store := ctx.KVStore(k.storeKey)
bz := sdk.Uint64ToBigEndian(val)
store.Set(lastIDKey, bz)
}

func addrFromUint64(id uint64) sdk.AccAddress {
addr := make([]byte, 20)
addr[0] = 'C'
Expand Down
34 changes: 34 additions & 0 deletions x/wasm/internal/keeper/test_fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package keeper

import (
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
sdk "github.com/cosmos/cosmos-sdk/types"
fuzz "github.com/google/gofuzz"
tmBytes "github.com/tendermint/tendermint/libs/bytes"
)

func FuzzAddr(m *sdk.AccAddress, c fuzz.Continue) {
*m = make([]byte, 20)
c.Read(*m)
}
func FuzzAbsoluteTxPosition(m *types.AbsoluteTxPosition, c fuzz.Continue) {
m.BlockHeight = int64(c.RandUint64()) // can't be negative
m.TxIndex = c.RandUint64()
}

func FuzzContractInfo(m *types.ContractInfo, c fuzz.Continue) {
const maxSize = 1024
m.CodeID = c.RandUint64()
FuzzAddr(&m.Creator, c)
FuzzAddr(&m.Admin, c)
m.Label = c.RandString()
m.InitMsg = make([]byte, c.RandUint64()%maxSize)
c.Read(m.InitMsg)
c.Fuzz(&m.Created)
c.Fuzz(&m.LastUpdated)
m.PreviousCodeID = c.RandUint64()
}
func FuzzStateModel(m *types.Model, c fuzz.Continue) {
m.Key = tmBytes.HexBytes(c.RandString())
c.Fuzz(&m.Value)
}
6 changes: 6 additions & 0 deletions x/wasm/internal/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package types

import sdk "github.com/cosmos/cosmos-sdk/types"

type Sequence struct {
IDKey []byte `json:"id_key"`
Value uint64 `json:"value"`
}

// GenesisState is the struct representation of the export genesis
type GenesisState struct {
Codes []Code `json:"codes"`
Contracts []Contract `json:"contracts"`
Sequences []Sequence `json:"sequences"`
}

// Code struct encompasses CodeInfo and CodeBytes
Expand Down

0 comments on commit 8aad0c9

Please sign in to comment.