IBC implementation almost done

This commit is contained in:
Adrian Brink 2018-03-18 13:32:16 +01:00
parent aaaf18ad71
commit 95a5a7c9a0
No known key found for this signature in database
GPG Key ID: F61053D3FBD06353
6 changed files with 141 additions and 101 deletions

View File

@ -9,6 +9,9 @@ The initial implementation of IBC will include just enough for simple coin
transfers between chains, with safety features such as ACK messages being added
later.
It is a complete stand-alone module. It includes the commands to send IBC
packets as well as to post them to the destination chain.
### IBC Module
```go

View File

@ -56,7 +56,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolMapper := cool.NewMapper(app.capKeyMainStore)
ibcMapper := ibc.NewMapper(app.capKeyIBCStore)
ibcMapper := ibc.NewIBCMapper(app.capKeyIBCStore)
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("cool", cool.NewHandler(coinKeeper, coolMapper)).

View File

@ -51,13 +51,13 @@ func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error
fmt.Printf("%+v\n", msg)
bz := sdk.StdSignBytes(viper.GetString(flagChain), []int64{viper.GetInt64(client.FlagSequence)}, msg)
res, err := builder.BroadcastTx(bz)
res, err := builder.SignBuildBroadcast(msg, c.cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
}
@ -73,10 +73,9 @@ func buildMsg(from sdk.Address) (sdk.Msg, error) {
if err != nil {
return nil, err
}
to := sdk.Address(bz)
packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(flagChain),
packet := ibc.NewIBCPacket(from, to, coins, client.FlagChainID,
viper.GetString(flagChain))
msg := ibc.IBCTransferMsg{

View File

@ -23,6 +23,12 @@ const (
FlagToChainNode = "to-chain-node"
)
type relayCommander struct {
cdc *wire.Codec
address sdk.Address
ibcStore string
}
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
cmdr := relayCommander{
cdc: cdc,
@ -33,17 +39,20 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
Use: "relay",
Run: cmdr.runIBCRelay,
}
cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign")
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets")
cmd.Flags().String(FlagFromChainNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets")
cmd.Flags().String(FlagToChainNode, "tcp://localhost:46658", "<host>:<port> to tendermint rpc interface for this chain")
cmd.MarkFlagRequired(client.FlagName)
cmd.MarkFlagRequired(FlagFromChainID)
cmd.MarkFlagRequired(FlagFromChainNode)
cmd.MarkFlagRequired(FlagToChainID)
cmd.MarkFlagRequired(FlagToChainNode)
viper.BindPFlag(client.FlagName, cmd.Flags().Lookup(client.FlagName))
viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode))
viper.BindPFlag(FlagFromChainID, cmd.Flags().Lookup(FlagFromChainID))
@ -54,18 +63,13 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
return cmd
}
type relayCommander struct {
cdc *wire.Codec
address sdk.Address
ibcStore string
}
func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
fromChainID := viper.GetString(FlagFromChainID)
fromChainNode := viper.GetString(FlagFromChainNode)
toChainID := viper.GetString(FlagToChainID)
toChainNode := viper.GetString(FlagToChainNode)
address, err := builder.GetFromAddress()
if err != nil {
panic(err)
}
@ -74,44 +78,8 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
c.loop(fromChainID, fromChainNode, toChainID, toChainNode)
}
// https://github.com/cosmos/cosmos-sdk/blob/master/client/helpers.go using specified address
func query(id string, key []byte, storeName string) (res []byte, err error) {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, id)
res, err = builder.Query(key, storeName)
viper.Set(client.FlagNode, orig)
return res, err
}
func broadcastTx(id string, tx []byte) error {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, id)
_, err := builder.BroadcastTx(tx)
viper.Set(client.FlagNode, orig)
return err
}
func (c relayCommander) refine(bz []byte, sequence int64) []byte {
var packet ibc.IBCPacket
if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil {
panic(err)
}
msg := ibc.IBCReceiveMsg{
IBCPacket: packet,
Relayer: c.address,
Sequence: sequence,
}
res, err := builder.SignAndBuild(msg, c.cdc)
if err != nil {
panic(err)
}
return res
}
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
ingressKey := ibc.IngressKey(fromChainID)
ingressKey := ibc.IngressSequenceKey(fromChainID)
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
if err != nil {
@ -161,3 +129,37 @@ OUTER:
processed = egressLength
}
}
func query(id string, key []byte, storeName string) (res []byte, err error) {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, id)
res, err = builder.Query(key, storeName)
viper.Set(client.FlagNode, orig)
return res, err
}
func broadcastTx(id string, tx []byte) error {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, id)
_, err := builder.BroadcastTx(tx)
viper.Set(client.FlagNode, orig)
return err
}
func (c relayCommander) refine(bz []byte, sequence int64) []byte {
var packet ibc.IBCPacket
if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil {
panic(err)
}
msg := ibc.IBCReceiveMsg{
IBCPacket: packet,
Relayer: c.address,
Sequence: sequence,
}
res, err := builder.SignAndBuild(msg, c.cdc)
if err != nil {
panic(err)
}
return res
}

View File

@ -4,7 +4,6 @@ import (
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
)
@ -22,32 +21,37 @@ func NewHandler(ibcm IBCMapper, ck bank.CoinKeeper) sdk.Handler {
}
}
// Handle outgoing IBC packets.
func handleIBCTransferMsg(ctx sdk.Context, ibcm IBCMapper, ck bank.CoinKeeper, msg IBCTransferMsg) sdk.Result {
packet := msg.IBCPacket
_, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins)
if err != nil {
return err.Result()
}
ibcm.PushPacket(ctx, packet)
err = ibcm.PostIBCPacket(ctx, packet)
if err != nil {
return err.Result()
}
return sdk.Result{}
}
func handleIBCReceiveMsg(ctx sdk.Context, ibcm IBCMapper, ck bank.CoinKeeper, msg IBCReceiveMsg) sdk.Result {
packet := msg.IBCPacket
seq := ibcm.GetIngressSequence(ctx, packet.SrcChain)
if msg.Sequence != seq {
return ErrInvalidSequence().Result()
}
ibcm.SetIngressSequence(ctx, packet.SrcChain, seq+1)
_, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins)
if err != nil {
return err.Result()
}
// handle packet
// packet.Handle(ctx)...
ibcm.SetIngressSequence(ctx, packet.SrcChain, seq+1)
return sdk.Result{}
}

View File

@ -4,36 +4,95 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
)
type IBCMapper struct {
ibcKey sdk.StoreKey
cdc *wire.Codec
key sdk.StoreKey
cdc *wire.Codec
}
func NewMapper(ibcKey sdk.StoreKey) IBCMapper {
// XXX: The IBCMapper should not take a CoinKeeper. Rather have the CoinKeeper
// take an IBCMapper.
func NewIBCMapper(key sdk.StoreKey) IBCMapper {
// XXX: How are these codecs supposed to work?
cdc := wire.NewCodec()
return IBCMapper{
ibcKey: ibcKey,
cdc: cdc,
key: key,
cdc: cdc,
}
}
func IngressKey(srcChain string) []byte {
return []byte(fmt.Sprintf("ingress/%s", srcChain))
// XXX: This is not the public API. This will change in MVP2 and will henceforth
// only be invoked from another module directly and not through a user
// transaction.
// TODO: Handle invalid IBC packets and return errors.
func (ibcm IBCMapper) PostIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error {
// write everything into the state
store := ctx.KVStore(ibcm.key)
index := ibcm.getEgressLength(store, packet.DestChain)
bz, err := ibcm.cdc.MarshalBinary(packet)
if err != nil {
panic(err)
}
store.Set(EgressKey(packet.DestChain, index), bz)
bz, err = ibcm.cdc.MarshalBinary(int64(index + 1))
if err != nil {
panic(err)
}
store.Set(EgressLengthKey(packet.DestChain), bz)
return nil
}
func EgressKey(destChain string, index int64) []byte {
return []byte(fmt.Sprintf("egress/%s/%d", destChain, index))
// XXX: In the future every module is able to register it's own handler for
// handling it's own IBC packets. The "ibc" handler will only route the packets
// to the appropriate callbacks.
// XXX: For now this handles all interactions with the CoinKeeper.
// XXX: This needs to do some authentication checking.
func (ibcm IBCMapper) ReceiveIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error {
return nil
}
func EgressLengthKey(destChain string) []byte {
return []byte(fmt.Sprintf("egress/%s", destChain))
// --------------------------
// Functions for accessing the underlying KVStore.
func (ibcm IBCMapper) GetIngressSequence(ctx sdk.Context, srcChain string) int64 {
store := ctx.KVStore(ibcm.key)
key := IngressSequenceKey(srcChain)
bz := store.Get(key)
if bz == nil {
zero, err := ibcm.cdc.MarshalBinary(int64(0))
if err != nil {
panic(err)
}
store.Set(key, zero)
return 0
}
var res int64
err := ibcm.cdc.UnmarshalBinary(bz, &res)
if err != nil {
panic(err)
}
return res
}
func (ibcm IBCMapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence int64) {
store := ctx.KVStore(ibcm.key)
key := IngressSequenceKey(srcChain)
bz, err := ibcm.cdc.MarshalBinary(sequence)
if err != nil {
panic(err)
}
store.Set(key, bz)
}
// Retrieves the index of the currently stored outgoing IBC packets.
func (ibcm IBCMapper) getEgressLength(store sdk.KVStore, destChain string) int64 {
bz := store.Get(EgressLengthKey(destChain))
if bz == nil {
@ -51,44 +110,17 @@ func (ibcm IBCMapper) getEgressLength(store sdk.KVStore, destChain string) int64
return res
}
func (ibcm IBCMapper) GetIngressSequence(ctx sdk.Context, srcChain string) int64 {
store := ctx.KVStore(ibcm.ibcKey)
bz := store.Get(IngressKey(srcChain))
if bz == nil {
zero, err := ibcm.cdc.MarshalBinary(int64(0))
if err != nil {
panic(err)
}
store.Set(IngressKey(srcChain), zero)
return 0
}
var res int64
if err := ibcm.cdc.UnmarshalBinary(bz, &res); err != nil {
panic(err)
}
return res
// Stores an outgoing IBC packet under "egress/chain_id/index".
func EgressKey(destChain string, index int64) []byte {
return []byte(fmt.Sprintf("egress/%s/%d", destChain, index))
}
func (ibcm IBCMapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence int64) {
store := ctx.KVStore(ibcm.ibcKey)
bz, err := ibcm.cdc.MarshalBinary(sequence)
if err != nil {
panic(err)
}
store.Set(IngressKey(srcChain), bz)
// Stores the number of outgoing IBC packets under "egress/index".
func EgressLengthKey(destChain string) []byte {
return []byte(fmt.Sprintf("egress/%s", destChain))
}
func (ibcm IBCMapper) PushPacket(ctx sdk.Context, packet IBCPacket) {
store := ctx.KVStore(ibcm.ibcKey)
len := ibcm.getEgressLength(store, packet.DestChain)
packetbz, err := ibcm.cdc.MarshalBinary(packet)
if err != nil {
panic(err)
}
store.Set(EgressKey(packet.DestChain, len), packetbz)
lenbz, err := ibcm.cdc.MarshalBinary(int64(len + 1))
if err != nil {
panic(err)
}
store.Set(EgressLengthKey(packet.DestChain), lenbz)
// Stores the sequence number of incoming IBC packet under "ingress/index".
func IngressSequenceKey(srcChain string) []byte {
return []byte(fmt.Sprintf("ingress/%s", srcChain))
}