Write dispatcher, change SetOption arguments
This commit is contained in:
parent
9cd303d1fd
commit
fcab8ac901
33
app/app.go
33
app/app.go
|
@ -1,6 +1,9 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/basecoin"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
|
@ -15,8 +18,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
PluginNameBase = "base"
|
||||
ChainKey = "base/chain_id"
|
||||
ModuleNameBase = "base"
|
||||
ChainKey = "chain_id"
|
||||
)
|
||||
|
||||
type Basecoin struct {
|
||||
|
@ -66,18 +69,26 @@ func (app *Basecoin) Info() abci.ResponseInfo {
|
|||
|
||||
// ABCI::SetOption
|
||||
func (app *Basecoin) SetOption(key string, value string) string {
|
||||
if key == ChainKey {
|
||||
app.state.SetChainID(value)
|
||||
return "Success"
|
||||
module, prefix := splitKey(key)
|
||||
if module == ModuleNameBase {
|
||||
return app.setBaseOption(prefix, value)
|
||||
}
|
||||
|
||||
log, err := app.handler.SetOption(app.logger, app.state, key, value)
|
||||
log, err := app.handler.SetOption(app.logger, app.state, module, prefix, value)
|
||||
if err == nil {
|
||||
return log
|
||||
}
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
func (app *Basecoin) setBaseOption(key, value string) string {
|
||||
if key == ChainKey {
|
||||
app.state.SetChainID(value)
|
||||
return "Success"
|
||||
}
|
||||
return fmt.Sprintf("Error: unknown base option: %s", key)
|
||||
}
|
||||
|
||||
// ABCI::DeliverTx
|
||||
func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||
tx, err := basecoin.LoadTx(txBytes)
|
||||
|
@ -178,3 +189,13 @@ func (app *Basecoin) EndBlock(height uint64) (res abci.ResponseEndBlock) {
|
|||
// }
|
||||
return
|
||||
}
|
||||
|
||||
// Splits the string at the first '/'.
|
||||
// if there are none, assign default module ("base").
|
||||
func splitKey(key string) (string, string) {
|
||||
if strings.Contains(key, "/") {
|
||||
keyParts := strings.SplitN(key, "/", 2)
|
||||
return keyParts[0], keyParts[1]
|
||||
}
|
||||
return ModuleNameBase, key
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func (at *appTest) getTx(seq int, coins types.Coins) basecoin.Tx {
|
|||
func (at *appTest) acc2app(acc types.Account) {
|
||||
accBytes, err := json.Marshal(acc)
|
||||
require.Nil(at.t, err)
|
||||
res := at.app.SetOption("base/account", string(accBytes))
|
||||
res := at.app.SetOption("coin/account", string(accBytes))
|
||||
require.EqualValues(at.t, res, "Success")
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ func TestSetOption(t *testing.T) {
|
|||
accIn := types.MakeAcc("input0").Account
|
||||
accsInBytes, err := json.Marshal(accIn)
|
||||
assert.Nil(err)
|
||||
res = app.SetOption("base/account", string(accsInBytes))
|
||||
res = app.SetOption("coin/account", string(accsInBytes))
|
||||
require.EqualValues(res, "Success")
|
||||
|
||||
// make sure it is set correctly, with some balance
|
||||
|
@ -165,7 +165,7 @@ func TestSetOption(t *testing.T) {
|
|||
}
|
||||
]
|
||||
}`
|
||||
res = app.SetOption("base/account", unsortAcc)
|
||||
res = app.SetOption("coin/account", unsortAcc)
|
||||
require.EqualValues(res, "Success")
|
||||
|
||||
coins, err = getAddr(unsortAddr, app.state)
|
||||
|
@ -234,3 +234,19 @@ func TestQuery(t *testing.T) {
|
|||
})
|
||||
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
||||
}
|
||||
|
||||
func TestSplitKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
prefix, suffix := splitKey("foo/bar")
|
||||
assert.EqualValues("foo", prefix)
|
||||
assert.EqualValues("bar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("foobar")
|
||||
assert.EqualValues("base", prefix)
|
||||
assert.EqualValues("foobar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("some/complex/issue")
|
||||
assert.EqualValues("some", prefix)
|
||||
assert.EqualValues("complex/issue", suffix)
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func (app *Basecoin) LoadGenesis(path string) error {
|
|||
|
||||
// set accounts
|
||||
for _, acct := range genDoc.AppOptions.Accounts {
|
||||
_ = app.SetOption("base/account", string(acct))
|
||||
_ = app.SetOption("coin/account", string(acct))
|
||||
}
|
||||
|
||||
// set plugin options
|
||||
|
|
|
@ -24,6 +24,7 @@ var (
|
|||
errWrongChain = rawerr.New("Wrong chain for tx")
|
||||
errUnknownTxType = rawerr.New("Tx type unknown")
|
||||
errInvalidFormat = rawerr.New("Invalid format")
|
||||
errUnknownModule = rawerr.New("Unknown module")
|
||||
)
|
||||
|
||||
func ErrUnknownTxType(tx basecoin.Tx) TMError {
|
||||
|
@ -44,6 +45,14 @@ func IsInvalidFormatErr(err error) bool {
|
|||
return IsSameError(errInvalidFormat, err)
|
||||
}
|
||||
|
||||
func ErrUnknownModule(mod string) TMError {
|
||||
w := errors.Wrap(errUnknownModule, mod)
|
||||
return WithCode(w, abci.CodeType_UnknownRequest)
|
||||
}
|
||||
func IsUnknownModuleErr(err error) bool {
|
||||
return IsSameError(errUnknownModule, err)
|
||||
}
|
||||
|
||||
func ErrInternal(msg string) TMError {
|
||||
return New(msg, abci.CodeType_InternalError)
|
||||
}
|
||||
|
|
10
handler.go
10
handler.go
|
@ -47,14 +47,14 @@ func (c DeliverFunc) DeliverTx(ctx Context, store types.KVStore, tx Tx) (Result,
|
|||
}
|
||||
|
||||
type SetOptioner interface {
|
||||
SetOption(l log.Logger, store types.KVStore, key, value string) (string, error)
|
||||
SetOption(l log.Logger, store types.KVStore, module, key, value string) (string, error)
|
||||
}
|
||||
|
||||
// SetOptionFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
||||
type SetOptionFunc func(log.Logger, types.KVStore, string, string) (string, error)
|
||||
type SetOptionFunc func(log.Logger, types.KVStore, string, string, string) (string, error)
|
||||
|
||||
func (c SetOptionFunc) SetOption(l log.Logger, store types.KVStore, key, value string) (string, error) {
|
||||
return c(l, store, key, value)
|
||||
func (c SetOptionFunc) SetOption(l log.Logger, store types.KVStore, module, key, value string) (string, error) {
|
||||
return c(l, store, module, key, value)
|
||||
}
|
||||
|
||||
// Result captures any non-error abci result
|
||||
|
@ -83,6 +83,6 @@ func (_ NopDeliver) DeliverTx(Context, types.KVStore, Tx) (r Result, e error) {
|
|||
|
||||
type NopOption struct{}
|
||||
|
||||
func (_ NopOption) SetOption(log.Logger, types.KVStore, string, string) (string, error) {
|
||||
func (_ NopOption) SetOption(log.Logger, types.KVStore, string, string, string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -80,8 +80,11 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoi
|
|||
return basecoin.Result{}, nil
|
||||
}
|
||||
|
||||
func (h Handler) SetOption(l log.Logger, store types.KVStore, key, value string) (log string, err error) {
|
||||
if key == "base/account" {
|
||||
func (h Handler) SetOption(l log.Logger, store types.KVStore, module, key, value string) (log string, err error) {
|
||||
if module != NameCoin {
|
||||
return "", errors.ErrUnknownModule(module)
|
||||
}
|
||||
if key == "account" {
|
||||
var acc GenesisAccount
|
||||
err = data.FromJSON([]byte(value), &acc)
|
||||
if err != nil {
|
||||
|
|
|
@ -172,8 +172,7 @@ func TestSetOption(t *testing.T) {
|
|||
// some sample settings
|
||||
pk := crypto.GenPrivKeySecp256k1().Wrap()
|
||||
addr := pk.PubKey().Address()
|
||||
actor := basecoin.Actor{App: "coin", Address: addr}
|
||||
// actor2 := basecoin.Actor{App: "foo", Address: addr}
|
||||
actor := basecoin.Actor{App: stack.NameSigs, Address: addr}
|
||||
|
||||
someCoins := types.Coins{{"atom", 123}}
|
||||
otherCoins := types.Coins{{"eth", 11}}
|
||||
|
@ -198,13 +197,13 @@ func TestSetOption(t *testing.T) {
|
|||
l := log.NewNopLogger()
|
||||
for i, tc := range cases {
|
||||
store := types.NewMemKVStore()
|
||||
key := "base/account"
|
||||
key := "account"
|
||||
|
||||
// set the options
|
||||
for j, gen := range tc.init {
|
||||
value, err := json.Marshal(gen)
|
||||
require.Nil(err, "%d,%d: %+v", i, j, err)
|
||||
_, err = h.SetOption(l, store, key, string(value))
|
||||
_, err = h.SetOption(l, store, NameCoin, key, string(value))
|
||||
require.Nil(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ type Account struct {
|
|||
}
|
||||
|
||||
func loadAccount(store types.KVStore, key []byte) (acct Account, err error) {
|
||||
// fmt.Printf("load: %X\n", key)
|
||||
data := store.Get(key)
|
||||
if len(data) == 0 {
|
||||
return acct, ErrNoAccount()
|
||||
|
@ -107,6 +108,7 @@ func loadAccount(store types.KVStore, key []byte) (acct Account, err error) {
|
|||
}
|
||||
|
||||
func storeAccount(store types.KVStore, key []byte, acct Account) error {
|
||||
// fmt.Printf("store: %X\n", key)
|
||||
bin := wire.BinaryBytes(acct)
|
||||
store.Set(key, bin)
|
||||
return nil // real stores can return error...
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/errors"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
)
|
||||
|
||||
const (
|
||||
NameDispatcher = "disp"
|
||||
)
|
||||
|
||||
// Dispatcher grabs a bunch of Dispatchables and groups them into one Handler.
|
||||
//
|
||||
// It will route tx to the proper locations and also allows them to call each
|
||||
// other synchronously through the same tx methods.
|
||||
type Dispatcher struct {
|
||||
routes map[string]Dispatchable
|
||||
}
|
||||
|
||||
func NewDispatcher(routes ...Dispatchable) *Dispatcher {
|
||||
d := &Dispatcher{}
|
||||
d.AddRoutes(routes...)
|
||||
return d
|
||||
}
|
||||
|
||||
var _ basecoin.Handler = new(Dispatcher)
|
||||
|
||||
// AddRoutes registers all these dispatchable choices under their subdomains
|
||||
//
|
||||
// Panics on attempt to double-register a route name, as this is a configuration error.
|
||||
// Should I retrun an error instead?
|
||||
func (d *Dispatcher) AddRoutes(routes ...Dispatchable) {
|
||||
for _, r := range routes {
|
||||
name := r.Name()
|
||||
if _, ok := d.routes[name]; ok {
|
||||
panic(fmt.Sprintf("%s already registered with dispatcher", name))
|
||||
}
|
||||
d.routes[name] = r
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) Name() string {
|
||||
return NameDispatcher
|
||||
}
|
||||
|
||||
func (d *Dispatcher) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||
r, err := d.lookupTx(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.CheckTx(ctx, store, tx, nil)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||
r, err := d.lookupTx(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.DeliverTx(ctx, store, tx, nil)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) SetOption(l log.Logger, store types.KVStore, module, key, value string) (string, error) {
|
||||
r, err := d.lookupModule(module)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// TODO: callback
|
||||
return r.SetOption(l, store, module, key, value, nil)
|
||||
}
|
||||
|
||||
func (d *Dispatcher) lookupTx(tx basecoin.Tx) (Dispatchable, error) {
|
||||
// TODO
|
||||
name := "foo"
|
||||
r, ok := d.routes[name]
|
||||
if !ok {
|
||||
return nil, errors.ErrUnknownTxType(tx)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (d *Dispatcher) lookupModule(name string) (Dispatchable, error) {
|
||||
r, ok := d.routes[name]
|
||||
if !ok {
|
||||
return nil, errors.ErrUnknownModule(name)
|
||||
}
|
||||
return r, nil
|
||||
}
|
|
@ -39,13 +39,13 @@ func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store types.KVStore,
|
|||
}
|
||||
|
||||
type SetOptionMiddle interface {
|
||||
SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error)
|
||||
SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error)
|
||||
}
|
||||
|
||||
type SetOptionMiddleFunc func(log.Logger, types.KVStore, string, string, basecoin.SetOptioner) (string, error)
|
||||
type SetOptionMiddleFunc func(log.Logger, types.KVStore, string, string, string, basecoin.SetOptioner) (string, error)
|
||||
|
||||
func (c SetOptionMiddleFunc) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
return c(l, store, key, value, next)
|
||||
func (c SetOptionMiddleFunc) SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
return c(l, store, module, key, value, next)
|
||||
}
|
||||
|
||||
// holders
|
||||
|
@ -63,6 +63,43 @@ func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas
|
|||
|
||||
type PassOption struct{}
|
||||
|
||||
func (_ PassOption) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
return next.SetOption(l, store, key, value)
|
||||
func (_ PassOption) SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
return next.SetOption(l, store, module, key, value)
|
||||
}
|
||||
|
||||
// Dispatchable is like middleware, except the meaning of "next" is different.
|
||||
// Whereas in the middleware, it is the next handler that we should pass the same tx into,
|
||||
// for dispatchers, it is a dispatcher, which it can use to
|
||||
type Dispatchable interface {
|
||||
Middleware
|
||||
AssertDispatcher()
|
||||
}
|
||||
|
||||
// WrapHandler turns a basecoin.Handler into a Dispatchable interface
|
||||
func WrapHandler(h basecoin.Handler) Dispatchable {
|
||||
return wrapped{h}
|
||||
}
|
||||
|
||||
type wrapped struct {
|
||||
h basecoin.Handler
|
||||
}
|
||||
|
||||
var _ Dispatchable = wrapped{}
|
||||
|
||||
func (w wrapped) AssertDispatcher() {}
|
||||
|
||||
func (w wrapped) Name() string {
|
||||
return w.h.Name()
|
||||
}
|
||||
|
||||
func (w wrapped) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, _ basecoin.Checker) (basecoin.Result, error) {
|
||||
return w.h.CheckTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (w wrapped) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, _ basecoin.Deliver) (basecoin.Result, error) {
|
||||
return w.h.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (w wrapped) SetOption(l log.Logger, store types.KVStore, module, key, value string, _ basecoin.SetOptioner) (string, error) {
|
||||
return w.h.SetOption(l, store, module, key, value)
|
||||
}
|
||||
|
|
|
@ -50,12 +50,12 @@ func (_ Logger) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin
|
|||
return
|
||||
}
|
||||
|
||||
func (_ Logger) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
func (_ Logger) SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
start := time.Now()
|
||||
res, err := next.SetOption(l, store, key, value)
|
||||
res, err := next.SetOption(l, store, module, key, value)
|
||||
delta := time.Now().Sub(start)
|
||||
// TODO: log the value being set also?
|
||||
l = l.With("duration", micros(delta)).With("key", key)
|
||||
l = l.With("duration", micros(delta)).With("mod", module).With("key", key)
|
||||
if err == nil {
|
||||
l.Info("SetOption", "log", res)
|
||||
} else {
|
||||
|
|
|
@ -39,8 +39,8 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas
|
|||
return m.middleware.DeliverTx(ctx, store, tx, next)
|
||||
}
|
||||
|
||||
func (m *middleware) SetOption(l log.Logger, store types.KVStore, key, value string) (string, error) {
|
||||
return m.middleware.SetOption(l, store, key, value, m.next)
|
||||
func (m *middleware) SetOption(l log.Logger, store types.KVStore, module, key, value string) (string, error) {
|
||||
return m.middleware.SetOption(l, store, module, key, value, m.next)
|
||||
}
|
||||
|
||||
// Stack is the entire application stack
|
||||
|
|
|
@ -41,13 +41,13 @@ func (_ Recovery) DeliverTx(ctx basecoin.Context, store types.KVStore, tx baseco
|
|||
return next.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (_ Recovery) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
||||
func (_ Recovery) SetOption(l log.Logger, store types.KVStore, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = normalizePanic(r)
|
||||
}
|
||||
}()
|
||||
return next.SetOption(l, store, key, value)
|
||||
return next.SetOption(l, store, module, key, value)
|
||||
}
|
||||
|
||||
// normalizePanic makes sure we can get a nice TMError (with stack) out of it
|
||||
|
|
Loading…
Reference in New Issue