Add role struct, storage, better tx validation
This commit is contained in:
parent
9eb3c3c7de
commit
3e52e6b959
10
context.go
10
context.go
|
@ -1,6 +1,8 @@
|
|||
package basecoin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -21,10 +23,18 @@ func NewActor(app string, addr []byte) Actor {
|
|||
return Actor{App: app, Address: addr}
|
||||
}
|
||||
|
||||
// Bytes makes a binary coding, useful for turning this into a key in the store
|
||||
func (a Actor) Bytes() []byte {
|
||||
return wire.BinaryBytes(a)
|
||||
}
|
||||
|
||||
// Equals checks if two actors are the same
|
||||
func (a Actor) Equals(b Actor) bool {
|
||||
return a.ChainID == b.ChainID &&
|
||||
a.App == b.App &&
|
||||
bytes.Equal(a.Address, b.Address)
|
||||
}
|
||||
|
||||
// Context is an interface, so we can implement "secure" variants that
|
||||
// rely on private fields to control the actions
|
||||
type Context interface {
|
||||
|
|
|
@ -15,8 +15,11 @@ var (
|
|||
errInsufficientSigs = fmt.Errorf("Not enough signatures")
|
||||
errNoMembers = fmt.Errorf("No members specified")
|
||||
errTooManyMembers = fmt.Errorf("Too many members specified")
|
||||
errNotEnoughMembers = fmt.Errorf("Not enough members specified")
|
||||
)
|
||||
|
||||
// TODO: codegen?
|
||||
// ex: err-gen NoRole,"No such role",CodeType_Unauthorized
|
||||
func ErrNoRole() errors.TMError {
|
||||
return errors.WithCode(errNoRole, abci.CodeType_Unauthorized)
|
||||
}
|
||||
|
@ -58,3 +61,10 @@ func ErrTooManyMembers() errors.TMError {
|
|||
func IsTooManyMembersErr(err error) bool {
|
||||
return errors.IsSameError(errTooManyMembers, err)
|
||||
}
|
||||
|
||||
func ErrNotEnoughMembers() errors.TMError {
|
||||
return errors.WithCode(errNotEnoughMembers, abci.CodeType_Unauthorized)
|
||||
}
|
||||
func IsNotEnoughMembersErr(err error) bool {
|
||||
return errors.IsSameError(errNotEnoughMembers, err)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,37 @@ type Role struct {
|
|||
Signers []basecoin.Actor `json:"signers"`
|
||||
}
|
||||
|
||||
func NewRole(min uint32, signers []basecoin.Actor) Role {
|
||||
return Role{
|
||||
MinSigs: min,
|
||||
Signers: signers,
|
||||
}
|
||||
}
|
||||
|
||||
// IsSigner checks if the given Actor is allowed to sign this role
|
||||
func (r Role) IsSigner(a basecoin.Actor) bool {
|
||||
for _, s := range r.Signers {
|
||||
if a.Equals(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAuthorized checks if the context has permission to assume the role
|
||||
func (r Role) IsAuthorized(ctx basecoin.Context) bool {
|
||||
needed := r.MinSigs
|
||||
for _, s := range r.Signers {
|
||||
if ctx.HasPermission(s) {
|
||||
needed--
|
||||
if needed <= 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MakeKey creates the lookup key for a role
|
||||
func MakeKey(role []byte) []byte {
|
||||
prefix := []byte(NameRole + "/")
|
||||
|
@ -34,6 +65,7 @@ func loadRole(store state.KVStore, key []byte) (role Role, err error) {
|
|||
return role, nil
|
||||
}
|
||||
|
||||
// we only have create here, no update, since we don't allow update yet
|
||||
func createRole(store state.KVStore, key []byte, role Role) error {
|
||||
if _, err := loadRole(store, key); !IsNoRoleErr(err) {
|
||||
return ErrRoleExists()
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package roles_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/modules/roles"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
)
|
||||
|
||||
func TestRole(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// prepare some actors...
|
||||
a := basecoin.Actor{App: "foo", Address: []byte("bar")}
|
||||
b := basecoin.Actor{ChainID: "eth", App: "foo", Address: []byte("bar")}
|
||||
c := basecoin.Actor{App: "foo", Address: []byte("baz")}
|
||||
d := basecoin.Actor{App: "si-ly", Address: []byte("bar")}
|
||||
e := basecoin.Actor{App: "si-ly", Address: []byte("big")}
|
||||
f := basecoin.Actor{App: "sig", Address: []byte{1}}
|
||||
g := basecoin.Actor{App: "sig", Address: []byte{2, 3, 4}}
|
||||
|
||||
cases := []struct {
|
||||
sigs uint32
|
||||
allowed []basecoin.Actor
|
||||
signers []basecoin.Actor
|
||||
valid bool
|
||||
}{
|
||||
// make sure simple compare is correct
|
||||
{1, []basecoin.Actor{a}, []basecoin.Actor{a}, true},
|
||||
{1, []basecoin.Actor{a}, []basecoin.Actor{b}, false},
|
||||
{1, []basecoin.Actor{a}, []basecoin.Actor{c}, false},
|
||||
{1, []basecoin.Actor{a}, []basecoin.Actor{d}, false},
|
||||
// make sure multi-sig counts to 1
|
||||
{1, []basecoin.Actor{a, b, c}, []basecoin.Actor{d, e, a, f}, true},
|
||||
{1, []basecoin.Actor{a, b, c}, []basecoin.Actor{a, b, c, d}, true},
|
||||
{1, []basecoin.Actor{a, b, c}, []basecoin.Actor{d, e, f}, false},
|
||||
// make sure multi-sig counts higher
|
||||
{2, []basecoin.Actor{b, e, g}, []basecoin.Actor{g, c, a, d, b}, true},
|
||||
{2, []basecoin.Actor{b, e, g}, []basecoin.Actor{c, a, d, b}, false},
|
||||
{3, []basecoin.Actor{a, b, c}, []basecoin.Actor{g}, false},
|
||||
}
|
||||
|
||||
for idx, tc := range cases {
|
||||
i := strconv.Itoa(idx)
|
||||
// make sure IsSigner works
|
||||
role := roles.NewRole(tc.sigs, tc.allowed)
|
||||
for _, a := range tc.allowed {
|
||||
assert.True(role.IsSigner(a), i)
|
||||
}
|
||||
// make sure IsAuthorized works
|
||||
ctx := stack.MockContext("chain-id").WithPermissions(tc.signers...)
|
||||
allowed := role.IsAuthorized(ctx)
|
||||
assert.Equal(tc.valid, allowed, i)
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,10 @@ import (
|
|||
"github.com/tendermint/basecoin/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxMembers = 10
|
||||
)
|
||||
|
||||
// AssumeRoleTx is a layered tx that can wrap your normal tx to give it
|
||||
// the authority to use a given role.
|
||||
type AssumeRoleTx struct {
|
||||
|
@ -61,6 +65,12 @@ func (tx CreateRoleTx) ValidateBasic() error {
|
|||
if len(tx.Signers) == 0 {
|
||||
return ErrNoMembers()
|
||||
}
|
||||
if len(tx.Signers) < int(tx.MinSigs) {
|
||||
return ErrNotEnoughMembers()
|
||||
}
|
||||
if len(tx.Signers) > MaxMembers {
|
||||
return ErrTooManyMembers()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -52,7 +51,7 @@ func (c naiveContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context
|
|||
|
||||
func (c naiveContext) HasPermission(perm basecoin.Actor) bool {
|
||||
for _, p := range c.perms {
|
||||
if perm.App == p.App && bytes.Equal(perm.Address, p.Address) {
|
||||
if p.Equals(perm) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue