2018-04-25 07:18:06 -07:00
|
|
|
package cli
|
2018-03-14 08:14:04 -07:00
|
|
|
|
|
|
|
import (
|
2018-04-03 09:19:47 -07:00
|
|
|
"os"
|
2018-03-14 08:14:04 -07:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
|
2018-07-02 13:34:06 -07:00
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
2018-04-03 09:19:47 -07:00
|
|
|
|
2018-04-02 05:05:23 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/context"
|
2018-03-15 10:01:33 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2018-03-15 05:49:18 -07:00
|
|
|
wire "github.com/cosmos/cosmos-sdk/wire"
|
2018-05-23 19:47:33 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
2018-04-25 07:18:06 -07:00
|
|
|
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
2018-03-14 08:14:04 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/ibc"
|
|
|
|
)
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// flags
|
2018-03-15 10:01:33 -07:00
|
|
|
const (
|
2018-03-16 13:39:42 -07:00
|
|
|
FlagFromChainID = "from-chain-id"
|
|
|
|
FlagFromChainNode = "from-chain-node"
|
|
|
|
FlagToChainID = "to-chain-id"
|
|
|
|
FlagToChainNode = "to-chain-node"
|
2018-03-15 10:01:33 -07:00
|
|
|
)
|
|
|
|
|
2018-03-18 05:32:16 -07:00
|
|
|
type relayCommander struct {
|
2018-03-19 04:57:21 -07:00
|
|
|
cdc *wire.Codec
|
2018-07-06 00:06:53 -07:00
|
|
|
address sdk.AccAddress
|
2018-05-23 19:47:33 -07:00
|
|
|
decoder auth.AccountDecoder
|
2018-03-19 04:57:21 -07:00
|
|
|
mainStore string
|
|
|
|
ibcStore string
|
2018-05-21 00:53:59 -07:00
|
|
|
accStore string
|
2018-04-03 09:19:47 -07:00
|
|
|
|
|
|
|
logger log.Logger
|
2018-03-18 05:32:16 -07:00
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// IBC relay command
|
2018-03-15 05:48:52 -07:00
|
|
|
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
2018-03-15 10:01:33 -07:00
|
|
|
cmdr := relayCommander{
|
2018-03-19 04:57:21 -07:00
|
|
|
cdc: cdc,
|
2018-03-24 23:08:27 -07:00
|
|
|
decoder: authcmd.GetAccountDecoder(cdc),
|
2018-03-19 04:57:21 -07:00
|
|
|
ibcStore: "ibc",
|
|
|
|
mainStore: "main",
|
2018-05-21 00:53:59 -07:00
|
|
|
accStore: "acc",
|
2018-04-03 09:19:47 -07:00
|
|
|
|
|
|
|
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
2018-03-15 10:01:33 -07:00
|
|
|
}
|
2018-03-14 08:14:04 -07:00
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "relay",
|
|
|
|
Run: cmdr.runIBCRelay,
|
|
|
|
}
|
2018-03-18 05:32:16 -07:00
|
|
|
|
2018-03-16 13:39:42 -07:00
|
|
|
cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets")
|
2018-06-13 15:13:51 -07:00
|
|
|
cmd.Flags().String(FlagFromChainNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
|
2018-03-16 13:39:42 -07:00
|
|
|
cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets")
|
2018-03-18 08:24:48 -07:00
|
|
|
cmd.Flags().String(FlagToChainNode, "tcp://localhost:36657", "<host>:<port> to tendermint rpc interface for this chain")
|
2018-03-18 05:32:16 -07:00
|
|
|
|
2018-03-16 13:39:42 -07:00
|
|
|
cmd.MarkFlagRequired(FlagFromChainID)
|
|
|
|
cmd.MarkFlagRequired(FlagFromChainNode)
|
|
|
|
cmd.MarkFlagRequired(FlagToChainID)
|
|
|
|
cmd.MarkFlagRequired(FlagToChainNode)
|
2018-03-18 05:32:16 -07:00
|
|
|
|
2018-03-16 13:39:42 -07:00
|
|
|
viper.BindPFlag(FlagFromChainID, cmd.Flags().Lookup(FlagFromChainID))
|
|
|
|
viper.BindPFlag(FlagFromChainNode, cmd.Flags().Lookup(FlagFromChainNode))
|
|
|
|
viper.BindPFlag(FlagToChainID, cmd.Flags().Lookup(FlagToChainID))
|
|
|
|
viper.BindPFlag(FlagToChainNode, cmd.Flags().Lookup(FlagToChainNode))
|
|
|
|
|
2018-03-14 08:14:04 -07:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-06-29 15:22:24 -07:00
|
|
|
// nolint: unparam
|
2018-03-14 08:14:04 -07:00
|
|
|
func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
|
2018-03-16 13:39:42 -07:00
|
|
|
fromChainID := viper.GetString(FlagFromChainID)
|
|
|
|
fromChainNode := viper.GetString(FlagFromChainNode)
|
|
|
|
toChainID := viper.GetString(FlagToChainID)
|
|
|
|
toChainNode := viper.GetString(FlagToChainNode)
|
2018-04-02 05:05:23 -07:00
|
|
|
address, err := context.NewCoreContextFromViper().GetFromAddress()
|
2018-03-15 10:01:33 -07:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
c.address = address
|
|
|
|
|
2018-03-16 13:39:42 -07:00
|
|
|
c.loop(fromChainID, fromChainNode, toChainID, toChainNode)
|
2018-03-14 08:14:04 -07:00
|
|
|
}
|
|
|
|
|
2018-07-09 14:44:01 -07:00
|
|
|
// This is nolinted as someone is in the process of refactoring this to remove the goto
|
|
|
|
// nolint: gocyclo
|
2018-04-08 08:13:49 -07:00
|
|
|
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID,
|
|
|
|
toChainNode string) {
|
|
|
|
|
2018-04-02 05:05:23 -07:00
|
|
|
ctx := context.NewCoreContextFromViper()
|
2018-03-19 04:57:21 -07:00
|
|
|
// get password
|
2018-03-30 06:12:50 -07:00
|
|
|
passphrase, err := ctx.GetPassphraseFromStdin(ctx.FromAddressName)
|
2018-03-19 04:57:21 -07:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2018-03-18 05:32:16 -07:00
|
|
|
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
2018-04-18 21:49:24 -07:00
|
|
|
|
2018-03-14 08:14:04 -07:00
|
|
|
OUTER:
|
|
|
|
for {
|
2018-04-02 15:56:02 -07:00
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
|
|
|
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var processed int64
|
|
|
|
if processedbz == nil {
|
|
|
|
processed = 0
|
|
|
|
} else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-03-14 08:14:04 -07:00
|
|
|
|
2018-03-16 13:39:42 -07:00
|
|
|
lengthKey := ibc.EgressLengthKey(toChainID)
|
|
|
|
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
2018-03-14 08:14:04 -07:00
|
|
|
if err != nil {
|
2018-06-13 12:13:22 -07:00
|
|
|
c.logger.Error("error querying outgoing packet list length", "err", err)
|
2018-04-18 21:49:24 -07:00
|
|
|
continue OUTER //TODO replace with continue (I think it should just to the correct place where OUTER is now)
|
2018-03-14 08:14:04 -07:00
|
|
|
}
|
|
|
|
var egressLength int64
|
2018-03-16 13:39:42 -07:00
|
|
|
if egressLengthbz == nil {
|
|
|
|
egressLength = 0
|
|
|
|
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
2018-03-14 08:14:04 -07:00
|
|
|
panic(err)
|
|
|
|
}
|
2018-04-02 15:56:02 -07:00
|
|
|
if egressLength > processed {
|
2018-04-03 09:24:50 -07:00
|
|
|
c.logger.Info("Detected IBC packet", "number", egressLength-1)
|
2018-04-02 15:56:02 -07:00
|
|
|
}
|
2018-03-14 08:14:04 -07:00
|
|
|
|
2018-04-03 09:19:47 -07:00
|
|
|
seq := c.getSequence(toChainNode)
|
|
|
|
|
2018-03-14 08:14:04 -07:00
|
|
|
for i := processed; i < egressLength; i++ {
|
2018-03-16 13:39:42 -07:00
|
|
|
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
2018-03-14 08:14:04 -07:00
|
|
|
if err != nil {
|
2018-06-13 12:13:22 -07:00
|
|
|
c.logger.Error("error querying egress packet", "err", err)
|
2018-04-18 21:49:24 -07:00
|
|
|
continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER)
|
2018-03-14 08:14:04 -07:00
|
|
|
}
|
|
|
|
|
2018-04-03 13:23:09 -07:00
|
|
|
err = c.broadcastTx(seq, toChainNode, c.refine(egressbz, i, passphrase))
|
2018-04-03 09:19:47 -07:00
|
|
|
seq++
|
2018-03-14 08:14:04 -07:00
|
|
|
if err != nil {
|
2018-06-13 12:13:22 -07:00
|
|
|
c.logger.Error("error broadcasting ingress packet", "err", err)
|
2018-04-18 21:49:24 -07:00
|
|
|
continue OUTER // TODO replace to break, will break first loop then send back to the beginning (aka OUTER)
|
2018-03-14 08:14:04 -07:00
|
|
|
}
|
|
|
|
|
2018-04-03 09:19:47 -07:00
|
|
|
c.logger.Info("Relayed IBC packet", "number", i)
|
2018-03-14 08:14:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-18 05:32:16 -07:00
|
|
|
|
2018-03-19 04:57:21 -07:00
|
|
|
func query(node string, key []byte, storeName string) (res []byte, err error) {
|
2018-06-13 22:49:21 -07:00
|
|
|
return context.NewCoreContextFromViper().WithNodeURI(node).QueryStore(key, storeName)
|
2018-03-18 05:32:16 -07:00
|
|
|
}
|
|
|
|
|
2018-04-03 13:23:09 -07:00
|
|
|
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {
|
|
|
|
_, err := context.NewCoreContextFromViper().WithNodeURI(node).WithSequence(seq + 1).BroadcastTx(tx)
|
2018-03-18 05:32:16 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-03-19 04:57:21 -07:00
|
|
|
func (c relayCommander) getSequence(node string) int64 {
|
2018-05-21 00:53:59 -07:00
|
|
|
res, err := query(node, c.address, c.accStore)
|
2018-03-19 04:57:21 -07:00
|
|
|
if err != nil {
|
2018-03-18 05:32:16 -07:00
|
|
|
panic(err)
|
|
|
|
}
|
2018-05-21 00:53:59 -07:00
|
|
|
if nil != res {
|
|
|
|
account, err := c.decoder(res)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-04-02 15:56:02 -07:00
|
|
|
|
2018-05-21 00:53:59 -07:00
|
|
|
return account.GetSequence()
|
2018-03-18 09:21:26 -07:00
|
|
|
}
|
|
|
|
|
2018-05-21 00:53:59 -07:00
|
|
|
return 0
|
2018-03-19 04:57:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte {
|
|
|
|
var packet ibc.IBCPacket
|
|
|
|
if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2018-03-18 05:32:16 -07:00
|
|
|
msg := ibc.IBCReceiveMsg{
|
|
|
|
IBCPacket: packet,
|
|
|
|
Relayer: c.address,
|
|
|
|
Sequence: sequence,
|
|
|
|
}
|
2018-03-18 09:21:26 -07:00
|
|
|
|
2018-05-21 00:53:59 -07:00
|
|
|
ctx := context.NewCoreContextFromViper().WithSequence(sequence)
|
2018-06-21 15:05:25 -07:00
|
|
|
res, err := ctx.SignAndBuild(ctx.FromAddressName, passphrase, []sdk.Msg{msg}, c.cdc)
|
2018-03-18 05:32:16 -07:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|