From 11a8c0d9a2a067c19ac14d21671eef0bad50b9c6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Jul 2017 19:41:17 +0200 Subject: [PATCH] Add roles middleware and handler --- errors/common.go | 9 +++++ modules/coin/handler.go | 5 +-- modules/roles/handler.go | 59 +++++++++++++++++++++++++++ modules/roles/middleware.go | 80 +++++++++++++++++++++++++++++++++++++ modules/roles/store.go | 7 ++++ 5 files changed, 156 insertions(+), 4 deletions(-) diff --git a/errors/common.go b/errors/common.go index c04af0984d..c793a6953e 100644 --- a/errors/common.go +++ b/errors/common.go @@ -22,6 +22,7 @@ var ( errInvalidFormat = fmt.Errorf("Invalid format") errUnknownModule = fmt.Errorf("Unknown module") errExpired = fmt.Errorf("Tx expired") + errUnknownKey = fmt.Errorf("Unknown key") ) // some crazy reflection to unwrap any generated struct. @@ -63,6 +64,14 @@ func IsUnknownModuleErr(err error) bool { return IsSameError(errUnknownModule, err) } +func ErrUnknownKey(mod string) TMError { + w := errors.Wrap(errUnknownKey, mod) + return WithCode(w, abci.CodeType_UnknownRequest) +} +func IsUnknownKeyErr(err error) bool { + return IsSameError(errUnknownKey, err) +} + func ErrInternal(msg string) TMError { return New(msg, abci.CodeType_InternalError) } diff --git a/modules/coin/handler.go b/modules/coin/handler.go index cd45910f38..590362045f 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -1,8 +1,6 @@ package coin import ( - "fmt" - "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/log" @@ -106,8 +104,7 @@ func (h Handler) SetOption(l log.Logger, store state.KVStore, module, key, value return "Success", nil } - msg := fmt.Sprintf("Unknown key: %s", key) - return "", errors.ErrInternal(msg) + return errors.ErrUnknownKey(key) } func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { diff --git a/modules/roles/handler.go b/modules/roles/handler.go index a20d4f27ac..53c7607c77 100644 --- a/modules/roles/handler.go +++ b/modules/roles/handler.go @@ -1,4 +1,63 @@ package roles +import ( + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/state" +) + //NameRole - name space of the roles module const NameRole = "role" + +type Handler struct{} + +var _ basecoin.Handler = Handler{} + +// NewHandler makes a role handler to create roles +func NewHandler() Handler { + return Handler{} +} + +// Name - return name space +func (Handler) Name() string { + return NameRole +} + +// CheckTx checks if there is enough money in the account +func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + _, err = checkTx(ctx, tx) + return res, err +} + +// DeliverTx moves the money +func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + create, err := checkTx(ctx, tx) + if err != nil { + return res, err + } + + // lets try... + role := NewRole(create.MinSigs, create.Signers) + err = createRole(store, MakeKey(create.Role), role) + return res, err +} + +// SetOption - sets the genesis account balance +func (h Handler) SetOption(l log.Logger, store state.KVStore, module, key, value string) (log string, err error) { + if module != NameRole { + return "", errors.ErrUnknownModule(module) + } + return "", errors.ErrUnknownKey(key) +} + +func checkTx(ctx basecoin.Context, tx basecoin.Tx) (create CreateRoleTx, err error) { + // check if the tx is proper type and valid + create, ok := tx.Unwrap().(CreateRoleTx) + if !ok { + return create, errors.ErrInvalidFormat(tx) + } + err = create.ValidateBasic() + return create, err +} diff --git a/modules/roles/middleware.go b/modules/roles/middleware.go index 3258a668dc..8568bab2b2 100644 --- a/modules/roles/middleware.go +++ b/modules/roles/middleware.go @@ -1 +1,81 @@ package roles + +import ( + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/state" +) + +type Middleware struct { + stack.PassOption +} + +var _ stack.Middleware = Middleware{} + +func NewMiddleware() Middleware { + return Middleware{} +} + +// Name - return name space +func (Middleware) Name() string { + return NameRole +} + +// CheckTx checks if this is valid +func (m Middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) { + assume, err := checkMiddleTx(ctx, tx) + if err != nil { + return res, err + } + + ctx, err = assumeRole(ctx, store, assume) + if err != nil { + return res, err + } + + return next.CheckTx(ctx, store, assume.Tx) +} + +// DeliverTx moves the money +func (m Middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) { + assume, err := checkMiddleTx(ctx, tx) + if err != nil { + return res, err + } + + ctx, err = assumeRole(ctx, store, assume) + if err != nil { + return res, err + } + + return next.DeliverTx(ctx, store, assume.Tx) +} + +func checkMiddleTx(ctx basecoin.Context, tx basecoin.Tx) (assume AssumeRoleTx, err error) { + // check if the tx is proper type and valid + assume, ok := tx.Unwrap().(AssumeRoleTx) + if !ok { + return assume, errors.ErrInvalidFormat(tx) + } + err = assume.ValidateBasic() + if err != nil { + return assume, err + } + + // load it up and check it out... + return assume, err +} + +func assumeRole(ctx basecoin.Context, store state.KVStore, assume AssumeRoleTx) (basecoin.Context, error) { + role, err := loadRole(store, MakeKey(assume.Role)) + if err != nil { + return nil, err + } + + if !role.IsAuthorized(ctx) { + return nil, ErrInsufficientSigs() + } + ctx = ctx.WithPermissions(NewPerm(assume.Role)) + return ctx, nil +} diff --git a/modules/roles/store.go b/modules/roles/store.go index 61ca16e61c..a576b0e1a9 100644 --- a/modules/roles/store.go +++ b/modules/roles/store.go @@ -9,6 +9,13 @@ import ( wire "github.com/tendermint/go-wire" ) +func NewPerm(role []byte) basecoin.Actor { + return basecoin.Actor{ + App: NameRole, + Address: role, + } +} + // Role - structure to hold permissioning type Role struct { MinSigs uint32 `json:"min_sigs"`