mirror of https://github.com/certusone/wasmd.git
Merge pull request #168 from CosmWasm/genesis_io_codeids
Genesis code import not position agnostic
This commit is contained in:
commit
3f62e8a001
|
@ -1,8 +1,6 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
@ -14,23 +12,24 @@ import (
|
|||
//
|
||||
// CONTRACT: all types of accounts must have been already initialized/created
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error {
|
||||
var maxCodeID uint64
|
||||
for i, code := range data.Codes {
|
||||
newId, err := keeper.Create(ctx, code.CodeInfo.Creator, code.CodesBytes, code.CodeInfo.Source, code.CodeInfo.Builder)
|
||||
err := keeper.importCode(ctx, code.CodeID, code.CodeInfo, code.CodesBytes)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrapf(err, "code number %d", i)
|
||||
|
||||
return sdkerrors.Wrapf(err, "code %d with id: %d", i, code.CodeID)
|
||||
}
|
||||
newInfo := keeper.GetCodeInfo(ctx, newId)
|
||||
if !bytes.Equal(code.CodeInfo.CodeHash, newInfo.CodeHash) {
|
||||
return sdkerrors.Wrap(types.ErrInvalid, "code hashes not same")
|
||||
if code.CodeID > maxCodeID {
|
||||
maxCodeID = code.CodeID
|
||||
}
|
||||
}
|
||||
|
||||
var maxContractID int
|
||||
for i, contract := range data.Contracts {
|
||||
err := keeper.importContract(ctx, contract.ContractAddress, &contract.ContractInfo, contract.ContractState)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrapf(err, "contract number %d", i)
|
||||
}
|
||||
maxContractID = i + 1 // not ideal but max(contractID) is not persisted otherwise
|
||||
}
|
||||
|
||||
for i, seq := range data.Sequences {
|
||||
|
@ -39,6 +38,15 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
|
|||
return sdkerrors.Wrapf(err, "sequence number %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check seq values
|
||||
if keeper.peekAutoIncrementID(ctx, types.KeyLastCodeID) <= maxCodeID {
|
||||
return sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastCodeID), maxCodeID)
|
||||
}
|
||||
if keeper.peekAutoIncrementID(ctx, types.KeyLastInstanceID) <= uint64(maxContractID) {
|
||||
return sdkerrors.Wrapf(types.ErrInvalid, "seq %s must be greater %d ", string(types.KeyLastInstanceID), maxContractID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -48,11 +56,15 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
|||
|
||||
maxCodeID := keeper.GetNextCodeID(ctx)
|
||||
for i := uint64(1); i < maxCodeID; i++ {
|
||||
if !keeper.containsCodeInfo(ctx, i) {
|
||||
continue
|
||||
}
|
||||
bytecode, err := keeper.GetByteCode(ctx, i)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genState.Codes = append(genState.Codes, types.Code{
|
||||
CodeID: i,
|
||||
CodeInfo: *keeper.GetCodeInfo(ctx, i),
|
||||
CodesBytes: bytecode,
|
||||
})
|
||||
|
@ -78,8 +90,7 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
|||
return false
|
||||
})
|
||||
|
||||
// types.KeyLastCodeID is updated via keeper create
|
||||
for _, k := range [][]byte{types.KeyLastInstanceID} {
|
||||
for _, k := range [][]byte{types.KeyLastCodeID, types.KeyLastInstanceID} {
|
||||
genState.Sequences = append(genState.Sequences, types.Sequence{
|
||||
IDKey: k,
|
||||
Value: keeper.peekAutoIncrementID(ctx, k),
|
||||
|
|
|
@ -3,6 +3,7 @@ package keeper
|
|||
import (
|
||||
"crypto/sha256"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -49,9 +50,21 @@ func TestGenesisExportImport(t *testing.T) {
|
|||
// export
|
||||
genesisState := ExportGenesis(srcCtx, srcKeeper)
|
||||
|
||||
// order should not matter
|
||||
rand.Shuffle(len(genesisState.Codes), func(i, j int) {
|
||||
genesisState.Codes[i], genesisState.Codes[j] = genesisState.Codes[j], genesisState.Codes[i]
|
||||
})
|
||||
rand.Shuffle(len(genesisState.Contracts), func(i, j int) {
|
||||
genesisState.Contracts[i], genesisState.Contracts[j] = genesisState.Contracts[j], genesisState.Contracts[i]
|
||||
})
|
||||
rand.Shuffle(len(genesisState.Sequences), func(i, j int) {
|
||||
genesisState.Sequences[i], genesisState.Sequences[j] = genesisState.Sequences[j], genesisState.Sequences[i]
|
||||
})
|
||||
|
||||
// re-import
|
||||
dstKeeper, dstCtx, dstCleanup := setupKeeper(t)
|
||||
defer dstCleanup()
|
||||
|
||||
InitGenesis(dstCtx, dstKeeper, genesisState)
|
||||
|
||||
// compare whole DB
|
||||
|
@ -81,6 +94,7 @@ func TestFailFastImport(t *testing.T) {
|
|||
"happy path: code info correct": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
@ -88,11 +102,66 @@ func TestFailFastImport(t *testing.T) {
|
|||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: nil,
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 2},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 1},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"happy path: code ids can contain gaps": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}, {
|
||||
CodeID: 3,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: nil,
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 10},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 1},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"happy path: code order does not matter": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 2,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}, {
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: nil,
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 3},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 1},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"prevent code hash mismatch": {src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: make([]byte, len(codeHash)),
|
||||
Creator: anyAddress,
|
||||
|
@ -101,9 +170,30 @@ func TestFailFastImport(t *testing.T) {
|
|||
}},
|
||||
Contracts: nil,
|
||||
}},
|
||||
"prevent duplicate codeIDs": {src: types.GenesisState{
|
||||
Codes: []types.Code{
|
||||
{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
},
|
||||
{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
},
|
||||
},
|
||||
}},
|
||||
"happy path: code id in info and contract do match": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
@ -116,12 +206,17 @@ func TestFailFastImport(t *testing.T) {
|
|||
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
|
||||
},
|
||||
},
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 2},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 2},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"happy path: code info with two contracts": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
@ -133,10 +228,14 @@ func TestFailFastImport(t *testing.T) {
|
|||
ContractAddress: contractAddress(1, 1),
|
||||
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
|
||||
}, {
|
||||
ContractAddress: contractAddress(2, 1),
|
||||
ContractAddress: contractAddress(1, 2),
|
||||
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
|
||||
},
|
||||
},
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 2},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 3},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
|
@ -153,6 +252,7 @@ func TestFailFastImport(t *testing.T) {
|
|||
"prevent duplicate contract address": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
@ -173,6 +273,7 @@ func TestFailFastImport(t *testing.T) {
|
|||
"prevent duplicate contract model keys": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
@ -205,6 +306,43 @@ func TestFailFastImport(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"prevent code id seq init value == max codeID used": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 2,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
"prevent contract id seq init value == count contracts": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeID: 1,
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: []types.Contract{
|
||||
{
|
||||
ContractAddress: contractAddress(1, 1),
|
||||
ContractInfo: types.ContractInfoFixture(func(c *wasmTypes.ContractInfo) { c.CodeID = 1 }),
|
||||
},
|
||||
},
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: types.KeyLastCodeID, Value: 2},
|
||||
{IDKey: types.KeyLastInstanceID, Value: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for msg, spec := range specs {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"path/filepath"
|
||||
|
||||
|
@ -97,6 +98,29 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
|
|||
return codeID, nil
|
||||
}
|
||||
|
||||
func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo, wasmCode []byte) error {
|
||||
wasmCode, err := uncompress(wasmCode)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
|
||||
}
|
||||
newCodeHash, err := k.wasmer.Create(wasmCode)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
|
||||
}
|
||||
if !bytes.Equal(codeInfo.CodeHash, newCodeHash) {
|
||||
return sdkerrors.Wrap(types.ErrInvalid, "code hashes not same")
|
||||
}
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := types.GetCodeKey(codeID)
|
||||
if store.Has(key) {
|
||||
return sdkerrors.Wrapf(types.ErrDuplicate, "duplicate code: %d", codeID)
|
||||
}
|
||||
// 0x01 | codeID (uint64) -> ContractInfo
|
||||
store.Set(key, k.cdc.MustMarshalBinaryBare(codeInfo))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Instantiate creates an instance of a WASM contract
|
||||
func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins) (sdk.AccAddress, error) {
|
||||
ctx.GasMeter().ConsumeGas(InstanceCost, "Loading CosmWasm module: init")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package types
|
||||
|
||||
import "C"
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
@ -45,11 +46,15 @@ func (s GenesisState) ValidateBasic() error {
|
|||
|
||||
// Code struct encompasses CodeInfo and CodeBytes
|
||||
type Code struct {
|
||||
CodeID uint64 `json:"code_id"`
|
||||
CodeInfo CodeInfo `json:"code_info"`
|
||||
CodesBytes []byte `json:"code_bytes"`
|
||||
}
|
||||
|
||||
func (c Code) ValidateBasic() error {
|
||||
if c.CodeID == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "code id")
|
||||
}
|
||||
if err := c.CodeInfo.ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrap(err, "code info")
|
||||
}
|
||||
|
|
|
@ -53,6 +53,12 @@ func TestCodeValidateBasic(t *testing.T) {
|
|||
expError bool
|
||||
}{
|
||||
"all good": {srcMutator: func(_ *Code) {}},
|
||||
"code id invalid": {
|
||||
srcMutator: func(c *Code) {
|
||||
c.CodeID = 0
|
||||
},
|
||||
expError: true,
|
||||
},
|
||||
"codeinfo invalid": {
|
||||
srcMutator: func(c *Code) {
|
||||
c.CodeInfo.CodeHash = nil
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -32,11 +34,15 @@ var (
|
|||
)
|
||||
|
||||
// GetCodeKey constructs the key for retreiving the ID for the WASM code
|
||||
func GetCodeKey(contractID uint64) []byte {
|
||||
contractIDBz := sdk.Uint64ToBigEndian(contractID)
|
||||
func GetCodeKey(codeID uint64) []byte {
|
||||
contractIDBz := sdk.Uint64ToBigEndian(codeID)
|
||||
return append(CodeKeyPrefix, contractIDBz...)
|
||||
}
|
||||
|
||||
func decodeCodeKey(src []byte) uint64 {
|
||||
return binary.BigEndian.Uint64(src[len(CodeKeyPrefix):])
|
||||
}
|
||||
|
||||
// GetContractAddressKey returns the key for the WASM contract instance
|
||||
func GetContractAddressKey(addr sdk.AccAddress) []byte {
|
||||
return append(ContractKeyPrefix, addr...)
|
||||
|
|
|
@ -43,6 +43,7 @@ func CodeFixture(mutators ...func(*Code)) Code {
|
|||
anyAddress := make([]byte, 20)
|
||||
|
||||
fixture := Code{
|
||||
CodeID: 1,
|
||||
CodeInfo: CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
|
|
Loading…
Reference in New Issue