Validate genesis model
This commit is contained in:
@@ -25,12 +25,11 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
|
||||
}
|
||||
|
||||
for _, contract := range data.Contracts {
|
||||
keeper.setContractInfo(ctx, contract.ContractAddress, &contract.ContractInfo)
|
||||
keeper.setContractState(ctx, contract.ContractAddress, contract.ContractState)
|
||||
keeper.importContract(ctx, contract.ContractAddress, &contract.ContractInfo, contract.ContractState)
|
||||
}
|
||||
|
||||
for _, seq := range data.Sequences {
|
||||
keeper.setAutoIncrementID(ctx, seq.IDKey, seq.Value)
|
||||
keeper.importAutoIncrementID(ctx, seq.IDKey, seq.Value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -42,7 +43,7 @@ func TestGenesisExportImport(t *testing.T) {
|
||||
contract.CodeID = codeID
|
||||
contractAddr := srcKeeper.generateContractAddress(srcCtx, codeID)
|
||||
srcKeeper.setContractInfo(srcCtx, contractAddr, &contract)
|
||||
srcKeeper.setContractState(srcCtx, contractAddr, stateModels)
|
||||
srcKeeper.importContractState(srcCtx, contractAddr, stateModels)
|
||||
}
|
||||
|
||||
// export
|
||||
@@ -67,6 +68,166 @@ func TestGenesisExportImport(t *testing.T) {
|
||||
require.False(t, dstIT.Valid())
|
||||
}
|
||||
|
||||
func TestFailFastImport(t *testing.T) {
|
||||
wasmCode, err := ioutil.ReadFile("./testdata/contract.wasm")
|
||||
require.NoError(t, err)
|
||||
codeHash := sha256.Sum256(wasmCode)
|
||||
anyAddress := make([]byte, 20)
|
||||
|
||||
specs := map[string]struct {
|
||||
src types.GenesisState
|
||||
expSuccess bool
|
||||
}{
|
||||
"happy path: code info correct": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: nil,
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"prevent code hash mismatch": {src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: make([]byte, len(codeHash)),
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: nil,
|
||||
}},
|
||||
"happy path: code info and contract do match": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: []types.Contract{
|
||||
{
|
||||
ContractAddress: addrFromUint64(1<<32 + 1),
|
||||
ContractInfo: wasmTypes.ContractInfo{
|
||||
CodeID: 1,
|
||||
Creator: anyAddress,
|
||||
Label: "any",
|
||||
Created: &types.AbsoluteTxPosition{BlockHeight: 1, TxIndex: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expSuccess: true,
|
||||
},
|
||||
"prevent contracts that points to non existing codeID": {
|
||||
src: types.GenesisState{
|
||||
Contracts: []types.Contract{
|
||||
{
|
||||
ContractAddress: addrFromUint64(1<<32 + 1),
|
||||
ContractInfo: wasmTypes.ContractInfo{
|
||||
CodeID: 1,
|
||||
Creator: anyAddress,
|
||||
Label: "any",
|
||||
Created: &types.AbsoluteTxPosition{BlockHeight: 1, TxIndex: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"prevent duplicate contracts": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: []types.Contract{
|
||||
{
|
||||
ContractAddress: addrFromUint64(1<<32 + 1),
|
||||
ContractInfo: wasmTypes.ContractInfo{
|
||||
CodeID: 1,
|
||||
Creator: anyAddress,
|
||||
Label: "any",
|
||||
Created: &types.AbsoluteTxPosition{BlockHeight: 1, TxIndex: 1},
|
||||
},
|
||||
}, {
|
||||
ContractAddress: addrFromUint64(1<<32 + 1),
|
||||
ContractInfo: wasmTypes.ContractInfo{
|
||||
CodeID: 1,
|
||||
Creator: anyAddress,
|
||||
Label: "any",
|
||||
Created: &types.AbsoluteTxPosition{BlockHeight: 1, TxIndex: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"prevent duplicate contract model": {
|
||||
src: types.GenesisState{
|
||||
Codes: []types.Code{{
|
||||
CodeInfo: wasmTypes.CodeInfo{
|
||||
CodeHash: codeHash[:],
|
||||
Creator: anyAddress,
|
||||
},
|
||||
CodesBytes: wasmCode,
|
||||
}},
|
||||
Contracts: []types.Contract{
|
||||
{
|
||||
ContractAddress: addrFromUint64(1<<32 + 1),
|
||||
ContractInfo: wasmTypes.ContractInfo{
|
||||
CodeID: 1,
|
||||
Creator: anyAddress,
|
||||
Label: "any",
|
||||
Created: &types.AbsoluteTxPosition{BlockHeight: 1, TxIndex: 1},
|
||||
},
|
||||
ContractState: []types.Model{
|
||||
{
|
||||
Key: []byte{0x1},
|
||||
Value: []byte("foo"),
|
||||
},
|
||||
{
|
||||
Key: []byte{0x1},
|
||||
Value: []byte("bar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"prevent duplicate sequences": {
|
||||
src: types.GenesisState{
|
||||
Sequences: []types.Sequence{
|
||||
{IDKey: []byte("foo"), Value: 1},
|
||||
{IDKey: []byte("foo"), Value: 9999},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for msg, spec := range specs {
|
||||
t.Run(msg, func(t *testing.T) {
|
||||
keeper, ctx, cleanup := setupKeeper(t)
|
||||
defer cleanup()
|
||||
|
||||
require.NoError(t, types.ValidateGenesis(spec.src))
|
||||
if spec.expSuccess {
|
||||
InitGenesis(ctx, keeper, spec.src)
|
||||
return
|
||||
}
|
||||
require.Panics(t, func() {
|
||||
InitGenesis(ctx, keeper, spec.src)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupKeeper(t *testing.T) (Keeper, sdk.Context, func()) {
|
||||
tempDir, err := ioutil.TempDir("", "wasm")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2,6 +2,7 @@ package keeper
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
@@ -374,6 +375,11 @@ func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress)
|
||||
return &contract
|
||||
}
|
||||
|
||||
func (k Keeper) containsContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Has(types.GetContractAddressKey(contractAddress))
|
||||
}
|
||||
|
||||
func (k Keeper) setContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress, contract *types.ContractInfo) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Set(types.GetContractAddressKey(contractAddress), k.cdc.MustMarshalBinaryBare(contract))
|
||||
@@ -398,13 +404,16 @@ func (k Keeper) GetContractState(ctx sdk.Context, contractAddress sdk.AccAddress
|
||||
return prefixStore.Iterator(nil, nil)
|
||||
}
|
||||
|
||||
func (k Keeper) setContractState(ctx sdk.Context, contractAddress sdk.AccAddress, models []types.Model) {
|
||||
func (k Keeper) importContractState(ctx sdk.Context, contractAddress sdk.AccAddress, models []types.Model) {
|
||||
prefixStoreKey := types.GetContractStorePrefixKey(contractAddress)
|
||||
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)
|
||||
for _, model := range models {
|
||||
if model.Value == nil {
|
||||
model.Value = []byte{}
|
||||
}
|
||||
if prefixStore.Has(model.Key) {
|
||||
panic(fmt.Sprintf("duplicate key: %x", model.Key))
|
||||
}
|
||||
prefixStore.Set(model.Key, model.Value)
|
||||
}
|
||||
}
|
||||
@@ -420,6 +429,11 @@ func (k Keeper) GetCodeInfo(ctx sdk.Context, codeID uint64) *types.CodeInfo {
|
||||
return &codeInfo
|
||||
}
|
||||
|
||||
func (k Keeper) containsCodeInfo(ctx sdk.Context, codeID uint64) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Has(types.GetCodeKey(codeID))
|
||||
}
|
||||
|
||||
func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
var codeInfo types.CodeInfo
|
||||
@@ -496,12 +510,26 @@ func (k Keeper) peekAutoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 {
|
||||
return id
|
||||
}
|
||||
|
||||
func (k Keeper) setAutoIncrementID(ctx sdk.Context, lastIDKey []byte, val uint64) {
|
||||
func (k Keeper) importAutoIncrementID(ctx sdk.Context, lastIDKey []byte, val uint64) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
if store.Has(lastIDKey) {
|
||||
panic(fmt.Sprintf("duplicate autoincrement id: %s", string(lastIDKey)))
|
||||
}
|
||||
bz := sdk.Uint64ToBigEndian(val)
|
||||
store.Set(lastIDKey, bz)
|
||||
}
|
||||
|
||||
func (k Keeper) importContract(ctx sdk.Context, address sdk.AccAddress, c *types.ContractInfo, state []types.Model) {
|
||||
if !k.containsCodeInfo(ctx, c.CodeID) {
|
||||
panic(fmt.Sprintf("unknown code id: %d", c.CodeID))
|
||||
}
|
||||
if k.containsContractInfo(ctx, address) {
|
||||
panic(fmt.Sprintf("duplicate contract: %s", address))
|
||||
}
|
||||
k.setContractInfo(ctx, address, c)
|
||||
k.importContractState(ctx, address, state)
|
||||
}
|
||||
|
||||
func addrFromUint64(id uint64) sdk.AccAddress {
|
||||
addr := make([]byte, 20)
|
||||
addr[0] = 'C'
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestQueryContractState(t *testing.T) {
|
||||
{Key: []byte("foo"), Value: []byte(`"bar"`)},
|
||||
{Key: []byte{0x0, 0x1}, Value: []byte(`{"count":8}`)},
|
||||
}
|
||||
keeper.setContractState(ctx, addr, contractModel)
|
||||
keeper.importContractState(ctx, addr, contractModel)
|
||||
|
||||
// this gets us full error, not redacted sdk.Error
|
||||
q := NewQuerier(keeper)
|
||||
|
||||
@@ -37,4 +37,13 @@ var (
|
||||
|
||||
// ErrMigrationFailed error for rust execution contract failure
|
||||
ErrMigrationFailed = sdkErrors.Register(DefaultCodespace, 10, "migrate wasm contract failed")
|
||||
|
||||
// ErrEmpty error for empty content
|
||||
ErrEmpty = sdkErrors.Register(DefaultCodespace, 11, "empty")
|
||||
|
||||
// ErrLimit error for content that exceeds a limit
|
||||
ErrLimit = sdkErrors.Register(DefaultCodespace, 12, "exceeds limit")
|
||||
|
||||
// ErrInvalid error for content that is invalid in this context
|
||||
ErrInvalid = sdkErrors.Register(DefaultCodespace, 13, "invalid")
|
||||
)
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
package types
|
||||
|
||||
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
type Sequence struct {
|
||||
IDKey []byte `json:"id_key"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
func (s Sequence) ValidateBasic() error {
|
||||
if len(s.IDKey) == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "id key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenesisState is the struct representation of the export genesis
|
||||
type GenesisState struct {
|
||||
Codes []Code `json:"codes"`
|
||||
@@ -14,12 +24,41 @@ type GenesisState struct {
|
||||
Sequences []Sequence `json:"sequences"`
|
||||
}
|
||||
|
||||
func (s GenesisState) ValidateBasic() error {
|
||||
for i := range s.Codes {
|
||||
if err := s.Codes[i].ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrapf(err, "code: %d", i)
|
||||
}
|
||||
}
|
||||
for i := range s.Contracts {
|
||||
if err := s.Contracts[i].ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrapf(err, "contract: %d", i)
|
||||
}
|
||||
}
|
||||
for i := range s.Sequences {
|
||||
if err := s.Sequences[i].ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrapf(err, "sequence: %d", i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Code struct encompasses CodeInfo and CodeBytes
|
||||
type Code struct {
|
||||
CodeInfo CodeInfo `json:"code_info"`
|
||||
CodesBytes []byte `json:"code_bytes"`
|
||||
}
|
||||
|
||||
func (c Code) ValidateBasic() error {
|
||||
if err := c.CodeInfo.ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrap(err, "code info")
|
||||
}
|
||||
if err := validateWasmCode(c.CodesBytes); err != nil {
|
||||
return sdkerrors.Wrap(err, "code bytes")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Contract struct encompasses ContractAddress, ContractInfo, and ContractState
|
||||
type Contract struct {
|
||||
ContractAddress sdk.AccAddress `json:"contract_address"`
|
||||
@@ -27,8 +66,23 @@ type Contract struct {
|
||||
ContractState []Model `json:"contract_state"`
|
||||
}
|
||||
|
||||
func (c Contract) ValidateBasic() error {
|
||||
if err := sdk.VerifyAddressFormat(c.ContractAddress); err != nil {
|
||||
return sdkerrors.Wrap(err, "contract address")
|
||||
}
|
||||
if err := c.ContractInfo.ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrap(err, "contract info")
|
||||
}
|
||||
for i := range c.ContractState {
|
||||
if err := c.ContractState[i].ValidateBasic(); err != nil {
|
||||
return sdkerrors.Wrapf(err, "contract state %d", i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateGenesis performs basic validation of supply genesis data returning an
|
||||
// error for any failed validation criteria.
|
||||
func ValidateGenesis(data GenesisState) error {
|
||||
return nil
|
||||
return data.ValidateBasic()
|
||||
}
|
||||
|
||||
@@ -2,35 +2,11 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxWasmSize = 500 * 1024
|
||||
|
||||
// MaxLabelSize is the longest label that can be used when Instantiating a contract
|
||||
MaxLabelSize = 128
|
||||
|
||||
// BuildTagRegexp is a docker image regexp.
|
||||
// We only support max 128 characters, with at least one organization name (subset of all legal names).
|
||||
//
|
||||
// Details from https://docs.docker.com/engine/reference/commandline/tag/#extended-description :
|
||||
//
|
||||
// An image name is made up of slash-separated name components (optionally prefixed by a registry hostname).
|
||||
// Name components may contain lowercase characters, digits and separators.
|
||||
// A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.
|
||||
//
|
||||
// A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits, underscores, periods and dashes.
|
||||
// A tag name may not start with a period or a dash and may contain a maximum of 128 characters.
|
||||
BuildTagRegexp = "^[a-z0-9][a-z0-9._-]*[a-z0-9](/[a-z0-9][a-z0-9._-]*[a-z0-9])+:[a-zA-Z0-9_][a-zA-Z0-9_.-]*$"
|
||||
|
||||
MaxBuildTagSize = 128
|
||||
)
|
||||
|
||||
type MsgStoreCode struct {
|
||||
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
|
||||
// WASMByteCode can be raw or gzip compressed
|
||||
@@ -54,28 +30,18 @@ func (msg MsgStoreCode) ValidateBasic() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(msg.WASMByteCode) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty wasm code")
|
||||
if err := validateWasmCode(msg.WASMByteCode); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "code bytes %s", err.Error())
|
||||
}
|
||||
|
||||
if len(msg.WASMByteCode) > MaxWasmSize {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "wasm code too large")
|
||||
if err := validateSourceURL(msg.Source); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "source %s", err.Error())
|
||||
}
|
||||
|
||||
if msg.Source != "" {
|
||||
u, err := url.Parse(msg.Source)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "source should be a valid url")
|
||||
}
|
||||
if !u.IsAbs() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "source should be an absolute url")
|
||||
}
|
||||
if u.Scheme != "https" {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "source must use https")
|
||||
}
|
||||
if err := validateBuilder(msg.Builder); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "builder %s", err.Error())
|
||||
}
|
||||
|
||||
return validateBuilder(msg.Builder)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MsgStoreCode) GetSignBytes() []byte {
|
||||
@@ -86,21 +52,6 @@ func (msg MsgStoreCode) GetSigners() []sdk.AccAddress {
|
||||
return []sdk.AccAddress{msg.Sender}
|
||||
}
|
||||
|
||||
func validateBuilder(buildTag string) error {
|
||||
if len(buildTag) > MaxBuildTagSize {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "builder tag longer than 128 characters")
|
||||
}
|
||||
|
||||
if buildTag != "" {
|
||||
ok, err := regexp.MatchString(BuildTagRegexp, buildTag)
|
||||
if err != nil || !ok {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid tag supplied for builder")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MsgInstantiateContract struct {
|
||||
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
|
||||
// Admin is an optional address that can execute migrations
|
||||
@@ -127,11 +78,9 @@ func (msg MsgInstantiateContract) ValidateBasic() error {
|
||||
if msg.Code == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "code_id is required")
|
||||
}
|
||||
if msg.Label == "" {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "label is required")
|
||||
}
|
||||
if len(msg.Label) > MaxLabelSize {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "label cannot be longer than 128 characters")
|
||||
|
||||
if err := validateLabel(msg.Label); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.InitFunds.IsAnyNegative() {
|
||||
@@ -143,7 +92,6 @@ func (msg MsgInstantiateContract) ValidateBasic() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
tmBytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
|
||||
wasmTypes "github.com/CosmWasm/go-cosmwasm/types"
|
||||
@@ -20,6 +21,13 @@ type Model struct {
|
||||
Value []byte `json:"val"`
|
||||
}
|
||||
|
||||
func (m Model) ValidateBasic() error {
|
||||
if len(m.Key) == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CodeInfo is data for the uploaded contract WASM code
|
||||
type CodeInfo struct {
|
||||
CodeHash []byte `json:"code_hash"`
|
||||
@@ -28,6 +36,22 @@ type CodeInfo struct {
|
||||
Builder string `json:"builder"`
|
||||
}
|
||||
|
||||
func (c CodeInfo) ValidateBasic() error {
|
||||
if len(c.CodeHash) == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "code hash")
|
||||
}
|
||||
if err := sdk.VerifyAddressFormat(c.Creator); err != nil {
|
||||
return sdkerrors.Wrap(err, "creator")
|
||||
}
|
||||
if err := validateSourceURL(c.Source); err != nil {
|
||||
return sdkerrors.Wrap(err, "source")
|
||||
}
|
||||
if err := validateBuilder(c.Builder); err != nil {
|
||||
return sdkerrors.Wrap(err, "builder")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCodeInfo fills a new Contract struct
|
||||
func NewCodeInfo(codeHash []byte, creator sdk.AccAddress, source string, builder string) CodeInfo {
|
||||
return CodeInfo{
|
||||
@@ -58,6 +82,24 @@ func (c *ContractInfo) UpdateCodeID(ctx sdk.Context, newCodeID uint64) {
|
||||
c.LastUpdated = NewCreatedAt(ctx)
|
||||
}
|
||||
|
||||
func (c *ContractInfo) ValidateBasic() error {
|
||||
if c.CodeID == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "code id")
|
||||
}
|
||||
if err := sdk.VerifyAddressFormat(c.Creator); err != nil {
|
||||
return sdkerrors.Wrap(err, "creator")
|
||||
}
|
||||
if c.Admin != nil {
|
||||
if err := sdk.VerifyAddressFormat(c.Admin); err != nil {
|
||||
return sdkerrors.Wrap(err, "admin")
|
||||
}
|
||||
}
|
||||
if err := validateLabel(c.Label); err != nil {
|
||||
return sdkerrors.Wrap(err, "label")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AbsoluteTxPosition can be used to sort contracts
|
||||
type AbsoluteTxPosition struct {
|
||||
// BlockHeight is the block the contract was created at
|
||||
|
||||
80
x/wasm/internal/types/validation.go
Normal file
80
x/wasm/internal/types/validation.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxWasmSize = 500 * 1024
|
||||
|
||||
// MaxLabelSize is the longest label that can be used when Instantiating a contract
|
||||
MaxLabelSize = 128
|
||||
|
||||
// BuildTagRegexp is a docker image regexp.
|
||||
// We only support max 128 characters, with at least one organization name (subset of all legal names).
|
||||
//
|
||||
// Details from https://docs.docker.com/engine/reference/commandline/tag/#extended-description :
|
||||
//
|
||||
// An image name is made up of slash-separated name components (optionally prefixed by a registry hostname).
|
||||
// Name components may contain lowercase characters, digits and separators.
|
||||
// A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.
|
||||
//
|
||||
// A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits, underscores, periods and dashes.
|
||||
// A tag name may not start with a period or a dash and may contain a maximum of 128 characters.
|
||||
BuildTagRegexp = "^[a-z0-9][a-z0-9._-]*[a-z0-9](/[a-z0-9][a-z0-9._-]*[a-z0-9])+:[a-zA-Z0-9_][a-zA-Z0-9_.-]*$"
|
||||
|
||||
MaxBuildTagSize = 128
|
||||
)
|
||||
|
||||
func validateSourceURL(source string) error {
|
||||
if source != "" {
|
||||
u, err := url.Parse(source)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(ErrInvalid, "not an url")
|
||||
}
|
||||
if !u.IsAbs() {
|
||||
return sdkerrors.Wrap(ErrInvalid, "not an absolute url")
|
||||
}
|
||||
if u.Scheme != "https" {
|
||||
return sdkerrors.Wrap(ErrInvalid, "must use https")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateBuilder(buildTag string) error {
|
||||
if len(buildTag) > MaxBuildTagSize {
|
||||
return sdkerrors.Wrap(ErrLimit, "longer than 128 characters")
|
||||
}
|
||||
|
||||
if buildTag != "" {
|
||||
ok, err := regexp.MatchString(BuildTagRegexp, buildTag)
|
||||
if err != nil || !ok {
|
||||
return ErrInvalid
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateWasmCode(s []byte) error {
|
||||
if len(s) == 0 {
|
||||
return sdkerrors.Wrap(ErrEmpty, "is required")
|
||||
}
|
||||
if len(s) > MaxWasmSize {
|
||||
return sdkerrors.Wrapf(ErrLimit, "cannot be longer than %d bytes", MaxWasmSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLabel(label string) error {
|
||||
if label == "" {
|
||||
return sdkerrors.Wrap(ErrEmpty, "is required")
|
||||
}
|
||||
if len(label) > MaxLabelSize {
|
||||
return sdkerrors.Wrap(ErrLimit, "cannot be longer than 128 characters")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user