Start defining CreatePacket

This commit is contained in:
Ethan Frey 2017-07-16 22:46:21 +02:00
parent 715d573e1a
commit 91eb91b803
4 changed files with 116 additions and 21 deletions

View File

@ -9,8 +9,10 @@ import (
// nolint // nolint
var ( var (
errChainNotRegistered = fmt.Errorf("Chain not registered") errChainNotRegistered = fmt.Errorf("Chain not registered")
errChainAlreadyExists = fmt.Errorf("Chain already exists") errChainAlreadyExists = fmt.Errorf("Chain already exists")
errNeedsIBCPermission = fmt.Errorf("Needs app-permission to send IBC")
errCannotSetPermission = fmt.Errorf("Requesting invalid permission on IBC")
// errNotMember = fmt.Errorf("Not a member") // errNotMember = fmt.Errorf("Not a member")
// errInsufficientSigs = fmt.Errorf("Not enough signatures") // errInsufficientSigs = fmt.Errorf("Not enough signatures")
// errNoMembers = fmt.Errorf("No members specified") // errNoMembers = fmt.Errorf("No members specified")
@ -23,12 +25,13 @@ var (
IBCCodeUnknownHeight = abci.CodeType(1004) IBCCodeUnknownHeight = abci.CodeType(1004)
IBCCodeInvalidCommit = abci.CodeType(1005) IBCCodeInvalidCommit = abci.CodeType(1005)
IBCCodeInvalidProof = abci.CodeType(1006) IBCCodeInvalidProof = abci.CodeType(1006)
IBCCodeInvalidCall = abci.CodeType(1007)
) )
func ErrNotRegistered(chainID string) error { func ErrNotRegistered(chainID string) error {
return errors.WithMessage(chainID, errChainNotRegistered, IBCCodeChainNotRegistered) return errors.WithMessage(chainID, errChainNotRegistered, IBCCodeChainNotRegistered)
} }
func IsNotRegistetedErr(err error) bool { func IsNotRegisteredErr(err error) bool {
return errors.IsSameError(errChainNotRegistered, err) return errors.IsSameError(errChainNotRegistered, err)
} }
@ -38,3 +41,17 @@ func ErrAlreadyRegistered(chainID string) error {
func IsAlreadyRegistetedErr(err error) bool { func IsAlreadyRegistetedErr(err error) bool {
return errors.IsSameError(errChainAlreadyExists, err) return errors.IsSameError(errChainAlreadyExists, err)
} }
func ErrNeedsIBCPermission() error {
return errors.WithCode(errNeedsIBCPermission, IBCCodeInvalidCall)
}
func IsNeedsIBCPermissionErr(err error) bool {
return errors.IsSameError(errNeedsIBCPermission, err)
}
func ErrCannotSetPermission() error {
return errors.WithCode(errCannotSetPermission, IBCCodeInvalidCall)
}
func IsCannotSetPermissionErr(err error) bool {
return errors.IsSameError(errCannotSetPermission, err)
}

View File

@ -7,16 +7,27 @@ import (
"github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/state"
) )
// nolint
const ( const (
// NameIBC is the name of this module
NameIBC = "ibc" NameIBC = "ibc"
) )
var (
allowIBC = []byte{0x42, 0xbe, 0xef, 0x1}
)
// AllowIBC is the special code that an app must set to
// enable sending IBC packets for this app-type
func AllowIBC(app string) basecoin.Actor {
return basecoin.Actor{App: app, Address: allowIBC}
}
// Handler allows us to update the chain state or create a packet // Handler allows us to update the chain state or create a packet
// //
// TODO: require auth for registration, the authorized actor (or role) // TODO: require auth for registration, the authorized actor (or role)
// should be defined in the handler, and set via SetOption // should be defined in the handler, and set via SetOption
type Handler struct { type Handler struct {
// TODO: add option to set who can permit registration and store it
basecoin.NopOption basecoin.NopOption
} }
@ -45,6 +56,8 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
return h.initSeed(ctx, store, t) return h.initSeed(ctx, store, t)
case UpdateChainTx: case UpdateChainTx:
return h.updateSeed(ctx, store, t) return h.updateSeed(ctx, store, t)
case CreatePacketTx:
return h.createPacket(ctx, store, t)
} }
return res, errors.ErrUnknownTxType(tx.Unwrap()) return res, errors.ErrUnknownTxType(tx.Unwrap())
} }
@ -63,6 +76,8 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoi
return h.initSeed(ctx, store, t) return h.initSeed(ctx, store, t)
case UpdateChainTx: case UpdateChainTx:
return h.updateSeed(ctx, store, t) return h.updateSeed(ctx, store, t)
case CreatePacketTx:
return h.createPacket(ctx, store, t)
} }
return res, errors.ErrUnknownTxType(tx.Unwrap()) return res, errors.ErrUnknownTxType(tx.Unwrap())
} }
@ -106,3 +121,45 @@ func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
err = cert.Update(seed.Checkpoint, seed.Validators) err = cert.Update(seed.Checkpoint, seed.Validators)
return res, err return res, err
} }
// 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,
t CreatePacketTx) (res basecoin.Result, err error) {
// make sure the chain is registed
dest := t.DestChain
if !NewChainSet(store).Exists([]byte(dest)) {
return res, ErrNotRegistered(dest)
}
// make sure we have the special IBC permission
mod, err := t.Tx.GetKind()
if err != nil {
return res, err
}
if !ctx.HasPermission(AllowIBC(mod)) {
return res, ErrNeedsIBCPermission()
}
// start making the packet to send
packet := Packet{
DestChain: t.DestChain,
Tx: t.Tx,
Permissions: make([]basecoin.Actor, len(t.Permissions)),
}
// make sure we have all the permissions we want to send
for i, p := range t.Permissions {
if !ctx.HasPermission(p) {
return res, ErrCannotSetPermission()
}
// add the permission with the current ChainID
packet.Permissions[i] = p
packet.Permissions[i].ChainID = ctx.ChainID()
}
// now add it to the output queue....
// TODO: where to store, also set the sequence....
return res, nil
}

View File

@ -1,6 +1,7 @@
package ibc package ibc
import ( import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/state"
wire "github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
@ -47,3 +48,17 @@ func (c ChainSet) Register(chainID string, ourHeight uint64, theirHeight int) er
c.Set.Set([]byte(chainID), data) c.Set.Set([]byte(chainID), data)
return nil return nil
} }
// Packet is a wrapped transaction and permission that we want to
// send off to another chain.
type Packet struct {
DestChain string `json:"dest_chain"`
Sequence int `json:"sequence"`
Permissions []basecoin.Actor `json:"permissions"`
Tx basecoin.Tx `json:"tx"`
}
// Bytes returns a serialization of the Packet
func (p Packet) Bytes() []byte {
return wire.BinaryBytes(p)
}

View File

@ -13,21 +13,21 @@ const (
// 0x3? series for ibc // 0x3? series for ibc
ByteRegisterChain = byte(0x30) ByteRegisterChain = byte(0x30)
ByteUpdateChain = byte(0x31) ByteUpdateChain = byte(0x31)
BytePacketCreate = byte(0x32) ByteCreatePacket = byte(0x32)
BytePacketPost = byte(0x33) BytePostPacket = byte(0x33)
TypeRegisterChain = NameIBC + "/register" TypeRegisterChain = NameIBC + "/register"
TypeUpdateChain = NameIBC + "/update" TypeUpdateChain = NameIBC + "/update"
TypePacketCreate = NameIBC + "/create" TypeCreatePacket = NameIBC + "/create"
TypePacketPost = NameIBC + "/post" TypePostPacket = NameIBC + "/post"
) )
func init() { func init() {
basecoin.TxMapper. basecoin.TxMapper.
RegisterImplementation(RegisterChainTx{}, TypeRegisterChain, ByteRegisterChain). RegisterImplementation(RegisterChainTx{}, TypeRegisterChain, ByteRegisterChain).
RegisterImplementation(UpdateChainTx{}, TypeUpdateChain, ByteUpdateChain). RegisterImplementation(UpdateChainTx{}, TypeUpdateChain, ByteUpdateChain).
RegisterImplementation(PacketCreateTx{}, TypePacketCreate, BytePacketCreate). RegisterImplementation(CreatePacketTx{}, TypeCreatePacket, ByteCreatePacket).
RegisterImplementation(PacketPostTx{}, TypePacketPost, BytePacketPost) RegisterImplementation(PostPacketTx{}, TypePostPacket, BytePostPacket)
} }
// RegisterChainTx allows you to register a new chain on this blockchain // RegisterChainTx allows you to register a new chain on this blockchain
@ -70,20 +70,21 @@ func (u UpdateChainTx) Wrap() basecoin.Tx {
return basecoin.Tx{u} return basecoin.Tx{u}
} }
// PacketCreateTx is meant to be called by IPC, another module... // CreatePacketTx is meant to be called by IPC, another module...
// //
// this is the tx that will be sent to another app and the permissions it // this is the tx that will be sent to another app and the permissions it
// comes with (which must be a subset of the permissions on the current tx) // comes with (which must be a subset of the permissions on the current tx)
// //
// TODO: how to control who can create packets (can I just signed create packet?) // If must have the special `AllowIBC` permission from the app
type PacketCreateTx struct { // that can send this packet (so only coins can request SendTx packet)
type CreatePacketTx struct {
DestChain string `json:"dest_chain"` DestChain string `json:"dest_chain"`
Permissions []basecoin.Actor `json:"permissions"` Permissions []basecoin.Actor `json:"permissions"`
Tx basecoin.Tx `json:"tx"` Tx basecoin.Tx `json:"tx"`
} }
// ValidateBasic makes sure this is consistent - used to satisfy TxInner // ValidateBasic makes sure this is consistent - used to satisfy TxInner
func (p PacketCreateTx) ValidateBasic() error { func (p CreatePacketTx) ValidateBasic() error {
if p.DestChain == "" { if p.DestChain == "" {
return errors.ErrNoChain() return errors.ErrNoChain()
} }
@ -94,27 +95,32 @@ func (p PacketCreateTx) ValidateBasic() error {
} }
// Wrap - used to satisfy TxInner // Wrap - used to satisfy TxInner
func (p PacketCreateTx) Wrap() basecoin.Tx { func (p CreatePacketTx) Wrap() basecoin.Tx {
return basecoin.Tx{p} return basecoin.Tx{p}
} }
// PacketPostTx takes a wrapped packet from another chain and // PostPacketTx takes a wrapped packet from another chain and
// TODO!!! // TODO!!!
type PacketPostTx struct { // also think... which chains can relay packets???
// right now, enforce that these packets are only sent directly,
// not routed over the hub. add routing later.
type PostPacketTx struct {
// make sure we have this header...
FromChainID string // The immediate source of the packet, not always Packet.SrcChainID FromChainID string // The immediate source of the packet, not always Packet.SrcChainID
FromChainHeight uint64 // The block height in which Packet was committed, to check Proof FromChainHeight uint64 // The block height in which Packet was committed, to check Proof
Proof *merkle.IAVLProof // this proof must match the header and the packet.Bytes()
// Packet Proof *merkle.IAVLProof
Packet Packet
} }
// ValidateBasic makes sure this is consistent - used to satisfy TxInner // ValidateBasic makes sure this is consistent - used to satisfy TxInner
func (p PacketPostTx) ValidateBasic() error { func (p PostPacketTx) ValidateBasic() error {
// TODO // TODO
return nil return nil
} }
// Wrap - used to satisfy TxInner // Wrap - used to satisfy TxInner
func (p PacketPostTx) Wrap() basecoin.Tx { func (p PostPacketTx) Wrap() basecoin.Tx {
return basecoin.Tx{p} return basecoin.Tx{p}
} }