cosmos-sdk/stack/context.go

125 lines
3.3 KiB
Go

package stack
import (
"github.com/pkg/errors"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/state"
)
// store nonce as it's own type so no one can even try to fake it
type nonce int64
type secureContext struct {
app string
ibc bool
// this exposes the log.Logger and all other methods we don't override
naiveContext
}
// NewContext - create a new secureContext
func NewContext(chain string, height uint64, logger log.Logger) basecoin.Context {
mock := MockContext(chain, height).(naiveContext)
mock.Logger = logger
return secureContext{
naiveContext: mock,
}
}
var _ basecoin.Context = secureContext{}
// WithPermissions will panic if they try to set permission without the proper app
func (c secureContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context {
// the guard makes sure you only set permissions for the app you are inside
for _, p := range perms {
if !c.validPermisison(p) {
err := errors.Errorf("Cannot set permission for %s/%s on (app=%s, ibc=%b)",
p.ChainID, p.App, c.app, c.ibc)
panic(err)
}
}
return secureContext{
app: c.app,
ibc: c.ibc,
naiveContext: c.naiveContext.WithPermissions(perms...).(naiveContext),
}
}
func (c secureContext) validPermisison(p basecoin.Actor) bool {
// if app is set, then it must match
if c.app != "" && c.app != p.App {
return false
}
// if ibc, chain must be set, otherwise it must not
return c.ibc == (p.ChainID != "")
}
// Reset should clear out all permissions,
// but carry on knowledge that this is a child
func (c secureContext) Reset() basecoin.Context {
return secureContext{
app: c.app,
ibc: c.ibc,
naiveContext: c.naiveContext.Reset().(naiveContext),
}
}
// IsParent ensures that this is derived from the given secureClient
func (c secureContext) IsParent(other basecoin.Context) bool {
so, ok := other.(secureContext)
if !ok {
return false
}
return c.naiveContext.IsParent(so.naiveContext)
}
// withApp is a private method that we can use to properly set the
// app controls in the middleware
func withApp(ctx basecoin.Context, app string) basecoin.Context {
sc, ok := ctx.(secureContext)
if !ok {
return ctx
}
return secureContext{
app: app,
ibc: false,
naiveContext: sc.naiveContext,
}
}
// withIBC is a private method so we can securely allow IBC permissioning
func withIBC(ctx basecoin.Context) basecoin.Context {
sc, ok := ctx.(secureContext)
if !ok {
return ctx
}
return secureContext{
app: "",
ibc: true,
naiveContext: sc.naiveContext,
}
}
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
if !parent.IsParent(ctx) {
return res, errors.New("Passing in non-child Context")
}
return h.CheckTx(ctx, store, tx)
}
return basecoin.CheckerFunc(next)
}
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
if !parent.IsParent(ctx) {
return res, errors.New("Passing in non-child Context")
}
return h.DeliverTx(ctx, store, tx)
}
return basecoin.DeliverFunc(next)
}