Skip to content

Commit

Permalink
Add ante handler
Browse files Browse the repository at this point in the history
  • Loading branch information
pinosu committed Sep 11, 2023
1 parent cb08623 commit ade4fd9
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 25 deletions.
2 changes: 2 additions & 0 deletions app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type HandlerOptions struct {
ante.HandlerOptions

IBCKeeper *keeper.Keeper
WasmKeeper *wasmkeeper.Keeper
WasmConfig *wasmTypes.WasmConfig
TXCounterStoreKey storetypes.StoreKey
}
Expand All @@ -46,6 +47,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit), // after setup context to enforce limits early
wasmkeeper.NewCountTXDecorator(options.TXCounterStoreKey),
wasmkeeper.NewGasRegisterDecorator(options.WasmKeeper.GetGasRegister()),
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
Expand Down
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ func (app *WasmApp) setAnteHandler(txConfig client.TxConfig, wasmConfig wasmtype
IBCKeeper: app.IBCKeeper,
WasmConfig: &wasmConfig,
TXCounterStoreKey: txCounterStoreKey,
WasmKeeper: &app.WasmKeeper,
},
)
if err != nil {
Expand Down
13 changes: 6 additions & 7 deletions x/wasm/ioutils/ioutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ package ioutils
import (
"bytes"
"compress/gzip"
"fmt"
"io"

errorsmod "cosmossdk.io/errors"
)

var errLimit = errorsmod.Register("wasm", 29, "exceeds limit")
var errLimit = fmt.Errorf("exceeds limit")

// Uncompress expects a valid gzip source to unpack or fails. See IsGzip
func Uncompress(gzipSrc []byte, limit int64) ([]byte, error) {
if int64(len(gzipSrc)) > limit {
return nil, errLimit.Wrapf(" max %d bytes", limit)
return nil, fmt.Errorf("%s: max %d bytes", errLimit, limit)
}
zr, err := gzip.NewReader(bytes.NewReader(gzipSrc))
if err != nil {
Expand All @@ -22,14 +21,14 @@ func Uncompress(gzipSrc []byte, limit int64) ([]byte, error) {
zr.Multistream(false)
defer zr.Close()
bz, err := io.ReadAll(LimitReader(zr, limit))
if errLimit.Is(err) {
return nil, errLimit.Wrapf(" max %d bytes", limit)
if err == errLimit {
return nil, fmt.Errorf("%s: max %d bytes", errLimit, limit)
}
return bz, err
}

// LimitReader returns a Reader that reads from r
// but stops with types.ErrLimit after n bytes.
// but stops with "limit error" after n bytes.
// The underlying implementation is a *io.LimitedReader.
func LimitReader(r io.Reader, n int64) io.Reader {
return &LimitedReader{r: &io.LimitedReader{R: r, N: n}}
Expand Down
18 changes: 18 additions & 0 deletions x/wasm/keeper/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,21 @@ func (d LimitSimulationGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu
}
return next(ctx, tx, simulate)
}

// GasRegisterDecorator ante decorator to store gas register in the context
type GasRegisterDecorator struct {
gasRegister GasRegister
}

// NewGasRegisterDecorator constructor.
func NewGasRegisterDecorator(gr GasRegister) *GasRegisterDecorator {
return &GasRegisterDecorator{gasRegister: gr}
}

// AnteHandle adds the gas register to the context.
func (g GasRegisterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
if simulate {
return next(ctx, tx, simulate)
}
return next(types.WithGasRegister(ctx, g.gasRegister), tx, simulate)
}
5 changes: 5 additions & 0 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ func (k Keeper) GetAuthority() string {
return k.authority
}

// GetGasRegister returns the x/wasm module's gas register.
func (k Keeper) GetGasRegister() GasRegister {
return k.gasRegister
}

func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ types.AuthorizationPolicy) (codeID uint64, checksum []byte, err error) {
if creator == nil {
return 0, checksum, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "cannot be nil")
Expand Down
44 changes: 28 additions & 16 deletions x/wasm/types/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"strings"

"github.com/CosmWasm/wasmd/x/wasm/ioutils"
wasmvm "github.com/CosmWasm/wasmvm"
"github.com/cosmos/gogoproto/proto"

Expand All @@ -14,11 +13,14 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authztypes "github.com/cosmos/cosmos-sdk/x/authz"

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

const (
gasDeserializationCostPerByte = uint64(1)
codehashWildcard = "*"
// CodehashWildcard matches any code hash
CodehashWildcard = "*"
)

var (
Expand Down Expand Up @@ -52,7 +54,11 @@ func (a *StoreCodeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authztype
permission := *storeMsg.InstantiatePermission

if ioutils.IsGzip(code) {
// TODO: consume gas
gasRegister, ok := GasRegister(ctx)
if !ok {
panic("gas register not found") // TODO: check how to handle this
}
ctx.GasMeter().ConsumeGas(gasRegister.UncompressCosts(len(code)), "Uncompress gzip bytecode")
wasmCode, err := ioutils.Uncompress(code, int64(MaxWasmSize))
if err != nil {
return authztypes.AcceptResponse{}, sdkerrors.ErrInvalidRequest.Wrap(errorsmod.Wrap(err, "uncompress wasm archive").Error())
Expand All @@ -75,23 +81,29 @@ func (a *StoreCodeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authztype

// ValidateBasic implements Authorization.ValidateBasic.
func (a StoreCodeAuthorization) ValidateBasic() error {
if len(a.Grants) == 0 {
numberOfGrants := len(a.Grants)
switch numberOfGrants {
case 0:
return ErrEmpty.Wrap("grants")
}
for i := 0; i < len(a.Grants); i++ {
if strings.EqualFold(string(a.Grants[i].CodeHash), codehashWildcard) && len(a.Grants) > 1 {
return sdkerrors.ErrInvalidRequest.Wrap("cannot have multiple grants when wildcard grant is one of them")
}
if err := a.Grants[i].ValidateBasic(); err != nil {
return errorsmod.Wrapf(err, "position %d", i)
case 1:
if err := a.Grants[0].ValidateBasic(); err != nil {
return errorsmod.Wrapf(err, "position %d", 0)
}
for j := i + 1; j < len(a.Grants); j++ {
if strings.EqualFold(string(a.Grants[i].CodeHash), string(a.Grants[i].CodeHash)) {
return sdkerrors.ErrInvalidRequest.Wrap("cannot have multiple grants with same code hash")
default:
uniqueGrants := make(map[string]struct{})
for i, grant := range a.Grants {
if strings.EqualFold(string(grant.CodeHash), CodehashWildcard) {
return sdkerrors.ErrInvalidRequest.Wrap("cannot have multiple grants when wildcard grant is one of them")
}
if err := grant.ValidateBasic(); err != nil {
return errorsmod.Wrapf(err, "position %d", i)
}
uniqueGrants[strings.ToLower(string(grant.CodeHash))] = struct{}{}
}
if len(uniqueGrants) != numberOfGrants {
return sdkerrors.ErrInvalidRequest.Wrap("cannot have multiple grants with same code hash")
}
}

return nil
}

Expand All @@ -118,7 +130,7 @@ func (g CodeGrant) ValidateBasic() error {

// Accept checks if checksum and permission match the grant
func (g CodeGrant) Accept(checksum []byte, permission AccessConfig) bool {
if !bytes.Equal(g.CodeHash, []byte(codehashWildcard)) && !bytes.Equal(g.CodeHash, checksum) {
if !bytes.Equal(g.CodeHash, []byte(CodehashWildcard)) && !bytes.Equal(g.CodeHash, checksum) {
return false
}
return permission.IsSubset(*g.InstantiatePermission)
Expand Down
21 changes: 21 additions & 0 deletions x/wasm/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ const (
contextKeyQueryStackSize contextKey = iota
// authorization policy for sub-messages
contextKeySubMsgAuthzPolicy = iota
// gas register
contextKeyGasRegister = iota
)

type gasRegister interface {
// UncompressCosts costs to unpack a new wasm contract
UncompressCosts(byteLength int) sdk.Gas
}

// WithTXCounter stores a transaction counter value in the context
func WithTXCounter(ctx sdk.Context, counter uint32) sdk.Context {
return ctx.WithValue(contextKeyTXCount, counter)
Expand Down Expand Up @@ -52,3 +59,17 @@ func SubMsgAuthzPolicy(ctx sdk.Context) (AuthorizationPolicy, bool) {
val, ok := ctx.Value(contextKeySubMsgAuthzPolicy).(AuthorizationPolicy)
return val, ok
}

// WithGasRegister stores the gas register into the context returned
func WithGasRegister(ctx sdk.Context, gr gasRegister) sdk.Context {
if gr == nil {
panic("gas register must not be nil")
}
return ctx.WithValue(contextKeyGasRegister, gr)
}

// GetGasRegister reads the gas register from the context
func GasRegister(ctx sdk.Context) (gasRegister, bool) {
val, ok := ctx.Value(contextKeyGasRegister).(gasRegister)
return val, ok
}
2 changes: 0 additions & 2 deletions x/wasm/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ var (
ErrNoSuchCodeFn = WasmVMFlavouredErrorFactory(errorsmod.Register(DefaultCodespace, 28, "no such code"),
func(id uint64) error { return wasmvmtypes.NoSuchCode{CodeID: id} },
)

// code 29 is used for ioutils gzip uncompress
)

// WasmVMErrorable mapped error type in wasmvm and are not redacted
Expand Down

0 comments on commit ade4fd9

Please sign in to comment.