From 5cc7406a00897585f82485419931d23767c946a4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Jul 2017 15:05:37 +0200 Subject: [PATCH] Add state space to the modules and test --- stack/dispatcher.go | 26 +++++-- stack/middleware.go | 7 ++ stack/prefixstore.go | 4 +- stack/state_space_test.go | 139 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 stack/state_space_test.go diff --git a/stack/dispatcher.go b/stack/dispatcher.go index b8850b89b0..11090fafa0 100644 --- a/stack/dispatcher.go +++ b/stack/dispatcher.go @@ -69,8 +69,14 @@ func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.KVStore, tx basec if err != nil { return res, err } - // TODO: check on callback - cb := d + + // make sure no monkey business with the context + cb := secureCheck(d, ctx) + + // and isolate the permissions and the data store for this app + ctx = withApp(ctx, r.Name()) + store = stateSpace(store, r.Name()) + return r.CheckTx(ctx, store, tx, cb) } @@ -84,8 +90,14 @@ func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.KVStore, tx bas if err != nil { return res, err } - // TODO: check on callback - cb := d + + // make sure no monkey business with the context + cb := secureDeliver(d, ctx) + + // and isolate the permissions and the data store for this app + ctx = withApp(ctx, r.Name()) + store = stateSpace(store, r.Name()) + return r.DeliverTx(ctx, store, tx, cb) } @@ -98,8 +110,12 @@ func (d *Dispatcher) SetOption(l log.Logger, store state.KVStore, module, key, v if err != nil { return "", err } - // TODO: check on callback + + // no ctx, so secureCheck not needed cb := d + // but isolate data space + store = stateSpace(store, r.Name()) + return r.SetOption(l, store, module, key, value, cb) } diff --git a/stack/middleware.go b/stack/middleware.go index b7e1e58dd3..9e1b8e75a1 100644 --- a/stack/middleware.go +++ b/stack/middleware.go @@ -27,6 +27,8 @@ func (m *middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basec next := secureCheck(m.next, ctx) // set the permissions for this app ctx = withApp(ctx, m.Name()) + store = stateSpace(store, m.Name()) + return m.middleware.CheckTx(ctx, store, tx, next) } @@ -36,10 +38,15 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx bas next := secureDeliver(m.next, ctx) // set the permissions for this app ctx = withApp(ctx, m.Name()) + store = stateSpace(store, m.Name()) + return m.middleware.DeliverTx(ctx, store, tx, next) } func (m *middleware) SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error) { + // set the namespace for the app + store = stateSpace(store, m.Name()) + return m.middleware.SetOption(l, store, module, key, value, m.next) } diff --git a/stack/prefixstore.go b/stack/prefixstore.go index 8592ba321d..e9616ad521 100644 --- a/stack/prefixstore.go +++ b/stack/prefixstore.go @@ -10,12 +10,12 @@ type prefixStore struct { var _ state.KVStore = prefixStore{} func (p prefixStore) Set(key, value []byte) { - key = append(key, p.prefix...) + key = append(p.prefix, key...) p.store.Set(key, value) } func (p prefixStore) Get(key []byte) (value []byte) { - key = append(key, p.prefix...) + key = append(p.prefix, key...) return p.store.Get(key) } diff --git a/stack/state_space_test.go b/stack/state_space_test.go new file mode 100644 index 0000000000..2722ad6a3c --- /dev/null +++ b/stack/state_space_test.go @@ -0,0 +1,139 @@ +package stack + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/state" + "github.com/tendermint/go-wire/data" +) + +// writerMid is a middleware that writes the given bytes on CheckTx and DeliverTx +type writerMid struct { + name string + key, value []byte +} + +var _ Middleware = writerMid{} + +func (w writerMid) Name() string { return w.name } + +func (w writerMid) CheckTx(ctx basecoin.Context, store state.KVStore, + tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) { + store.Set(w.key, w.value) + return next.CheckTx(ctx, store, tx) +} + +func (w writerMid) DeliverTx(ctx basecoin.Context, store state.KVStore, + tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) { + store.Set(w.key, w.value) + return next.DeliverTx(ctx, store, tx) +} + +func (w writerMid) SetOption(l log.Logger, store state.KVStore, module, + key, value string, next basecoin.SetOptioner) (string, error) { + store.Set([]byte(key), []byte(value)) + return next.SetOption(l, store, module, key, value) +} + +// writerHand is a middleware that writes the given bytes on CheckTx and DeliverTx +type writerHand struct { + name string + key, value []byte +} + +var _ basecoin.Handler = writerHand{} + +func (w writerHand) Name() string { return w.name } + +func (w writerHand) CheckTx(ctx basecoin.Context, store state.KVStore, + tx basecoin.Tx) (basecoin.Result, error) { + store.Set(w.key, w.value) + return basecoin.Result{}, nil +} + +func (w writerHand) DeliverTx(ctx basecoin.Context, store state.KVStore, + tx basecoin.Tx) (basecoin.Result, error) { + store.Set(w.key, w.value) + return basecoin.Result{}, nil +} + +func (w writerHand) SetOption(l log.Logger, store state.KVStore, module, + key, value string) (string, error) { + store.Set([]byte(key), []byte(value)) + return "Success", nil +} + +func TestStateSpace(t *testing.T) { + cases := []struct { + h basecoin.Handler + m []Middleware + expected []data.Bytes + }{ + { + writerHand{"foo", []byte{1, 2}, []byte("bar")}, + []Middleware{ + writerMid{"bing", []byte{1, 2}, []byte("bang")}, + }, + []data.Bytes{ + {'f', 'o', 'o', 0, 1, 2}, + {'b', 'i', 'n', 'g', 0, 1, 2}, + }, + }, + } + + for i, tc := range cases { + // make an app with this setup + d := NewDispatcher(WrapHandler(tc.h)) + app := New(tc.m...).Use(d) + + // register so RawTx is routed to this handler + basecoin.TxMapper.RegisterImplementation(RawTx{}, tc.h.Name(), byte(50+i)) + + // run various tests on this setup + spaceCheck(t, i, app, tc.expected) + spaceDeliver(t, i, app, tc.expected) + // spaceOption(t, i, app, keys) + } +} + +func spaceCheck(t *testing.T, i int, app basecoin.Handler, keys []data.Bytes) { + assert := assert.New(t) + require := require.New(t) + + ctx := MockContext("chain", 100) + store := state.NewMemKVStore() + + // run a tx + _, err := app.CheckTx(ctx, store, NewRawTx([]byte{77})) + require.Nil(err, "%d: %+v", i, err) + + // verify that the data was writen + for j, k := range keys { + v := store.Get(k) + assert.NotEmpty(v, "%d / %d", i, j) + } +} + +func spaceDeliver(t *testing.T, i int, app basecoin.Handler, keys []data.Bytes) { + assert := assert.New(t) + require := require.New(t) + + ctx := MockContext("chain", 100) + store := state.NewMemKVStore() + + // run a tx + _, err := app.DeliverTx(ctx, store, NewRawTx([]byte{1, 56})) + require.Nil(err, "%d: %+v", i, err) + + // verify that the data was writen + for j, k := range keys { + v := store.Get(k) + assert.NotEmpty(v, "%d / %d", i, j) + } +}