diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go index 934b3df2b2..6813dd9163 100644 --- a/cmd/basecli/main.go +++ b/cmd/basecli/main.go @@ -71,6 +71,7 @@ func main() { // these are for handling ibc ibccmd.RegisterChainTxCmd, ibccmd.UpdateChainTxCmd, + ibccmd.PostPacketTxCmd, ) // Set up the various commands to use diff --git a/modules/ibc/commands/query.go b/modules/ibc/commands/query.go index 67e9430c7b..80bb6c752e 100644 --- a/modules/ibc/commands/query.go +++ b/modules/ibc/commands/query.go @@ -1,6 +1,8 @@ package commands import ( + "fmt" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -9,6 +11,9 @@ import ( proofcmd "github.com/tendermint/basecoin/client/commands/proofs" "github.com/tendermint/basecoin/modules/ibc" "github.com/tendermint/basecoin/stack" + "github.com/tendermint/go-wire/data" + "github.com/tendermint/light-client/proofs" + "github.com/tendermint/merkleeyes/iavl" ) // IBCQueryCmd - parent command to query ibc info @@ -175,11 +180,45 @@ func packetQueryCmd(cmd *cobra.Command, args []string) error { key = stack.PrefixedKey(ibc.NameIBC, ibc.QueueOutPacketKey(to, uint64(seq))) } - var res ibc.Packet - proof, err := proofcmd.GetAndParseAppProof(key, &res) + // Input queue just display the results + if from != "" { + var packet ibc.Packet + proof, err := proofcmd.GetAndParseAppProof(key, &packet) + if err != nil { + return err + } + return proofcmd.OutputProof(packet, proof.BlockHeight()) + } + + // output queue, create a post packet + var packet ibc.Packet + proof, err := proofcmd.GetAndParseAppProof(key, &packet) if err != nil { return err } - return proofcmd.OutputProof(res, proof.BlockHeight()) + // TODO: oh so ugly. fix before merge! + // wait, i want to change go-merkle too.... + appProof := proof.(proofs.AppProof) + extractedProof, err := iavl.ReadProof(appProof.Proof) + if err != nil { + return err + } + + // create the post packet here. + post := ibc.PostPacketTx{ + FromChainID: commands.GetChainID(), + FromChainHeight: proof.BlockHeight(), + Key: key, + Packet: packet, + Proof: extractedProof, + } + + // print json direct, as we don't need to wrap with the height + res, err := data.ToJSON(post) + if err != nil { + return err + } + fmt.Println(string(res)) + return nil } diff --git a/modules/ibc/commands/tx.go b/modules/ibc/commands/tx.go index 608082bb1a..475d35809b 100644 --- a/modules/ibc/commands/tx.go +++ b/modules/ibc/commands/tx.go @@ -28,13 +28,19 @@ var UpdateChainTxCmd = &cobra.Command{ RunE: commands.RequireInit(updateChainTxCmd), } -// TODO: post packet (query and all that jazz) +// PostPacketTxCmd is CLI command to post ibc packet on the destination chain +var PostPacketTxCmd = &cobra.Command{ + Use: "ibc-post", + Short: "Post an ibc packet on the destination chain", + RunE: commands.RequireInit(postPacketTxCmd), +} // TODO: relay! //nolint const ( - FlagSeed = "seed" + FlagSeed = "seed" + FlagPacket = "packet" ) func init() { @@ -43,6 +49,9 @@ func init() { fs2 := UpdateChainTxCmd.Flags() fs2.String(FlagSeed, "", "Filename with a seed file") + + fs3 := PostPacketTxCmd.Flags() + fs3.String(FlagPacket, "", "Filename with a packet to post") } func registerChainTxCmd(cmd *cobra.Command, args []string) error { @@ -63,45 +72,44 @@ func updateChainTxCmd(cmd *cobra.Command, args []string) error { return txcmd.DoTx(tx) } +func postPacketTxCmd(cmd *cobra.Command, args []string) error { + post, err := readPostPacket() + if err != nil { + return err + } + return txcmd.DoTx(post.Wrap()) +} + func readSeed() (seed certifiers.Seed, err error) { name := viper.GetString(FlagSeed) if name == "" { return seed, errors.New("You must specify a seed file") } + err = readFile(name, &seed) + return +} + +func readPostPacket() (post ibc.PostPacketTx, err error) { + name := viper.GetString(FlagPacket) + if name == "" { + return post, errors.New("You must specify a packet file") + } + + err = readFile(name, &post) + return +} + +func readFile(name string, input interface{}) (err error) { var f *os.File f, err = os.Open(name) if err != nil { - return seed, errors.Wrap(err, "Cannot read seed file") + return errors.WithStack(err) } defer f.Close() // read the file as json into a seed j := json.NewDecoder(f) - err = j.Decode(&seed) - err = errors.Wrap(err, "Invalid seed file") - return + err = j.Decode(input) + return errors.Wrap(err, "Invalid file") } - -// func readCreateRoleTxFlags() (tx basecoin.Tx, err error) { -// role, err := parseRole(viper.GetString(FlagRole)) -// if err != nil { -// return tx, err -// } - -// sigs := viper.GetInt(FlagMinSigs) -// if sigs < 1 { -// return tx, errors.Errorf("--%s must be at least 1", FlagMinSigs) -// } - -// signers, err := commands.ParseActors(viper.GetString(FlagMembers)) -// if err != nil { -// return tx, err -// } -// if len(signers) == 0 { -// return tx, errors.New("must specify at least one member") -// } - -// tx = roles.NewCreateRoleTx(role, uint32(sigs), signers) -// return tx, nil -// } diff --git a/modules/ibc/ibc_test.go b/modules/ibc/ibc_test.go index 0d830f5343..c31aeae53a 100644 --- a/modules/ibc/ibc_test.go +++ b/modules/ibc/ibc_test.go @@ -381,29 +381,26 @@ func TestIBCPostPacket(t *testing.T) { // bad chain -> error {packetBad, ibcPerm, IsNotRegisteredErr}, - // invalid permissions -> error - {packet0, nil, IsNeedsIBCPermissionErr}, - // no matching header -> error - {packet0badHeight, ibcPerm, IsHeaderNotFoundErr}, + {packet0badHeight, nil, IsHeaderNotFoundErr}, // out of order -> error {packet1, ibcPerm, IsPacketOutOfOrderErr}, - // all good -> execute tx } + // all good -> execute tx {packet0, ibcPerm, errors.NoErr}, // bad proof -> error {packet1badProof, ibcPerm, IsInvalidProofErr}, - // all good -> execute tx } - {packet1, ibcPerm, errors.NoErr}, + // all good -> execute tx (no special permission needed) + {packet1, nil, errors.NoErr}, // repeat -> error - {packet0, ibcPerm, IsPacketAlreadyExistsErr}, + {packet0, nil, IsPacketAlreadyExistsErr}, // packet2 contains invalid permissions - {packet2, ibcPerm, IsCannotSetPermissionErr}, + {packet2, nil, IsCannotSetPermissionErr}, } for i, tc := range cases { diff --git a/modules/ibc/middleware.go b/modules/ibc/middleware.go index 7852bf1e57..fe6176964a 100644 --- a/modules/ibc/middleware.go +++ b/modules/ibc/middleware.go @@ -80,15 +80,6 @@ func (m Middleware) verifyPost(ctx basecoin.Context, store state.SimpleDB, return ictx, itx, ErrCannotSetPermission() } - // make sure it has AllowIBC - mod, err := packet.Tx.GetMod() - if err != nil { - return ictx, itx, err - } - if !ctx.HasPermission(AllowIBC(mod)) { - return ictx, itx, ErrNeedsIBCPermission() - } - // make sure this sequence number is the next in the list q := InputQueue(store, from) tail := q.Tail() diff --git a/modules/ibc/tx.go b/modules/ibc/tx.go index 48a09531aa..8ee4d934c4 100644 --- a/modules/ibc/tx.go +++ b/modules/ibc/tx.go @@ -1,6 +1,7 @@ package ibc import ( + "github.com/tendermint/go-wire/data" "github.com/tendermint/light-client/certifiers" merkle "github.com/tendermint/merkleeyes/iavl" @@ -109,13 +110,14 @@ func (p CreatePacketTx) Wrap() basecoin.Tx { // right now, enforce that these packets are only sent directly, // not routed over the hub. add routing later. type PostPacketTx struct { - // make sure we have this header... - FromChainID string // The immediate source of the packet, not always Packet.SrcChainID - FromChainHeight uint64 // The block height in which Packet was committed, to check Proof + // The immediate source of the packet, not always Packet.SrcChainID + FromChainID string `json:"src_chain"` + // The block height in which Packet was committed, to check Proof + FromChainHeight uint64 `json:"src_height"` // this proof must match the header and the packet.Bytes() - Proof *merkle.IAVLProof - Key []byte - Packet Packet + Proof *merkle.IAVLProof `json:"proof"` + Key data.Bytes `json:"key"` + Packet Packet `json:"packet"` } // ValidateBasic makes sure this is consistent - used to satisfy TxInner diff --git a/stack/context.go b/stack/context.go index 9e3c247e8f..7359adbe18 100644 --- a/stack/context.go +++ b/stack/context.go @@ -21,8 +21,10 @@ type secureContext struct { // NewContext - create a new secureContext func NewContext(chain string, height uint64, logger log.Logger) basecoin.Context { + mock := MockContext(chain, height).(naiveContext) + mock.Logger = logger return secureContext{ - naiveContext: MockContext(chain, height).(naiveContext), + naiveContext: mock, } } diff --git a/tests/cli/ibc.sh b/tests/cli/ibc.sh index 8fcad28146..876d0675de 100755 --- a/tests/cli/ibc.sh +++ b/tests/cli/ibc.sh @@ -195,10 +195,11 @@ test04SendIBCPacket() { # and look at the packet itself PACKET=$(${CLIENT_EXE} query ibc packet --to=$CHAIN_ID_2 --sequence=0) assertTrue "line=${LINENO}, packet query" $? - echo $PACKET | jq . + assertEquals "line=${LINENO}, proper src" "\"$CHAIN_ID_1\"" $(echo $PACKET | jq .src_chain) + assertEquals "line=${LINENO}, proper dest" "\"$CHAIN_ID_2\"" $(echo $PACKET | jq .packet.dest_chain) + assertEquals "line=${LINENO}, proper sequence" "0" $(echo $PACKET | jq .packet.sequence) # nothing arrived - # look, we wrote a packet ARRIVED=$(${CLIENT_EXE} query ibc packets --from=$CHAIN_ID_1 --home=$CLIENT_2 2>/dev/null) assertFalse "line=${LINENO}, packet query" $? assertFalse "line=${LINENO}, no relay running" "BC_HOME=${CLIENT_2} ${CLIENT_EXE} query account $RECV" @@ -212,7 +213,35 @@ test05ReceiveIBCPacket() { txSucceeded $? "$TX" "${CHAIN_ID_2}::${RECV}" checkAccount $CHAIN_2:: "60006" - # now, we try to post it.... + # now, we try to post it.... (this is PACKET from last test) + + # get the seed and post it + SRC_HEIGHT=$(echo $PACKET | jq .src_height) + PACKET_SEED="$BASE_DIR_1/packet_seed.json" + ${CLIENT_EXE} seeds export $PACKET_SEED --home=${CLIENT_1} --height=$SRC_HEIGHT + assertTrue "line=${LINENO}, export seed failed" $? + + TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \ + --seed=${PACKET_SEED} --name=$POOR) + txSucceeded $? "$TX" "prepare packet chain1 on chain 2" + # an example to quit early if there is no point in more tests + if [ $? != 0 ]; then echo "aborting!"; return 1; fi + + # write the packet to the file + POST_PACKET="$BASE_DIR_1/post_packet.json" + echo $PACKET > $POST_PACKET + + # post it as a tx (cross-fingers) + TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-post \ + --packet=${POST_PACKET} --name=$POOR) + txSucceeded $? "$TX" "post packet from chain1 on chain 2" + + # TODO: more queries on stuff... + + # look, we wrote a packet + PACKETS=$(${CLIENT_EXE} query ibc packets --from=$CHAIN_ID_1) + assertTrue "line=${LINENO}, packets query" $? + assertEquals "line=${LINENO}, packet count" 1 $(echo $PACKETS | jq .data) }