Add Registrar permission to attaching chains

This commit is contained in:
Ethan Frey 2017-07-16 22:56:28 +02:00
parent 91eb91b803
commit 485ec80cca
3 changed files with 70 additions and 20 deletions

View File

@ -43,6 +43,13 @@ func (a Actor) Empty() bool {
return a.ChainID == "" && a.App == "" && len(a.Address) == 0
}
// WithChain creates a copy of the actor with a different chainID
func (a Actor) WithChain(chainID string) (b Actor) {
b = a
b.ChainID = chainID
return
}
// Context is an interface, so we can implement "secure" variants that
// rely on private fields to control the actions
type Context interface {

View File

@ -1,6 +1,9 @@
package ibc
import (
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/stack"
@ -10,6 +13,9 @@ import (
const (
// NameIBC is the name of this module
NameIBC = "ibc"
// OptionRegistrar is the option name to set the actor
// to handle ibc chain registration
OptionRegistrar = "registrar"
)
var (
@ -23,29 +29,44 @@ func AllowIBC(app string) basecoin.Actor {
}
// Handler allows us to update the chain state or create a packet
//
// TODO: require auth for registration, the authorized actor (or role)
// should be defined in the handler, and set via SetOption
type Handler struct {
// TODO: add option to set who can permit registration and store it
basecoin.NopOption
Registrar basecoin.Actor
}
var _ basecoin.Handler = Handler{}
var _ basecoin.Handler = &Handler{}
// NewHandler makes a role handler to create roles
func NewHandler() Handler {
return Handler{}
// NewHandler makes a Handler that allows all chains to connect via IBC.
// Set a Registrar via SetOption to restrict it.
func NewHandler() *Handler {
return new(Handler)
}
// Name - return name space
func (Handler) Name() string {
func (*Handler) Name() string {
return NameIBC
}
// SetOption - sets the registrar for IBC
func (h *Handler) SetOption(l log.Logger, store state.KVStore, module, key, value string) (log string, err error) {
if module != NameIBC {
return "", errors.ErrUnknownModule(module)
}
if key == OptionRegistrar {
var act basecoin.Actor
err = data.FromJSON([]byte(value), &act)
if err != nil {
return "", err
}
h.Registrar = act
// TODO: save/load from disk!
return "Success", nil
}
return "", errors.ErrUnknownKey(key)
}
// CheckTx verifies the packet is formated correctly, and has the proper sequence
// for a registered chain
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h *Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
@ -64,7 +85,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
// DeliverTx verifies all signatures on the tx and updated the chain state
// apropriately
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h *Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
@ -72,7 +93,6 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoi
switch t := tx.Unwrap().(type) {
case RegisterChainTx:
// TODO: do we want some permissioning for this???
return h.initSeed(ctx, store, t)
case UpdateChainTx:
return h.updateSeed(ctx, store, t)
@ -82,10 +102,19 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoi
return res, errors.ErrUnknownTxType(tx.Unwrap())
}
// initSeed imports the first seed for this chain and accepts it as the root of trust
func (h Handler) initSeed(ctx basecoin.Context, store state.KVStore,
// initSeed imports the first seed for this chain and
// accepts it as the root of trust.
//
// only the registrar, if set, is allowed to do this
func (h *Handler) initSeed(ctx basecoin.Context, store state.KVStore,
t RegisterChainTx) (res basecoin.Result, err error) {
// check permission to attach
// nothing set, means anyone can connect
if !h.Registrar.Empty() && !ctx.HasPermission(h.Registrar) {
return res, errors.ErrUnauthorized()
}
chainID := t.ChainID()
s := NewChainSet(store)
err = s.Register(chainID, ctx.BlockHeight(), t.Seed.Height())
@ -101,7 +130,7 @@ func (h Handler) initSeed(ctx basecoin.Context, store state.KVStore,
// updateSeed checks the seed against the existing chain data and rejects it if it
// doesn't fit (or no chain data)
func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
func (h *Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
t UpdateChainTx) (res basecoin.Result, err error) {
chainID := t.ChainID()
@ -124,7 +153,7 @@ func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
// createPacket makes sure all permissions are good and the destination
// chain is registed. If so, it appends it to the outgoing queue
func (h Handler) createPacket(ctx basecoin.Context, store state.KVStore,
func (h *Handler) createPacket(ctx basecoin.Context, store state.KVStore,
t CreatePacketTx) (res basecoin.Result, err error) {
// make sure the chain is registed
@ -134,7 +163,7 @@ func (h Handler) createPacket(ctx basecoin.Context, store state.KVStore,
}
// make sure we have the special IBC permission
mod, err := t.Tx.GetKind()
mod, err := t.Tx.GetMod()
if err != nil {
return res, err
}
@ -155,8 +184,7 @@ func (h Handler) createPacket(ctx basecoin.Context, store state.KVStore,
return res, ErrCannotSetPermission()
}
// add the permission with the current ChainID
packet.Permissions[i] = p
packet.Permissions[i].ChainID = ctx.ChainID()
packet.Permissions[i] = p.WithChain(ctx.ChainID())
}
// now add it to the output queue....

15
tx.go
View File

@ -1,6 +1,8 @@
package basecoin
import (
"strings"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/basecoin/errors"
@ -75,3 +77,16 @@ func (t Tx) GetKind() (string, error) {
// grab the type we used in json
return text.Kind, nil
}
func (t Tx) GetMod() (string, error) {
kind, err := t.GetKind()
if err != nil {
return "", err
}
parts := strings.SplitN(kind, "/", 2)
if len(parts) != 2 {
// TODO: return "base"?
return "", errors.ErrUnknownTxType(t)
}
return parts[0], nil
}