diff --git a/app/app.go b/app/app.go index fa99458e6..e053408a4 100644 --- a/app/app.go +++ b/app/app.go @@ -31,6 +31,7 @@ type Basecoin struct { state *sm.State cacheState *sm.State handler basecoin.Handler + height uint64 logger log.Logger } @@ -45,6 +46,7 @@ func NewBasecoin(handler basecoin.Handler, eyesCli *eyes.Client, logger log.Logg eyesCli: eyesCli, state: state, cacheState: nil, + height: 0, logger: logger, } } @@ -73,6 +75,7 @@ func (app *Basecoin) Info() abci.ResponseInfo { if err != nil { cmn.PanicCrisis(err) } + app.height = resp.LastBlockHeight return abci.ResponseInfo{ Data: fmt.Sprintf("Basecoin v%v", version.Version), LastBlockHeight: resp.LastBlockHeight, @@ -111,6 +114,7 @@ func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result { cache := app.state.CacheWrap() ctx := stack.NewContext( app.state.GetChainID(), + app.height, app.logger.With("call", "delivertx"), ) res, err := app.handler.DeliverTx(ctx, cache, tx) @@ -134,6 +138,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) abci.Result { // TODO: can we abstract this setup and commit logic?? ctx := stack.NewContext( app.state.GetChainID(), + app.height, app.logger.With("call", "checktx"), ) // checktx generally shouldn't touch the state, but we don't care @@ -187,6 +192,7 @@ func (app *Basecoin) InitChain(validators []*abci.Validator) { // BeginBlock - ABCI func (app *Basecoin) BeginBlock(hash []byte, header *abci.Header) { + app.height++ // for _, plugin := range app.plugins.GetList() { // plugin.BeginBlock(app.state, hash, header) // } diff --git a/context.go b/context.go index 66ba9c5d3..b82aa042d 100644 --- a/context.go +++ b/context.go @@ -36,4 +36,5 @@ type Context interface { IsParent(ctx Context) bool Reset() Context ChainID() string + BlockHeight() uint64 } diff --git a/modules/auth/bench_test.go b/modules/auth/bench_test.go index b79449a7d..f2434f1ee 100644 --- a/modules/auth/bench_test.go +++ b/modules/auth/bench_test.go @@ -40,7 +40,7 @@ func BenchmarkCheckOneSig(b *testing.B) { h := makeHandler() store := state.NewMemKVStore() for i := 1; i <= b.N; i++ { - ctx := stack.NewContext("foo", log.NewNopLogger()) + ctx := stack.NewContext("foo", 100, log.NewNopLogger()) _, err := h.DeliverTx(ctx, store, tx) // never should error if err != nil { @@ -64,7 +64,7 @@ func benchmarkCheckMultiSig(b *testing.B, cnt int) { h := makeHandler() store := state.NewMemKVStore() for i := 1; i <= b.N; i++ { - ctx := stack.NewContext("foo", log.NewNopLogger()) + ctx := stack.NewContext("foo", 100, log.NewNopLogger()) _, err := h.DeliverTx(ctx, store, tx) // never should error if err != nil { diff --git a/modules/auth/signature_test.go b/modules/auth/signature_test.go index 3229e01dc..5afbee1a6 100644 --- a/modules/auth/signature_test.go +++ b/modules/auth/signature_test.go @@ -18,7 +18,7 @@ func TestSignatureChecks(t *testing.T) { assert := assert.New(t) // generic args - ctx := stack.NewContext("test-chain", log.NewNopLogger()) + ctx := stack.NewContext("test-chain", 100, log.NewNopLogger()) store := state.NewMemKVStore() raw := stack.NewRawTx([]byte{1, 2, 3, 4}) diff --git a/modules/base/chain_test.go b/modules/base/chain_test.go index 9d69d154f..21f48dc6f 100644 --- a/modules/base/chain_test.go +++ b/modules/base/chain_test.go @@ -61,7 +61,7 @@ func TestChain(t *testing.T) { } // generic args here... - ctx := stack.NewContext(chainID, log.NewNopLogger()) + ctx := stack.NewContext(chainID, 100, log.NewNopLogger()) store := state.NewMemKVStore() // build the stack diff --git a/modules/coin/bench_test.go b/modules/coin/bench_test.go index 52c819a85..83d3da6b7 100644 --- a/modules/coin/bench_test.go +++ b/modules/coin/bench_test.go @@ -34,7 +34,7 @@ func BenchmarkSimpleTransfer(b *testing.B) { // now, loop... for i := 1; i <= b.N; i++ { - ctx := stack.MockContext("foo").WithPermissions(sender) + ctx := stack.MockContext("foo", 100).WithPermissions(sender) tx := makeSimpleTx(sender, receiver, Coins{{"mycoin", 2}}, i) _, err := h.DeliverTx(ctx, store, tx) // never should error diff --git a/modules/coin/handler_test.go b/modules/coin/handler_test.go index 74692bbc6..50a4bbed7 100644 --- a/modules/coin/handler_test.go +++ b/modules/coin/handler_test.go @@ -74,7 +74,7 @@ func TestHandlerValidation(t *testing.T) { } for i, tc := range cases { - ctx := stack.MockContext("base-chain").WithPermissions(tc.perms...) + ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...) _, err := checkTx(ctx, tc.tx) if tc.valid { assert.Nil(err, "%d: %+v", i, err) @@ -148,7 +148,7 @@ func TestDeliverTx(t *testing.T) { require.Nil(err, "%d: %+v", i, err) } - ctx := stack.MockContext("base-chain").WithPermissions(tc.perms...) + ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...) _, err := h.DeliverTx(ctx, store, tc.tx) if len(tc.final) > 0 { // valid assert.Nil(err, "%d: %+v", i, err) diff --git a/stack/context.go b/stack/context.go index 7ff0fa4f2..0bcc0e4ec 100644 --- a/stack/context.go +++ b/stack/context.go @@ -1,9 +1,6 @@ package stack import ( - "bytes" - "math/rand" - "github.com/pkg/errors" "github.com/tendermint/tmlibs/log" @@ -16,28 +13,20 @@ import ( type nonce int64 type secureContext struct { - id nonce - chain string - app string - perms []basecoin.Actor - log.Logger + app string + // this exposes the log.Logger and all other methods we don't override + naiveContext } // NewContext - create a new secureContext -func NewContext(chain string, logger log.Logger) basecoin.Context { +func NewContext(chain string, height uint64, logger log.Logger) basecoin.Context { return secureContext{ - id: nonce(rand.Int63()), - chain: chain, - Logger: logger, + naiveContext: MockContext(chain, height).(naiveContext), } } var _ basecoin.Context = secureContext{} -func (c secureContext) ChainID() string { - return c.chain -} - // 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 @@ -50,32 +39,18 @@ func (c secureContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context } return secureContext{ - id: c.id, - chain: c.chain, - app: c.app, - perms: append(c.perms, perms...), - Logger: c.Logger, + app: c.app, + naiveContext: c.naiveContext.WithPermissions(perms...).(naiveContext), } } -func (c secureContext) HasPermission(perm basecoin.Actor) bool { - for _, p := range c.perms { - if perm.App == p.App && bytes.Equal(perm.Address, p.Address) { - return true - } +// 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, + naiveContext: c.naiveContext.Reset().(naiveContext), } - return false -} - -func (c secureContext) GetPermissions(chain, app string) (res []basecoin.Actor) { - for _, p := range c.perms { - if chain == p.ChainID { - if app == "" || app == p.App { - res = append(res, p) - } - } - } - return res } // IsParent ensures that this is derived from the given secureClient @@ -84,19 +59,7 @@ func (c secureContext) IsParent(other basecoin.Context) bool { if !ok { return false } - return c.id == so.id -} - -// Reset should clear out all permissions, -// but carry on knowledge that this is a child -func (c secureContext) Reset() basecoin.Context { - return secureContext{ - id: c.id, - chain: c.chain, - app: c.app, - perms: nil, - Logger: c.Logger, - } + return c.naiveContext.IsParent(so.naiveContext) } // withApp is a private method that we can use to properly set the @@ -107,11 +70,8 @@ func withApp(ctx basecoin.Context, app string) basecoin.Context { return ctx } return secureContext{ - id: sc.id, - chain: sc.chain, - app: app, - perms: sc.perms, - Logger: sc.Logger, + app: app, + naiveContext: sc.naiveContext, } } diff --git a/stack/helpers_test.go b/stack/helpers_test.go index 1105988f4..58748e054 100644 --- a/stack/helpers_test.go +++ b/stack/helpers_test.go @@ -15,7 +15,7 @@ import ( func TestOK(t *testing.T) { assert := assert.New(t) - ctx := NewContext("test-chain", log.NewNopLogger()) + ctx := NewContext("test-chain", 20, log.NewNopLogger()) store := state.NewMemKVStore() data := "this looks okay" tx := basecoin.Tx{} @@ -33,7 +33,7 @@ func TestOK(t *testing.T) { func TestFail(t *testing.T) { assert := assert.New(t) - ctx := NewContext("test-chain", log.NewNopLogger()) + ctx := NewContext("test-chain", 20, log.NewNopLogger()) store := state.NewMemKVStore() msg := "big problem" tx := basecoin.Tx{} @@ -53,7 +53,7 @@ func TestFail(t *testing.T) { func TestPanic(t *testing.T) { assert := assert.New(t) - ctx := NewContext("test-chain", log.NewNopLogger()) + ctx := NewContext("test-chain", 20, log.NewNopLogger()) store := state.NewMemKVStore() msg := "system crash!" tx := basecoin.Tx{} diff --git a/stack/middleware_test.go b/stack/middleware_test.go index bcd5f9b0d..1b4ed8df3 100644 --- a/stack/middleware_test.go +++ b/stack/middleware_test.go @@ -22,7 +22,7 @@ func TestPermissionSandbox(t *testing.T) { require := require.New(t) // generic args - ctx := NewContext("test-chain", log.NewNopLogger()) + ctx := NewContext("test-chain", 20, log.NewNopLogger()) store := state.NewMemKVStore() raw := NewRawTx([]byte{1, 2, 3, 4}) rawBytes, err := data.ToWire(raw) diff --git a/stack/mock.go b/stack/mock.go index 781f13d4f..2f6ef9a5d 100644 --- a/stack/mock.go +++ b/stack/mock.go @@ -2,40 +2,55 @@ package stack import ( "bytes" + "math/rand" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" ) -type mockContext struct { - perms []basecoin.Actor - chain string +type naiveContext struct { + id nonce + chain string + height uint64 + perms []basecoin.Actor log.Logger } -func MockContext(chain string) basecoin.Context { - return mockContext{ +// MockContext returns a simple, non-checking context for test cases. +// +// Always use NewContext() for production code to sandbox malicious code better +func MockContext(chain string, height uint64) basecoin.Context { + return naiveContext{ + id: nonce(rand.Int63()), chain: chain, + height: height, Logger: log.NewNopLogger(), } } -var _ basecoin.Context = mockContext{} +var _ basecoin.Context = naiveContext{} -func (c mockContext) ChainID() string { +func (c naiveContext) ChainID() string { return c.chain } +func (c naiveContext) BlockHeight() uint64 { + return c.height +} + // WithPermissions will panic if they try to set permission without the proper app -func (c mockContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context { - return mockContext{ +func (c naiveContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context { + return naiveContext{ + id: c.id, + chain: c.chain, + height: c.height, perms: append(c.perms, perms...), Logger: c.Logger, } } -func (c mockContext) HasPermission(perm basecoin.Actor) bool { +func (c naiveContext) HasPermission(perm basecoin.Actor) bool { for _, p := range c.perms { if perm.App == p.App && bytes.Equal(perm.Address, p.Address) { return true @@ -44,7 +59,7 @@ func (c mockContext) HasPermission(perm basecoin.Actor) bool { return false } -func (c mockContext) GetPermissions(chain, app string) (res []basecoin.Actor) { +func (c naiveContext) GetPermissions(chain, app string) (res []basecoin.Actor) { for _, p := range c.perms { if chain == p.ChainID { if app == "" || app == p.App { @@ -56,15 +71,21 @@ func (c mockContext) GetPermissions(chain, app string) (res []basecoin.Actor) { } // IsParent ensures that this is derived from the given secureClient -func (c mockContext) IsParent(other basecoin.Context) bool { - _, ok := other.(mockContext) - return ok +func (c naiveContext) IsParent(other basecoin.Context) bool { + nc, ok := other.(naiveContext) + if !ok { + return false + } + return c.id == nc.id } // Reset should clear out all permissions, // but carry on knowledge that this is a child -func (c mockContext) Reset() basecoin.Context { - return mockContext{ +func (c naiveContext) Reset() basecoin.Context { + return naiveContext{ + id: c.id, + chain: c.chain, + height: c.height, Logger: c.Logger, } } diff --git a/stack/recovery_test.go b/stack/recovery_test.go index 15eafba7b..944d0db31 100644 --- a/stack/recovery_test.go +++ b/stack/recovery_test.go @@ -17,7 +17,7 @@ func TestRecovery(t *testing.T) { assert := assert.New(t) // generic args here... - ctx := NewContext("test-chain", log.NewNopLogger()) + ctx := NewContext("test-chain", 20, log.NewNopLogger()) store := state.NewMemKVStore() tx := basecoin.Tx{}