commit
03fe47192d
|
@ -463,6 +463,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "3d1aebf798b51882ed5281b0ca0a3766c32cdb08592de57dd3d574a4b3e67987"
|
||||
inputs-digest = "64881873c2a0899c3d6920de588c06a2b59e6b072e1a3d7e676e906cb7d5ad0e"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/bgentry/speakeasy"
|
||||
version = "0.1.0"
|
||||
|
@ -41,10 +40,6 @@
|
|||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
# [[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/rigelrozanski/common"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "0.0.1"
|
||||
|
|
4
Makefile
4
Makefile
|
@ -71,8 +71,10 @@ test_unit:
|
|||
@go test $(PACKAGES)
|
||||
|
||||
test_cover:
|
||||
@rm -rf examples/basecoin/vendor/
|
||||
@rm -rf examples/basecoin/vendor
|
||||
@rm -rf client/lcd/keys.db ~/.tendermint_test
|
||||
@bash tests/test_cover.sh
|
||||
@rm -rf client/lcd/keys.db ~/.tendermint_test
|
||||
|
||||
benchmark:
|
||||
@go test -bench=. $(PACKAGES)
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
# IBC Spec
|
||||
|
||||
*This is a living document and should be edited as the IBC spec and implementation change*
|
||||
*This is a living document and should be edited as the IBC spec and
|
||||
implementation change*
|
||||
|
||||
## MVP1
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
```golang
|
||||
```go
|
||||
// User facing API
|
||||
|
||||
type IBCPacket struct {
|
||||
DestAddr sdk.Address
|
||||
Coins sdk.Coins
|
||||
SrcChain string
|
||||
SrcAddr sdk.Address
|
||||
DestAddr sdk.Address
|
||||
Coins sdk.Coins
|
||||
SrcChain string
|
||||
DestChain string
|
||||
}
|
||||
|
||||
|
@ -26,6 +33,8 @@ type IBCTransferMsg struct {
|
|||
// Implements sdk.Msg
|
||||
type IBCReceiveMsg struct {
|
||||
IBCPacket
|
||||
Relayer sdk.Address
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
// Internal API
|
||||
|
@ -47,9 +56,12 @@ type EgressKey struct {
|
|||
|
||||
```
|
||||
|
||||
`egressKey` stores the outgoing `IBCTransfer`s as a list. Its getter takes an `EgressKey` and returns the length if `egressKey.Index == -1`, an element if `egressKey.Index > 0`.
|
||||
`egressKey` stores the outgoing `IBCTransfer`s as a list. Its getter takes an
|
||||
`EgressKey` and returns the length if `egressKey.Index == -1`, an element if
|
||||
`egressKey.Index > 0`.
|
||||
|
||||
`ingressKey` stores the last income `IBCTransfer`'s sequence. Its getter takes an `IngressKey`.
|
||||
`ingressKey` stores the latest income `IBCTransfer`'s sequence. It's getter
|
||||
takes an `IngressKey`.
|
||||
|
||||
## Relayer
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ type Payload interface {
|
|||
}
|
||||
|
||||
type TransferPayload struct {
|
||||
SrcAddr sdk.Address
|
||||
DestAddr sdk.Address
|
||||
Coins sdk.Coins
|
||||
}
|
||||
|
@ -35,6 +36,8 @@ type IBCTransferMsg struct {
|
|||
// Implements sdk.Msg
|
||||
type IBCReceiveMsg struct {
|
||||
Packet
|
||||
Relayer sdk.Address
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
// Internal API
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
|
||||
|
@ -55,17 +56,19 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
|||
// add handlers
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolMapper := cool.NewMapper(app.capKeyMainStore)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coinKeeper, coolMapper)).
|
||||
AddRoute("sketchy", sketchy.NewHandler())
|
||||
AddRoute("sketchy", sketchy.NewHandler()).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetTxDecoder(app.txDecoder)
|
||||
app.SetInitChainer(app.initChainer)
|
||||
// TODO: mounting multiple stores is broken
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/532
|
||||
app.MountStoresIAVL(app.capKeyMainStore) // , app.capKeyIBCStore)
|
||||
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
|
||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
||||
if err != nil {
|
||||
|
@ -83,12 +86,16 @@ func MakeCodec() *wire.Codec {
|
|||
const msgTypeIssue = 0x2
|
||||
const msgTypeQuiz = 0x3
|
||||
const msgTypeSetTrend = 0x4
|
||||
const msgTypeIBCTransferMsg = 0x5
|
||||
const msgTypeIBCReceiveMsg = 0x6
|
||||
var _ = oldwire.RegisterInterface(
|
||||
struct{ sdk.Msg }{},
|
||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
||||
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
||||
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
||||
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
||||
)
|
||||
|
||||
const accTypeApp = 0x1
|
||||
|
@ -101,6 +108,7 @@ func MakeCodec() *wire.Codec {
|
|||
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
|
||||
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
|
||||
// ibc.RegisterWire(cdc) // Register ibc.[IBCTransferMsg, IBCReceiveMsg] types.
|
||||
return cdc
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
@ -141,13 +142,13 @@ func TestGenesis(t *testing.T) {
|
|||
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, acc, res1)
|
||||
|
||||
// reload app and ensure the account is still there
|
||||
bapp = NewBasecoinApp(logger, db)
|
||||
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, acc, res1)
|
||||
|
||||
/*
|
||||
// reload app and ensure the account is still there
|
||||
bapp = NewBasecoinApp(logger, db)
|
||||
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, acc, res1)
|
||||
*/
|
||||
}
|
||||
|
||||
func TestSendMsgWithAccounts(t *testing.T) {
|
||||
|
@ -271,6 +272,59 @@ func TestQuizMsg(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
sourceChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
vals := []abci.Validator{}
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
genesisState := types.GenesisState{
|
||||
Accounts: []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
packet := ibc.IBCPacket{
|
||||
SrcAddr: addr1,
|
||||
DestAddr: addr1,
|
||||
Coins: coins,
|
||||
SrcChain: sourceChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
|
||||
transferMsg := ibc.IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
receiveMsg := ibc.IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: addr1,
|
||||
Sequence: 0,
|
||||
}
|
||||
|
||||
SignCheckDeliver(t, bapp, transferMsg, 0, true)
|
||||
CheckBalance(t, bapp, "")
|
||||
SignCheckDeliver(t, bapp, transferMsg, 1, false)
|
||||
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
|
||||
CheckBalance(t, bapp, "10foocoin")
|
||||
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
|
||||
}
|
||||
|
||||
func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, expPass bool) {
|
||||
|
||||
// Sign the tx
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
|
@ -69,6 +70,14 @@ func main() {
|
|||
client.PostCommands(
|
||||
coolcmd.SetTrendTxCmd(cdc),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
basecliCmd.AddCommand(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
//"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -30,6 +30,7 @@ func TestStartStandAlone(t *testing.T) {
|
|||
RunOrTimeout(startCmd, timeout, t)
|
||||
}
|
||||
|
||||
/*
|
||||
func TestStartWithTendermint(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
|
||||
|
@ -51,3 +52,4 @@ func TestStartWithTendermint(t *testing.T) {
|
|||
|
||||
RunOrTimeout(startCmd, timeout, t)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -17,7 +17,7 @@ func (code CodeType) IsOK() bool {
|
|||
}
|
||||
|
||||
// ABCI Response Codes
|
||||
// Base SDK reserves 0 ~ 99.
|
||||
// Base SDK reserves 0 - 99.
|
||||
const (
|
||||
CodeOK CodeType = 0
|
||||
CodeInternal CodeType = 1
|
||||
|
@ -56,6 +56,8 @@ func CodeToDefaultMsg(code CodeType) string {
|
|||
return "Invalid address"
|
||||
case CodeInvalidPubKey:
|
||||
return "Invalid pubkey"
|
||||
case CodeUnknownAddress:
|
||||
return "Unknown address"
|
||||
case CodeInsufficientCoins:
|
||||
return "Insufficient coins"
|
||||
case CodeInvalidCoins:
|
||||
|
|
|
@ -14,7 +14,7 @@ var codeTypes = []CodeType{
|
|||
CodeUnauthorized,
|
||||
CodeInsufficientFunds,
|
||||
CodeUnknownRequest,
|
||||
CodeUnrecognizedAddress,
|
||||
CodeUnknownAddress,
|
||||
CodeInvalidPubKey,
|
||||
CodeGenesisParse,
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ var errFns = []errFn{
|
|||
ErrUnauthorized,
|
||||
ErrInsufficientFunds,
|
||||
ErrUnknownRequest,
|
||||
ErrUnrecognizedAddress,
|
||||
ErrUnknownAddress,
|
||||
ErrInvalidPubKey,
|
||||
ErrGenesisParse,
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@ import (
|
|||
|
||||
// Register concrete types on wire codec
|
||||
func RegisterWire(cdc *wire.Codec) {
|
||||
// TODO: bring this back ...
|
||||
/*
|
||||
// TODO include option to always include prefix bytes.
|
||||
cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/SendMsg", nil)
|
||||
cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/IssueMsg", nil)
|
||||
*/
|
||||
// TODO include option to always include prefix bytes.
|
||||
//cdc.RegisterConcrete(SendMsg{}, "github.com/cosmos/cosmos-sdk/bank/SendMsg", nil)
|
||||
//cdc.RegisterConcrete(IssueMsg{}, "github.com/cosmos/cosmos-sdk/bank/IssueMsg", nil)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# IBC CLI Usage
|
||||
|
||||
## initialize
|
||||
|
||||
```bash
|
||||
basecoind init # copy the recover key
|
||||
basecli keys add keyname --recover
|
||||
basecoind start
|
||||
```
|
||||
|
||||
## transfer
|
||||
|
||||
`transfer` sends coins from one chain to another(or itself).
|
||||
|
||||
```bash
|
||||
basecli transfer --name keyname --to address_of_destination --amount 10mycoin --chain test-chain-AAAAAA --chain-id AAAAAA
|
||||
```
|
||||
|
||||
The id of the chain can be found in `$HOME/.basecoind/config/genesis.json`
|
||||
|
||||
## relay
|
||||
|
||||
```bash
|
||||
basecli relay --name keyname --from-chain-id test-chain-AAAAAA --from-chain-node=tcp://0.0.0.0:46657 --to-chain-id test-chain-AAAAAA --to-chain-node=tcp://0.0.0.0:46657
|
||||
```
|
|
@ -0,0 +1,94 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
)
|
||||
|
||||
const (
|
||||
flagTo = "to"
|
||||
flagAmount = "amount"
|
||||
flagChain = "chain"
|
||||
)
|
||||
|
||||
func IBCTransferCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmdr := sendCommander{cdc}
|
||||
cmd := &cobra.Command{
|
||||
Use: "transfer",
|
||||
RunE: cmdr.sendIBCTransfer,
|
||||
}
|
||||
cmd.Flags().String(flagTo, "", "Address to send coins")
|
||||
cmd.Flags().String(flagAmount, "", "Amount of coins to send")
|
||||
cmd.Flags().String(flagChain, "", "Destination chain to send coins")
|
||||
return cmd
|
||||
}
|
||||
|
||||
type sendCommander struct {
|
||||
cdc *wire.Codec
|
||||
}
|
||||
|
||||
func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error {
|
||||
// get the from address
|
||||
from, err := builder.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build the message
|
||||
msg, err := buildMsg(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get password
|
||||
name := viper.GetString(client.FlagName)
|
||||
buf := client.BufferStdin()
|
||||
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
||||
passphrase, err := client.GetPassword(prompt, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := builder.SignBuildBroadcast(name, passphrase, msg, c.cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildMsg(from sdk.Address) (sdk.Msg, error) {
|
||||
amount := viper.GetString(flagAmount)
|
||||
coins, err := sdk.ParseCoins(amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dest := viper.GetString(flagTo)
|
||||
bz, err := hex.DecodeString(dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
to := sdk.Address(bz)
|
||||
|
||||
packet := ibc.NewIBCPacket(from, to, coins, client.FlagChainID,
|
||||
viper.GetString(flagChain))
|
||||
|
||||
msg := ibc.IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
)
|
||||
|
||||
const (
|
||||
FlagFromChainID = "from-chain-id"
|
||||
FlagFromChainNode = "from-chain-node"
|
||||
FlagToChainID = "to-chain-id"
|
||||
FlagToChainNode = "to-chain-node"
|
||||
)
|
||||
|
||||
type relayCommander struct {
|
||||
cdc *wire.Codec
|
||||
address sdk.Address
|
||||
parser sdk.ParseAccount
|
||||
mainStore string
|
||||
ibcStore string
|
||||
}
|
||||
|
||||
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmdr := relayCommander{
|
||||
cdc: cdc,
|
||||
parser: authcmd.GetParseAccount(cdc),
|
||||
ibcStore: "ibc",
|
||||
mainStore: "main",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "relay",
|
||||
Run: cmdr.runIBCRelay,
|
||||
}
|
||||
|
||||
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:36657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
|
||||
cmd.MarkFlagRequired(FlagFromChainID)
|
||||
cmd.MarkFlagRequired(FlagFromChainNode)
|
||||
cmd.MarkFlagRequired(FlagToChainID)
|
||||
cmd.MarkFlagRequired(FlagToChainNode)
|
||||
|
||||
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))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
c.address = address
|
||||
|
||||
c.loop(fromChainID, fromChainNode, toChainID, toChainNode)
|
||||
}
|
||||
|
||||
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
|
||||
// get password
|
||||
name := viper.GetString(client.FlagName)
|
||||
buf := client.BufferStdin()
|
||||
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
||||
passphrase, err := client.GetPassword(prompt, buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
OUTER:
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
lengthKey := ibc.EgressLengthKey(toChainID)
|
||||
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying outgoing packet list length: '%s'\n", err)
|
||||
continue OUTER
|
||||
}
|
||||
var egressLength int64
|
||||
if egressLengthbz == nil {
|
||||
egressLength = 0
|
||||
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("egressLength queried: %d\n", egressLength)
|
||||
|
||||
for i := processed; i < egressLength; i++ {
|
||||
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying egress packet: '%s'\n", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
err = c.broadcastTx(toChainNode, c.refine(egressbz, i, passphrase))
|
||||
if err != nil {
|
||||
fmt.Printf("Error broadcasting ingress packet: '%s'\n", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
fmt.Printf("Relayed packet: %d\n", i)
|
||||
}
|
||||
|
||||
processed = egressLength
|
||||
}
|
||||
}
|
||||
|
||||
func query(node string, key []byte, storeName string) (res []byte, err error) {
|
||||
orig := viper.GetString(client.FlagNode)
|
||||
viper.Set(client.FlagNode, node)
|
||||
res, err = builder.Query(key, storeName)
|
||||
viper.Set(client.FlagNode, orig)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (c relayCommander) broadcastTx(node string, tx []byte) error {
|
||||
orig := viper.GetString(client.FlagNode)
|
||||
viper.Set(client.FlagNode, node)
|
||||
seq := c.getSequence(node) + 1
|
||||
viper.Set(client.FlagSequence, seq)
|
||||
_, err := builder.BroadcastTx(tx)
|
||||
viper.Set(client.FlagNode, orig)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c relayCommander) getSequence(node string) int64 {
|
||||
res, err := query(node, c.address, c.mainStore)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
account, err := c.parser(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return account.GetSequence()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
msg := ibc.IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: c.address,
|
||||
Sequence: sequence,
|
||||
}
|
||||
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignAndBuild(name, passphrase, msg, c.cdc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// IBC errors reserve 200 - 299.
|
||||
CodeInvalidSequence sdk.CodeType = 200
|
||||
CodeIdenticalChains sdk.CodeType = 201
|
||||
CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
func codeToDefaultMsg(code sdk.CodeType) string {
|
||||
switch code {
|
||||
case CodeInvalidSequence:
|
||||
return "Invalid IBC packet sequence"
|
||||
case CodeIdenticalChains:
|
||||
return "Source and destination chain cannot be identical"
|
||||
default:
|
||||
return sdk.CodeToDefaultMsg(code)
|
||||
}
|
||||
}
|
||||
|
||||
func ErrInvalidSequence() sdk.Error {
|
||||
return newError(CodeInvalidSequence, "")
|
||||
}
|
||||
|
||||
func ErrIdenticalChains() sdk.Error {
|
||||
return newError(CodeIdenticalChains, "")
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Helpers
|
||||
|
||||
func newError(code sdk.CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(code, msg)
|
||||
}
|
||||
|
||||
func msgOrDefaultMsg(msg string, code sdk.CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
} else {
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
func NewHandler(ibcm IBCMapper, ck bank.CoinKeeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case IBCTransferMsg:
|
||||
return handleIBCTransferMsg(ctx, ibcm, ck, msg)
|
||||
case IBCReceiveMsg:
|
||||
return handleIBCReceiveMsg(ctx, ibcm, ck, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized IBC Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IBCTransferMsg deducts coins from the account and creates an egress IBC packet.
|
||||
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()
|
||||
}
|
||||
|
||||
err = ibcm.PostIBCPacket(ctx, packet)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// IBCReceiveMsg adds coins to the destination address and creates an ingress IBC packet.
|
||||
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()
|
||||
}
|
||||
|
||||
_, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
ibcm.SetIngressSequence(ctx, packet.SrcChain, seq+1)
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/go-crypto"
|
||||
oldwire "github.com/tendermint/go-wire"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
|
||||
)
|
||||
|
||||
// AccountMapper(/CoinKeeper) and IBCMapper should use different StoreKey later
|
||||
|
||||
func defaultContext(key sdk.StoreKey) sdk.Context {
|
||||
db := dbm.NewMemDB()
|
||||
cms := store.NewCommitMultiStore(db)
|
||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
||||
cms.LoadLatestVersion()
|
||||
ctx := sdk.NewContext(cms, abci.Header{}, false, nil)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func newAddress() crypto.Address {
|
||||
return crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
}
|
||||
|
||||
func getCoins(ck bank.CoinKeeper, ctx sdk.Context, addr crypto.Address) (sdk.Coins, sdk.Error) {
|
||||
zero := sdk.Coins{}
|
||||
return ck.AddCoins(ctx, addr, zero)
|
||||
}
|
||||
|
||||
// custom tx codec
|
||||
// TODO: use new go-wire
|
||||
func makeCodec() *wire.Codec {
|
||||
|
||||
const msgTypeSend = 0x1
|
||||
const msgTypeIssue = 0x2
|
||||
const msgTypeQuiz = 0x3
|
||||
const msgTypeSetTrend = 0x4
|
||||
const msgTypeIBCTransferMsg = 0x5
|
||||
const msgTypeIBCReceiveMsg = 0x6
|
||||
var _ = oldwire.RegisterInterface(
|
||||
struct{ sdk.Msg }{},
|
||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
||||
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
||||
oldwire.ConcreteType{IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
||||
oldwire.ConcreteType{IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
||||
)
|
||||
|
||||
const accTypeApp = 0x1
|
||||
var _ = oldwire.RegisterInterface(
|
||||
struct{ sdk.Account }{},
|
||||
oldwire.ConcreteType{&auth.BaseAccount{}, accTypeApp},
|
||||
)
|
||||
cdc := wire.NewCodec()
|
||||
|
||||
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
|
||||
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
|
||||
// ibc.RegisterWire(cdc) // Register ibc.[IBCTransferMsg, IBCReceiveMsg] types.
|
||||
return cdc
|
||||
}
|
||||
|
||||
func TestIBC(t *testing.T) {
|
||||
cdc := makeCodec()
|
||||
|
||||
key := sdk.NewKVStoreKey("ibc")
|
||||
ctx := defaultContext(key)
|
||||
|
||||
am := auth.NewAccountMapper(key, &auth.BaseAccount{})
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
|
||||
src := newAddress()
|
||||
dest := newAddress()
|
||||
chainid := "ibcchain"
|
||||
zero := sdk.Coins{}
|
||||
mycoins := sdk.Coins{sdk.Coin{"mycoin", 10}}
|
||||
|
||||
coins, err := ck.AddCoins(ctx, src, mycoins)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, mycoins, coins)
|
||||
|
||||
ibcm := NewIBCMapper(cdc, key)
|
||||
h := NewHandler(ibcm, ck)
|
||||
packet := IBCPacket{
|
||||
SrcAddr: src,
|
||||
DestAddr: dest,
|
||||
Coins: mycoins,
|
||||
SrcChain: chainid,
|
||||
DestChain: chainid,
|
||||
}
|
||||
|
||||
store := ctx.KVStore(key)
|
||||
|
||||
var msg sdk.Msg
|
||||
var res sdk.Result
|
||||
var egl int64
|
||||
var igs int64
|
||||
|
||||
egl = ibcm.getEgressLength(store, chainid)
|
||||
assert.Equal(t, egl, int64(0))
|
||||
|
||||
msg = IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
res = h(ctx, msg)
|
||||
assert.True(t, res.IsOK())
|
||||
|
||||
coins, err = getCoins(ck, ctx, src)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, zero, coins)
|
||||
|
||||
egl = ibcm.getEgressLength(store, chainid)
|
||||
assert.Equal(t, egl, int64(1))
|
||||
|
||||
igs = ibcm.GetIngressSequence(ctx, chainid)
|
||||
assert.Equal(t, igs, int64(0))
|
||||
|
||||
msg = IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: src,
|
||||
Sequence: 0,
|
||||
}
|
||||
res = h(ctx, msg)
|
||||
assert.True(t, res.IsOK())
|
||||
|
||||
coins, err = getCoins(ck, ctx, dest)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, mycoins, coins)
|
||||
|
||||
igs = ibcm.GetIngressSequence(ctx, chainid)
|
||||
assert.Equal(t, igs, int64(1))
|
||||
|
||||
res = h(ctx, msg)
|
||||
assert.False(t, res.IsOK())
|
||||
|
||||
igs = ibcm.GetIngressSequence(ctx, chainid)
|
||||
assert.Equal(t, igs, int64(1))
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
type IBCMapper struct {
|
||||
key sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
}
|
||||
|
||||
// XXX: The IBCMapper should not take a CoinKeeper. Rather have the CoinKeeper
|
||||
// take an IBCMapper.
|
||||
func NewIBCMapper(cdc *wire.Codec, key sdk.StoreKey) IBCMapper {
|
||||
// XXX: How are these codecs supposed to work?
|
||||
return IBCMapper{
|
||||
key: key,
|
||||
cdc: cdc,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// Functions for accessing the underlying KVStore.
|
||||
|
||||
func marshalBinaryPanic(cdc *wire.Codec, value interface{}) []byte {
|
||||
res, err := cdc.MarshalBinary(value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func unmarshalBinaryPanic(cdc *wire.Codec, bz []byte, ptr interface{}) {
|
||||
err := cdc.UnmarshalBinary(bz, ptr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
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 := marshalBinaryPanic(ibcm.cdc, int64(0))
|
||||
store.Set(key, zero)
|
||||
return 0
|
||||
}
|
||||
|
||||
var res int64
|
||||
unmarshalBinaryPanic(ibcm.cdc, bz, &res)
|
||||
return res
|
||||
}
|
||||
|
||||
func (ibcm IBCMapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence int64) {
|
||||
store := ctx.KVStore(ibcm.key)
|
||||
key := IngressSequenceKey(srcChain)
|
||||
|
||||
bz := marshalBinaryPanic(ibcm.cdc, sequence)
|
||||
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 {
|
||||
zero := marshalBinaryPanic(ibcm.cdc, int64(0))
|
||||
store.Set(EgressLengthKey(destChain), zero)
|
||||
return 0
|
||||
}
|
||||
var res int64
|
||||
unmarshalBinaryPanic(ibcm.cdc, bz, &res)
|
||||
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))
|
||||
}
|
||||
|
||||
// Stores the number of outgoing IBC packets under "egress/index".
|
||||
func EgressLengthKey(destChain string) []byte {
|
||||
return []byte(fmt.Sprintf("egress/%s", destChain))
|
||||
}
|
||||
|
||||
// Stores the sequence number of incoming IBC packet under "ingress/index".
|
||||
func IngressSequenceKey(srcChain string) []byte {
|
||||
return []byte(fmt.Sprintf("ingress/%s", srcChain))
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// ------------------------------
|
||||
// IBCPacket
|
||||
|
||||
// IBCPacket defines a piece of data that can be send between two separate
|
||||
// blockchains.
|
||||
type IBCPacket struct {
|
||||
SrcAddr sdk.Address
|
||||
DestAddr sdk.Address
|
||||
Coins sdk.Coins
|
||||
SrcChain string
|
||||
DestChain string
|
||||
}
|
||||
|
||||
func NewIBCPacket(srcAddr sdk.Address, destAddr sdk.Address, coins sdk.Coins,
|
||||
srcChain string, destChain string) IBCPacket {
|
||||
|
||||
return IBCPacket{
|
||||
SrcAddr: srcAddr,
|
||||
DestAddr: destAddr,
|
||||
Coins: coins,
|
||||
SrcChain: srcChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
}
|
||||
|
||||
func (ibcp IBCPacket) ValidateBasic() sdk.Error {
|
||||
if ibcp.SrcChain == ibcp.DestChain {
|
||||
return ErrIdenticalChains().Trace("")
|
||||
}
|
||||
if !ibcp.Coins.IsValid() {
|
||||
return sdk.ErrInvalidCoins("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// IBCTransferMsg
|
||||
|
||||
// IBCTransferMsg defines how another module can send an IBCPacket.
|
||||
type IBCTransferMsg struct {
|
||||
IBCPacket
|
||||
}
|
||||
|
||||
func (msg IBCTransferMsg) Type() string {
|
||||
return "ibc"
|
||||
}
|
||||
|
||||
func (msg IBCTransferMsg) Get(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg IBCTransferMsg) GetSignBytes() []byte {
|
||||
cdc := wire.NewCodec()
|
||||
bz, err := cdc.MarshalBinary(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
}
|
||||
|
||||
func (msg IBCTransferMsg) ValidateBasic() sdk.Error {
|
||||
return msg.IBCPacket.ValidateBasic()
|
||||
}
|
||||
|
||||
// x/bank/tx.go SendMsg.GetSigners()
|
||||
func (msg IBCTransferMsg) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.SrcAddr}
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// IBCReceiveMsg
|
||||
|
||||
// IBCReceiveMsg defines the message that a relayer uses to post an IBCPacket
|
||||
// to the destination chain.
|
||||
type IBCReceiveMsg struct {
|
||||
IBCPacket
|
||||
Relayer sdk.Address
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
func (msg IBCReceiveMsg) Type() string {
|
||||
return "ibc"
|
||||
}
|
||||
|
||||
func (msg IBCReceiveMsg) Get(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg IBCReceiveMsg) GetSignBytes() []byte {
|
||||
cdc := wire.NewCodec()
|
||||
bz, err := cdc.MarshalBinary(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
}
|
||||
|
||||
func (msg IBCReceiveMsg) ValidateBasic() sdk.Error {
|
||||
return msg.IBCPacket.ValidateBasic()
|
||||
}
|
||||
|
||||
// x/bank/tx.go SendMsg.GetSigners()
|
||||
func (msg IBCReceiveMsg) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.Relayer}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// --------------------------------
|
||||
// IBCPacket Tests
|
||||
|
||||
func TestIBCPacketValidation(t *testing.T) {
|
||||
cases := []struct {
|
||||
valid bool
|
||||
packet IBCPacket
|
||||
}{
|
||||
{true, constructIBCPacket(true)},
|
||||
{false, constructIBCPacket(false)},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
err := tc.packet.ValidateBasic()
|
||||
if tc.valid {
|
||||
assert.Nil(t, err, "%d: %+v", i, err)
|
||||
} else {
|
||||
assert.NotNil(t, err, "%d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// IBCTransferMsg Tests
|
||||
|
||||
func TestIBCTransferMsg(t *testing.T) {
|
||||
packet := constructIBCPacket(true)
|
||||
msg := IBCTransferMsg{packet}
|
||||
|
||||
assert.Equal(t, msg.Type(), "ibc")
|
||||
}
|
||||
|
||||
func TestIBCTransferMsgValidation(t *testing.T) {
|
||||
validPacket := constructIBCPacket(true)
|
||||
invalidPacket := constructIBCPacket(false)
|
||||
|
||||
cases := []struct {
|
||||
valid bool
|
||||
msg IBCTransferMsg
|
||||
}{
|
||||
{true, IBCTransferMsg{validPacket}},
|
||||
{false, IBCTransferMsg{invalidPacket}},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
err := tc.msg.ValidateBasic()
|
||||
if tc.valid {
|
||||
assert.Nil(t, err, "%d: %+v", i, err)
|
||||
} else {
|
||||
assert.NotNil(t, err, "%d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// IBCReceiveMsg Tests
|
||||
|
||||
func TestIBCReceiveMsg(t *testing.T) {
|
||||
packet := constructIBCPacket(true)
|
||||
msg := IBCReceiveMsg{packet, sdk.Address([]byte("relayer")), 0}
|
||||
|
||||
assert.Equal(t, msg.Type(), "ibc")
|
||||
}
|
||||
|
||||
func TestIBCReceiveMsgValidation(t *testing.T) {
|
||||
validPacket := constructIBCPacket(true)
|
||||
invalidPacket := constructIBCPacket(false)
|
||||
|
||||
cases := []struct {
|
||||
valid bool
|
||||
msg IBCReceiveMsg
|
||||
}{
|
||||
{true, IBCReceiveMsg{validPacket, sdk.Address([]byte("relayer")), 0}},
|
||||
{false, IBCReceiveMsg{invalidPacket, sdk.Address([]byte("relayer")), 0}},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
err := tc.msg.ValidateBasic()
|
||||
if tc.valid {
|
||||
assert.Nil(t, err, "%d: %+v", i, err)
|
||||
} else {
|
||||
assert.NotNil(t, err, "%d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Helpers
|
||||
|
||||
func constructIBCPacket(valid bool) IBCPacket {
|
||||
srcAddr := sdk.Address([]byte("source"))
|
||||
destAddr := sdk.Address([]byte("destination"))
|
||||
coins := sdk.Coins{{"atom", 10}}
|
||||
srcChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
if valid {
|
||||
return NewIBCPacket(srcAddr, destAddr, coins, srcChain, destChain)
|
||||
} else {
|
||||
return NewIBCPacket(srcAddr, destAddr, coins, srcChain, srcChain)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Register concrete types on wire codec
|
||||
func RegisterWire(cdc *wire.Codec) {
|
||||
//cdc.RegisterConcrete(IBCTransferMsg{}, "github.com/cosmos/cosmos-sdk/x/ibc/IBCTransferMsg", nil)
|
||||
//cdc.RegisterConcrete(IBCReceiveMsg{}, "github.com/cosmos/cosmos-sdk/x/ibc/IBCReceiveMsg", nil)
|
||||
}
|
Loading…
Reference in New Issue