IBC implementation almost done
This commit is contained in:
parent
aaaf18ad71
commit
95a5a7c9a0
|
@ -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
|
||||
|
|
|
@ -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)).
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
|
128
x/ibc/mapper.go
128
x/ibc/mapper.go
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue