diff --git a/context.go b/context.go index 4da626ed3d..24535dc0ad 100644 --- a/context.go +++ b/context.go @@ -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 { diff --git a/modules/ibc/handler.go b/modules/ibc/handler.go index 1a5e191df1..114a580394 100644 --- a/modules/ibc/handler.go +++ b/modules/ibc/handler.go @@ -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.... diff --git a/tx.go b/tx.go index cc143ffc43..3a5626a7f6 100644 --- a/tx.go +++ b/tx.go @@ -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 +}