From 99be1ca85e79af95a1ab182cb7e7cf37c0ac5e3e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 23 Jan 2020 23:27:45 +0100 Subject: [PATCH 1/4] Update data types --- x/wasm/internal/keeper/keeper.go | 2 +- x/wasm/internal/keeper/querier.go | 1 + x/wasm/internal/types/types.go | 23 +++++++++++++---------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 0352735d..2ed25db2 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -130,7 +130,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre } // persist instance - instance := types.NewContractInfo(codeID, creator, string(initMsg)) + instance := types.NewContractInfo(codeID, creator, initMsg) // 0x02 | contractAddress (sdk.AccAddress) -> Instance store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshalBinaryBare(instance)) diff --git a/x/wasm/internal/keeper/querier.go b/x/wasm/internal/keeper/querier.go index 0d54c796..d4099d4d 100644 --- a/x/wasm/internal/keeper/querier.go +++ b/x/wasm/internal/keeper/querier.go @@ -90,6 +90,7 @@ func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.Requ case QueryMethodContractStateAll: for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() { resultData = append(resultData, types.Model{ + // TODO: binary and raw json Key: string(iter.Key()), Value: string(iter.Value()), }) diff --git a/x/wasm/internal/types/types.go b/x/wasm/internal/types/types.go index d06ca78d..047f3299 100644 --- a/x/wasm/internal/types/types.go +++ b/x/wasm/internal/types/types.go @@ -1,6 +1,9 @@ package types import ( + "encoding/json" + tmBytes "github.com/tendermint/tendermint/libs/bytes" + wasmTypes "github.com/confio/go-cosmwasm/types" sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth/exported" @@ -11,16 +14,16 @@ const defaultQueryGasLimit = uint64(3000000) // Model is a struct that holds a KV pair type Model struct { - Key string `json:"key"` - Value string `json:"val"` + Key tmBytes.HexBytes `json:"key"` + Value json.RawMessage `json:"val"` } // CodeInfo is data for the uploaded contract WASM code type CodeInfo struct { - CodeHash []byte `json:"code_hash"` - Creator sdk.AccAddress `json:"creator"` - Source string `json:"source"` - Builder string `json:"builder"` + CodeHash tmBytes.HexBytes `json:"code_hash"` + Creator sdk.AccAddress `json:"creator"` + Source string `json:"source"` + Builder string `json:"builder"` } // NewCodeInfo fills a new Contract struct @@ -35,9 +38,9 @@ func NewCodeInfo(codeHash []byte, creator sdk.AccAddress, source string, builder // ContractInfo stores a WASM contract instance type ContractInfo struct { - CodeID uint64 `json:"code_id"` - Creator sdk.AccAddress `json:"creator"` - InitMsg string `json:"init_msg"` + CodeID uint64 `json:"code_id"` + Creator sdk.AccAddress `json:"creator"` + InitMsg json.RawMessage `json:"init_msg"` } // NewParams initializes params for a contract instance @@ -72,7 +75,7 @@ func NewWasmCoins(cosmosCoins sdk.Coins) (wasmCoins []wasmTypes.Coin) { } // NewContractInfo creates a new instance of a given WASM contract info -func NewContractInfo(codeID uint64, creator sdk.AccAddress, initMsg string) ContractInfo { +func NewContractInfo(codeID uint64, creator sdk.AccAddress, initMsg []byte) ContractInfo { return ContractInfo{ CodeID: codeID, Creator: creator, From 9d2cbc9c669a28fe3512b7c42a7efc406e28efd7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 23 Jan 2020 23:35:40 +0100 Subject: [PATCH 2/4] WIP: fixing types in tests --- x/wasm/internal/keeper/genesis.go | 4 ++-- x/wasm/internal/keeper/keeper.go | 4 ++-- x/wasm/internal/keeper/querier.go | 5 ++--- x/wasm/internal/keeper/querier_test.go | 17 +++++++---------- x/wasm/internal/types/types.go | 8 ++++---- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/x/wasm/internal/keeper/genesis.go b/x/wasm/internal/keeper/genesis.go index ad72c7da..2666e369 100644 --- a/x/wasm/internal/keeper/genesis.go +++ b/x/wasm/internal/keeper/genesis.go @@ -52,8 +52,8 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { var state []types.Model for ; contractStateIterator.Valid(); contractStateIterator.Next() { m := types.Model{ - Key: string(contractStateIterator.Key()), - Value: string(contractStateIterator.Value()), + Key: contractStateIterator.Key(), + Value: contractStateIterator.Value(), } state = append(state, m) } diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 2ed25db2..0758be4e 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -193,8 +193,8 @@ func (k Keeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key [] if val := prefixStore.Get(key); val != nil { return append(result, types.Model{ - Key: string(key), - Value: string(val), + Key: key, + Value: val, }) } return result diff --git a/x/wasm/internal/keeper/querier.go b/x/wasm/internal/keeper/querier.go index d4099d4d..45855941 100644 --- a/x/wasm/internal/keeper/querier.go +++ b/x/wasm/internal/keeper/querier.go @@ -90,9 +90,8 @@ func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.Requ case QueryMethodContractStateAll: for iter := keeper.GetContractState(ctx, contractAddr); iter.Valid(); iter.Next() { resultData = append(resultData, types.Model{ - // TODO: binary and raw json - Key: string(iter.Key()), - Value: string(iter.Value()), + Key: iter.Key(), + Value: iter.Value(), }) } if resultData == nil { diff --git a/x/wasm/internal/keeper/querier_test.go b/x/wasm/internal/keeper/querier_test.go index c3f16779..f26ccd6f 100644 --- a/x/wasm/internal/keeper/querier_test.go +++ b/x/wasm/internal/keeper/querier_test.go @@ -15,10 +15,7 @@ import ( ) func TestQueryContractState(t *testing.T) { - type model struct { - Key string `json:"key"` - Value string `json:"val"` - } + type model = types.Model tempDir, err := ioutil.TempDir("", "wasm") require.NoError(t, err) @@ -48,8 +45,8 @@ func TestQueryContractState(t *testing.T) { require.NoError(t, err) contractModel := []types.Model{ - {Key: "foo", Value: "bar"}, - {Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})}, + {Key: []byte("foo"), Value: []byte("\"bar\"")}, + {Key: []byte{0x0, 0x1}, Value: []byte("{}")}, } keeper.setContractState(ctx, addr, contractModel) @@ -70,21 +67,21 @@ func TestQueryContractState(t *testing.T) { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateAll}, expModelLen: 3, expModelContains: []model{ - {Key: "foo", Value: "bar"}, - {Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})}, + {Key: []byte("foo"), Value: []byte(`"bar"`)}, + {Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}, }, }, "query raw key": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte("foo")}, expModelLen: 1, - expModelContains: []model{{Key: "foo", Value: "bar"}}, + expModelContains: []model{{Key: []byte("foo"), Value: []byte(`["bar"]`)}}, }, "query raw binary key": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte{0x0, 0x1}}, expModelLen: 1, - expModelContains: []model{{Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})}}, + expModelContains: []model{{Key: []byte{0x0, 0x1}, Value: []byte("798")}, }, "query smart": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart}, diff --git a/x/wasm/internal/types/types.go b/x/wasm/internal/types/types.go index 047f3299..2b182ba1 100644 --- a/x/wasm/internal/types/types.go +++ b/x/wasm/internal/types/types.go @@ -20,10 +20,10 @@ type Model struct { // CodeInfo is data for the uploaded contract WASM code type CodeInfo struct { - CodeHash tmBytes.HexBytes `json:"code_hash"` - Creator sdk.AccAddress `json:"creator"` - Source string `json:"source"` - Builder string `json:"builder"` + CodeHash []byte `json:"code_hash"` + Creator sdk.AccAddress `json:"creator"` + Source string `json:"source"` + Builder string `json:"builder"` } // NewCodeInfo fills a new Contract struct From 33c4e92bdfb2fef6fc60b4328fd38ce6eea95e7d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 24 Jan 2020 11:23:10 +0100 Subject: [PATCH 3/4] Fix tests --- x/wasm/internal/keeper/querier.go | 2 +- x/wasm/internal/keeper/querier_test.go | 8 ++++---- x/wasm/module_test.go | 11 +++-------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/x/wasm/internal/keeper/querier.go b/x/wasm/internal/keeper/querier.go index 45855941..f8a946b3 100644 --- a/x/wasm/internal/keeper/querier.go +++ b/x/wasm/internal/keeper/querier.go @@ -104,7 +104,7 @@ func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.Requ default: return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, queryMethod) } - bz, err := json.MarshalIndent(resultData, "", " ") + bz, err := json.Marshal(resultData) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } diff --git a/x/wasm/internal/keeper/querier_test.go b/x/wasm/internal/keeper/querier_test.go index f26ccd6f..d1acdb8d 100644 --- a/x/wasm/internal/keeper/querier_test.go +++ b/x/wasm/internal/keeper/querier_test.go @@ -45,8 +45,8 @@ func TestQueryContractState(t *testing.T) { require.NoError(t, err) contractModel := []types.Model{ - {Key: []byte("foo"), Value: []byte("\"bar\"")}, - {Key: []byte{0x0, 0x1}, Value: []byte("{}")}, + {Key: []byte("foo"), Value: []byte(`"bar"`)}, + {Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}, } keeper.setContractState(ctx, addr, contractModel) @@ -75,13 +75,13 @@ func TestQueryContractState(t *testing.T) { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte("foo")}, expModelLen: 1, - expModelContains: []model{{Key: []byte("foo"), Value: []byte(`["bar"]`)}}, + expModelContains: []model{{Key: []byte("foo"), Value: []byte(`"bar"`)}}, }, "query raw binary key": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte{0x0, 0x1}}, expModelLen: 1, - expModelContains: []model{{Key: []byte{0x0, 0x1}, Value: []byte("798")}, + expModelContains: []model{{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}}, }, "query smart": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart}, diff --git a/x/wasm/module_test.go b/x/wasm/module_test.go index 491d3841..104573c6 100644 --- a/x/wasm/module_test.go +++ b/x/wasm/module_test.go @@ -398,25 +398,20 @@ func assertContractList(t *testing.T, q sdk.Querier, ctx sdk.Context, addrs []st assert.Equal(t, addrs, res) } -type model struct { - Key string `json:"key"` - Value string `json:"val"` -} - func assertContractState(t *testing.T, q sdk.Querier, ctx sdk.Context, addr sdk.AccAddress, expected state) { path := []string{QueryGetContractState, addr.String(), keeper.QueryMethodContractStateAll} bz, sdkerr := q(ctx, path, abci.RequestQuery{}) require.NoError(t, sdkerr) - var res []model + var res []Model err := json.Unmarshal(bz, &res) require.NoError(t, err) require.Equal(t, 1, len(res), "#v", res) - require.Equal(t, "config", res[0].Key) + require.Equal(t, []byte("config"), []byte(res[0].Key)) expectedBz, err := json.Marshal(expected) require.NoError(t, err) - assert.Equal(t, string(expectedBz), res[0].Value) + assert.Equal(t, json.RawMessage(expectedBz), res[0].Value) } func assertContractInfo(t *testing.T, q sdk.Querier, ctx sdk.Context, addr sdk.AccAddress, codeID uint64, creator sdk.AccAddress) { From a8fd5e97da7b7c4a36e8e91d538b3680eb6da616 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 24 Jan 2020 11:31:21 +0100 Subject: [PATCH 4/4] Cleanup and README explanations --- README.md | 10 ++++++++++ x/wasm/internal/keeper/querier_test.go | 16 +++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e4e862b7..4fbaafee 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,16 @@ This code was forked from the `cosmos/gaia` repository and the majority of the c **Compatibility**: Last merge from `cosmos/gaia` was `090c545347b03e59415a18107a0a279c703c8f40` (Jan 23, 2020) +## Stability + +**This is alpha software, do not run on a production system.** Notably, we currently provide **no migration path** not even "dump state and restart" to move to future versions. At **beta** we will begin to offer migrations and better backwards compatibility guarantees. + +With the `v0.6.0` tag, we are entering semver. That means anything with `v0.6.x` tags is compatible. We will have a series of minor version updates prior to `v1.0.0`, where we offer strong backwards compatibility guarantees. In particular, work has begun in the `cosmwasm` library on `v0.7`, which will change many internal APIs, in order to allow adding other languages for writing smart contracts. We hope to stabilize much of this well before `v1`, but we are still in the process of learning from real-world use-cases + +## Encoding + +We use standard cosmos-sdk encoding (amino) for all sdk Messages. However, the message body sent to all contracts, as well as the internal state is encoded using JSON. Cosmwasm allows arbitrary bytes with the contract itself responsible for decodng. For better UX, we often use `json.RawMessage` to contain these bytes, which enforces that it is valid json, but also give a much more readable interface. If you want to use another encoding in the contracts, that is a relatively minor change to wasmd but would currently require a fork. Please open in issue if this is important for your use case. + ## Quick Start ``` diff --git a/x/wasm/internal/keeper/querier_test.go b/x/wasm/internal/keeper/querier_test.go index d1acdb8d..6c77e17d 100644 --- a/x/wasm/internal/keeper/querier_test.go +++ b/x/wasm/internal/keeper/querier_test.go @@ -15,8 +15,6 @@ import ( ) func TestQueryContractState(t *testing.T) { - type model = types.Model - tempDir, err := ioutil.TempDir("", "wasm") require.NoError(t, err) defer os.RemoveAll(tempDir) @@ -55,18 +53,18 @@ func TestQueryContractState(t *testing.T) { specs := map[string]struct { srcPath []string srcReq abci.RequestQuery - // smart queries return raw bytes from contract not []model + // smart queries return raw bytes from contract not []types.Model // if this is set, then we just compare - (should be json encoded string) expSmartRes string - // if success and expSmartRes is not set, we parse into []model and compare + // if success and expSmartRes is not set, we parse into []types.Model and compare expModelLen int - expModelContains []model + expModelContains []types.Model expErr *sdkErrors.Error }{ "query all": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateAll}, expModelLen: 3, - expModelContains: []model{ + expModelContains: []types.Model{ {Key: []byte("foo"), Value: []byte(`"bar"`)}, {Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}, }, @@ -75,13 +73,13 @@ func TestQueryContractState(t *testing.T) { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte("foo")}, expModelLen: 1, - expModelContains: []model{{Key: []byte("foo"), Value: []byte(`"bar"`)}}, + expModelContains: []types.Model{{Key: []byte("foo"), Value: []byte(`"bar"`)}}, }, "query raw binary key": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw}, srcReq: abci.RequestQuery{Data: []byte{0x0, 0x1}}, expModelLen: 1, - expModelContains: []model{{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}}, + expModelContains: []types.Model{{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)}}, }, "query smart": { srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart}, @@ -130,7 +128,7 @@ func TestQueryContractState(t *testing.T) { } // otherwise, check returned models - var r []model + var r []types.Model if spec.expErr == nil { require.NoError(t, json.Unmarshal(binResult, &r)) require.NotNil(t, r)