Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add state sync support #823

Merged
merged 22 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3080845
Add WasmSnapshotter
assafmo Apr 25, 2022
870bba8
Make snapshot from keeper
ethanfrey Apr 27, 2022
abfadce
Rough draft of the restore method using multistore
ethanfrey Apr 27, 2022
f789aed
properly register the snapshot manager
ethanfrey Apr 27, 2022
9952475
Move snapshotter to keeper and implement
ethanfrey Apr 27, 2022
1b3c133
Add compression to snapshot wasm bytecode
ethanfrey Apr 27, 2022
4cccddb
Put GzipIt and Uncompress logic in a common folder
ethanfrey Apr 27, 2022
f68ef90
Fix linting errors
ethanfrey Apr 27, 2022
abad37d
Avoid sending duplicate wasm codes over the wire
ethanfrey Apr 27, 2022
d2b0fb5
Properly handle when a message not for this extension arrives
ethanfrey Apr 27, 2022
a5dd494
Start testing snapshots
ethanfrey Apr 27, 2022
fea9d39
Basic tend to end test for no errors
ethanfrey Apr 27, 2022
9705a8d
Address @giansalex PR comments
ethanfrey Apr 27, 2022
d8426fb
Updates inspired by @giansalex branch
ethanfrey Apr 28, 2022
7663d6f
Cannot commit in my test cases to load from proper height
ethanfrey Apr 28, 2022
a154a9a
SRemove loading history until I can fix tests here
ethanfrey Apr 28, 2022
cdcb18d
Cleanup from Simon's comments
ethanfrey Apr 28, 2022
6b4accb
Add snapshotter integration tests and minor cleanup
alpe May 3, 2022
297a646
Minor test update
alpe May 3, 2022
a5695ee
Fix linter warnings
alpe May 3, 2022
cb2e8f2
Merge pull request #834 from CosmWasm/add-state-sync_integration
ethanfrey May 3, 2022
1253c8e
Merge branch 'main' into add-state-sync
ethanfrey May 4, 2022
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
20 changes: 17 additions & 3 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ import (
wasmappparams "github.com/CosmWasm/wasmd/app/params"
"github.com/CosmWasm/wasmd/x/wasm"
wasmclient "github.com/CosmWasm/wasmd/x/wasm/client"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"

// unnamed import of statik for swagger UI support
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
Expand Down Expand Up @@ -667,6 +668,22 @@ func NewWasmApp(
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)

// must be before Loading version
// requires the snapshot store to be created and registered as a BaseAppOption
// see cmd/wasmd/root.go: 206 - 214 approx
if manager := app.SnapshotManager(); manager != nil {
err := manager.RegisterExtensions(
wasmkeeper.NewWasmSnapshotter(app.CommitMultiStore(), &app.wasmKeeper),
)
if err != nil {
panic(fmt.Errorf("failed to register snapshot extension: %s", err))
}
}

app.scopedIBCKeeper = scopedIBCKeeper
app.scopedTransferKeeper = scopedTransferKeeper
app.scopedWasmKeeper = scopedWasmKeeper

if loadLatest {
if err := app.LoadLatestVersion(); err != nil {
tmos.Exit(fmt.Sprintf("failed to load latest version: %s", err))
Expand All @@ -679,9 +696,6 @@ func NewWasmApp(
}
}

app.scopedIBCKeeper = scopedIBCKeeper
app.scopedTransferKeeper = scopedTransferKeeper
app.scopedWasmKeeper = scopedWasmKeeper
return app
}

Expand Down
93 changes: 25 additions & 68 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,34 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"path/filepath"
"strconv"
"testing"
"time"

bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/CosmWasm/wasmd/x/wasm"

bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp/helpers"
"github.com/cosmos/cosmos-sdk/snapshots"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/CosmWasm/wasmd/x/wasm"
)

// DefaultConsensusParams defines the default Tendermint consensus params used in
Expand All @@ -53,44 +54,28 @@ var DefaultConsensusParams = &abci.ConsensusParams{
},
}

func setup(withGenesis bool, invCheckPeriod uint, opts ...wasm.Option) (*WasmApp, GenesisState) {
func setup(t testing.TB, withGenesis bool, invCheckPeriod uint, opts ...wasm.Option) (*WasmApp, GenesisState) {
nodeHome := t.TempDir()
snapshotDir := filepath.Join(nodeHome, "data", "snapshots")
snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir)
require.NoError(t, err)
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
require.NoError(t, err)
baseAppOpts := []func(*bam.BaseApp){bam.SetSnapshotStore(snapshotStore), bam.SetSnapshotKeepRecent(2)}
db := dbm.NewMemDB()
app := NewWasmApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, MakeEncodingConfig(), wasm.EnableAllProposals, EmptyBaseAppOptions{}, opts)
app := NewWasmApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, nodeHome, invCheckPeriod, MakeEncodingConfig(), wasm.EnableAllProposals, EmptyBaseAppOptions{}, opts, baseAppOpts...)
if withGenesis {
return app, NewDefaultGenesisState()
}
return app, GenesisState{}
}

// Setup initializes a new WasmApp. A Nop logger is set in WasmApp.
func Setup(isCheckTx bool) *WasmApp {
app, genesisState := setup(!isCheckTx, 5)
if !isCheckTx {
// init chain must be called to stop deliverState from being nil
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

// Initialize the chain
app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
}

return app
}

// SetupWithGenesisValSet initializes a new WasmApp with a validator set and genesis accounts
// that also act as delegators. For simplicity, each validator is bonded with a delegation
// of one consensus engine unit (10^6) in the default token of the WasmApp from first genesis
// account. A Nop logger is set in WasmApp.
func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, opts []wasm.Option, balances ...banktypes.Balance) *WasmApp {
app, genesisState := setup(true, 5, opts...)
app, genesisState := setup(t, true, 5, opts...)
// set genesis accounts
authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs)
genesisState[authtypes.ModuleName] = app.appCodec.MustMarshalJSON(authGenesis)
Expand Down Expand Up @@ -167,37 +152,9 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs
return app
}

// SetupWithGenesisAccounts initializes a new WasmApp with the provided genesis
// accounts and possible balances.
func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *WasmApp {
app, genesisState := setup(true, 0)
authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs)
genesisState[authtypes.ModuleName] = app.appCodec.MustMarshalJSON(authGenesis)

totalSupply := sdk.NewCoins()
for _, b := range balances {
totalSupply = totalSupply.Add(b.Coins...)
}

bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{})
genesisState[banktypes.ModuleName] = app.appCodec.MustMarshalJSON(bankGenesis)

stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

app.InitChain(
abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)

app.Commit()
app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}})

// SetupWithEmptyStore setup a wasmd app instance with empty DB
func SetupWithEmptyStore(t testing.TB) *WasmApp {
app, _ := setup(t, false, 0)
return app
}

Expand Down
8 changes: 4 additions & 4 deletions x/wasm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

wasmUtils "github.com/CosmWasm/wasmd/x/wasm/client/utils"
"github.com/CosmWasm/wasmd/x/wasm/ioutils"
"github.com/CosmWasm/wasmd/x/wasm/types"
)

Expand Down Expand Up @@ -87,13 +87,13 @@ func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet)
}

// gzip the wasm file
if wasmUtils.IsWasm(wasm) {
wasm, err = wasmUtils.GzipIt(wasm)
if ioutils.IsWasm(wasm) {
wasm, err = ioutils.GzipIt(wasm)

if err != nil {
return types.MsgStoreCode{}, err
}
} else if !wasmUtils.IsGzip(wasm) {
} else if !ioutils.IsGzip(wasm) {
return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip")
}

Expand Down
8 changes: 4 additions & 4 deletions x/wasm/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/gorilla/mux"

wasmUtils "github.com/CosmWasm/wasmd/x/wasm/client/utils"
"github.com/CosmWasm/wasmd/x/wasm/ioutils"
"github.com/CosmWasm/wasmd/x/wasm/types"
)

Expand Down Expand Up @@ -55,13 +55,13 @@ func storeCodeHandlerFn(cliCtx client.Context) http.HandlerFunc {
wasm := req.WasmBytes

// gzip the wasm file
if wasmUtils.IsWasm(wasm) {
wasm, err = wasmUtils.GzipIt(wasm)
if ioutils.IsWasm(wasm) {
wasm, err = ioutils.GzipIt(wasm)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
} else if !wasmUtils.IsGzip(wasm) {
} else if !ioutils.IsGzip(wasm) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "Invalid input file, use wasm binary or zip")
return
}
Expand Down
11 changes: 3 additions & 8 deletions x/wasm/keeper/ioutil.go → x/wasm/ioutils/ioutil.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package keeper
package ioutils

import (
"bytes"
Expand All @@ -9,13 +9,8 @@ import (
"github.com/CosmWasm/wasmd/x/wasm/types"
)

// magic bytes to identify gzip.
// See https://www.ietf.org/rfc/rfc1952.txt
// and https://github.com/golang/go/blob/master/src/net/http/sniff.go#L186
var gzipIdent = []byte("\x1F\x8B\x08")

// uncompress returns gzip uncompressed content or given src when not gzip.
func uncompress(src []byte, limit uint64) ([]byte, error) {
// Uncompress returns gzip uncompressed content if input was gzip, or original src otherwise
func Uncompress(src []byte, limit uint64) ([]byte, error) {
switch n := uint64(len(src)); {
case n < 3:
return src, nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package keeper
package ioutils

import (
"bytes"
Expand All @@ -16,10 +16,10 @@ import (
)

func TestUncompress(t *testing.T) {
wasmRaw, err := ioutil.ReadFile("./testdata/hackatom.wasm")
wasmRaw, err := ioutil.ReadFile("../keeper/testdata/hackatom.wasm")
require.NoError(t, err)

wasmGzipped, err := ioutil.ReadFile("./testdata/hackatom.wasm.gzip")
wasmGzipped, err := ioutil.ReadFile("../keeper/testdata/hackatom.wasm.gzip")
require.NoError(t, err)

const maxSize = 400_000
Expand Down Expand Up @@ -80,7 +80,7 @@ func TestUncompress(t *testing.T) {
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
r, err := uncompress(spec.src, maxSize)
r, err := Uncompress(spec.src, maxSize)
require.True(t, errors.Is(spec.expError, err), "exp %v got %+v", spec.expError, err)
if spec.expError != nil {
return
Expand Down
7 changes: 6 additions & 1 deletion x/wasm/client/utils/utils.go → x/wasm/ioutils/utils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package utils
package ioutils

import (
"bytes"
"compress/gzip"
)

// Note: []byte can never be const as they are inherently mutable
var (
// magic bytes to identify gzip.
// See https://www.ietf.org/rfc/rfc1952.txt
// and https://github.com/golang/go/blob/master/src/net/http/sniff.go#L186
gzipIdent = []byte("\x1F\x8B\x08")
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved

wasmIdent = []byte("\x00\x61\x73\x6D")
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package utils
package ioutils

import (
"io/ioutil"
Expand All @@ -8,7 +8,7 @@ import (
)

func GetTestData() ([]byte, []byte, []byte, error) {
wasmCode, err := ioutil.ReadFile("../../keeper/testdata/hackatom.wasm")
wasmCode, err := ioutil.ReadFile("../keeper/testdata/hackatom.wasm")

if err != nil {
return nil, nil, nil, err
Expand Down
5 changes: 3 additions & 2 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/tendermint/tendermint/libs/log"

"github.com/CosmWasm/wasmd/x/wasm/ioutils"
"github.com/CosmWasm/wasmd/x/wasm/types"
)

Expand Down Expand Up @@ -158,7 +159,7 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) {
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
}
wasmCode, err = uncompress(wasmCode, uint64(types.MaxWasmSize))
wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
Expand Down Expand Up @@ -200,7 +201,7 @@ func (k Keeper) storeCodeInfo(ctx sdk.Context, codeID uint64, codeInfo types.Cod
}

func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo, wasmCode []byte) error {
wasmCode, err := uncompress(wasmCode, uint64(types.MaxWasmSize))
wasmCode, err := ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
Expand Down
Loading