cosmos-sdk/modules/ibc/middleware.go

118 lines
3.3 KiB
Go

package ibc
import (
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
)
// Middleware allows us to verify the IBC proof on a packet and
// and if valid, attach this permission to the wrapped packet
type Middleware struct {
stack.PassInitState
stack.PassInitValidate
}
var _ stack.Middleware = Middleware{}
// NewMiddleware creates a role-checking middleware
func NewMiddleware() Middleware {
return Middleware{}
}
// Name - return name space
func (Middleware) Name() string {
return NameIBC
}
// CheckTx verifies the named chain and height is present, and verifies
// the merkle proof in the packet
func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.CheckResult, err error) {
// if it is not a PostPacket, just let it go through
post, ok := tx.Unwrap().(PostPacketTx)
if !ok {
return next.CheckTx(ctx, store, tx)
}
// parse this packet and get the ibc-enhanced tx and context
ictx, itx, err := m.verifyPost(ctx, store, post)
if err != nil {
return res, err
}
return next.CheckTx(ictx, store, itx)
}
// DeliverTx verifies the named chain and height is present, and verifies
// the merkle proof in the packet
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.DeliverResult, err error) {
// if it is not a PostPacket, just let it go through
post, ok := tx.Unwrap().(PostPacketTx)
if !ok {
return next.DeliverTx(ctx, store, tx)
}
// parse this packet and get the ibc-enhanced tx and context
ictx, itx, err := m.verifyPost(ctx, store, post)
if err != nil {
return res, err
}
return next.DeliverTx(ictx, store, itx)
}
// verifyPost accepts a message bound for this chain...
// TODO: think about relay
func (m Middleware) verifyPost(ctx basecoin.Context, store state.SimpleDB,
tx PostPacketTx) (ictx basecoin.Context, itx basecoin.Tx, err error) {
// make sure the chain is registered
from := tx.FromChainID
if !NewChainSet(store).Exists([]byte(from)) {
return ictx, itx, ErrNotRegistered(from)
}
// TODO: how to deal with routing/relaying???
packet := tx.Packet
if packet.DestChain != ctx.ChainID() {
return ictx, itx, ErrWrongDestChain(packet.DestChain)
}
// verify packet.Permissions all come from the other chain
if !packet.Permissions.AllHaveChain(tx.FromChainID) {
return ictx, itx, ErrCannotSetPermission()
}
// make sure this sequence number is the next in the list
q := InputQueue(store, from)
tail := q.Tail()
if packet.Sequence < tail {
return ictx, itx, ErrPacketAlreadyExists()
}
if packet.Sequence > tail {
return ictx, itx, ErrPacketOutOfOrder(tail)
}
// look up the referenced header
space := stack.PrefixedStore(from, store)
provider := newDBProvider(space)
seed, err := provider.GetExactHeight(int(tx.FromChainHeight))
if err != nil {
return ictx, itx, err
}
// verify the merkle hash....
root := seed.Header.AppHash
pBytes := packet.Bytes()
err = tx.Proof.Verify(tx.Key, pBytes, root)
if err != nil {
return ictx, itx, ErrInvalidProofWithReason(err)
}
// add to input queue
q.Push(pBytes)
// return the wrapped tx along with the extra permissions
ictx = ctx.WithPermissions(packet.Permissions...)
itx = packet.Tx
return
}