diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go index 02335e390..3a48febe2 100644 --- a/cmd/basecli/main.go +++ b/cmd/basecli/main.go @@ -19,6 +19,7 @@ import ( basecmd "github.com/tendermint/basecoin/modules/base/commands" coincmd "github.com/tendermint/basecoin/modules/coin/commands" feecmd "github.com/tendermint/basecoin/modules/fee/commands" + ibccmd "github.com/tendermint/basecoin/modules/ibc/commands" noncecmd "github.com/tendermint/basecoin/modules/nonce/commands" rolecmd "github.com/tendermint/basecoin/modules/roles/commands" ) @@ -46,6 +47,7 @@ func main() { coincmd.AccountQueryCmd, noncecmd.NonceQueryCmd, rolecmd.RoleQueryCmd, + ibccmd.IBCQueryCmd, ) proofs.TxPresenters.Register("base", txcmd.BaseTxPresenter{}) diff --git a/modules/coin/ibc_test.go b/modules/coin/ibc_test.go index 13908cfc2..24856ef6f 100644 --- a/modules/coin/ibc_test.go +++ b/modules/coin/ibc_test.go @@ -12,6 +12,10 @@ import ( "github.com/tendermint/basecoin/stack" ) +// TODO: other test making sure tx is output on send, balance is updated + +// This makes sure we respond properly to posttx +// TODO: set credit limit func TestIBCPostPacket(t *testing.T) { assert := assert.New(t) require := require.New(t) diff --git a/modules/ibc/commands/query.go b/modules/ibc/commands/query.go new file mode 100644 index 000000000..38514d4c2 --- /dev/null +++ b/modules/ibc/commands/query.go @@ -0,0 +1,185 @@ +package commands + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/basecoin/client/commands" + proofcmd "github.com/tendermint/basecoin/client/commands/proofs" + "github.com/tendermint/basecoin/modules/ibc" + "github.com/tendermint/basecoin/stack" +) + +// IBCQueryCmd - parent command to query ibc info +var IBCQueryCmd = &cobra.Command{ + Use: "ibc", + Short: "Get information about IBC", + RunE: commands.RequireInit(ibcQueryCmd), + // HandlerInfo +} + +// ChainsQueryCmd - get a list of all registered chains +var ChainsQueryCmd = &cobra.Command{ + Use: "chains", + Short: "Get a list of all registered chains", + RunE: commands.RequireInit(chainsQueryCmd), + // ChainSet ([]string) +} + +// ChainQueryCmd - get details on one registered chain +var ChainQueryCmd = &cobra.Command{ + Use: "chain [id]", + Short: "Get details on one registered chain", + RunE: commands.RequireInit(chainQueryCmd), + // ChainInfo +} + +// PacketsQueryCmd - get latest packet in a queue +var PacketsQueryCmd = &cobra.Command{ + Use: "packets", + Short: "Get latest packet in a queue", + RunE: commands.RequireInit(packetsQueryCmd), + // uint64 +} + +// PacketQueryCmd - get the names packet (by queue and sequence) +var PacketQueryCmd = &cobra.Command{ + Use: "packet", + Short: "Get packet with given sequence from the named queue", + RunE: commands.RequireInit(packetQueryCmd), + // Packet +} + +//nolint +const ( + FlagFromChain = "from" + FlagToChain = "to" + FlagSequence = "sequence" +) + +func init() { + IBCQueryCmd.AddCommand( + ChainQueryCmd, + ChainsQueryCmd, + PacketQueryCmd, + PacketsQueryCmd, + ) + + fs1 := PacketsQueryCmd.Flags() + fs1.String(FlagFromChain, "", "Name of the input chain (where packets came from)") + fs1.String(FlagToChain, "", "Name of the output chain (where packets go to)") + + fs2 := PacketQueryCmd.Flags() + fs2.String(FlagFromChain, "", "Name of the input chain (where packets came from)") + fs2.String(FlagToChain, "", "Name of the output chain (where packets go to)") + fs2.Int(FlagSequence, -1, "Name of the output chain (where packets go to)") +} + +func ibcQueryCmd(cmd *cobra.Command, args []string) error { + var res ibc.HandlerInfo + key := stack.PrefixedKey(ibc.NameIBC, ibc.HandlerKey()) + proof, err := proofcmd.GetAndParseAppProof(key, &res) + if err != nil { + return err + } + return proofcmd.OutputProof(res, proof.BlockHeight()) +} + +func chainsQueryCmd(cmd *cobra.Command, args []string) error { + list := [][]byte{} + key := stack.PrefixedKey(ibc.NameIBC, ibc.HandlerKey()) + proof, err := proofcmd.GetAndParseAppProof(key, &list) + if err != nil { + return err + } + + // convert these names to strings for better output + res := make([]string, len(list)) + for i := range list { + res[i] = string(list[i]) + } + + return proofcmd.OutputProof(res, proof.BlockHeight()) +} + +func chainQueryCmd(cmd *cobra.Command, args []string) error { + arg, err := commands.GetOneArg(args, "id") + if err != nil { + return err + } + + var res ibc.ChainInfo + key := stack.PrefixedKey(ibc.NameIBC, ibc.ChainKey(arg)) + proof, err := proofcmd.GetAndParseAppProof(key, &res) + if err != nil { + return err + } + + return proofcmd.OutputProof(res, proof.BlockHeight()) +} + +func assertOne(from, to string) error { + if from == "" && to == "" { + return errors.Errorf("You must specify either --%s or --%s", + FlagFromChain, FlagToChain) + } + if from != "" && to != "" { + return errors.Errorf("You can only specify one of --%s or --%s", + FlagFromChain, FlagToChain) + } + return nil +} + +func packetsQueryCmd(cmd *cobra.Command, args []string) error { + from := viper.GetString(FlagFromChain) + to := viper.GetString(FlagToChain) + err := assertOne(from, to) + if err != nil { + return err + } + + var key []byte + if from != "" { + key = stack.PrefixedKey(ibc.NameIBC, ibc.QueueInKey(from)) + } else { + key = stack.PrefixedKey(ibc.NameIBC, ibc.QueueOutKey(to)) + } + + var res uint64 + proof, err := proofcmd.GetAndParseAppProof(key, &res) + if err != nil { + return err + } + + return proofcmd.OutputProof(res, proof.BlockHeight()) +} + +func packetQueryCmd(cmd *cobra.Command, args []string) error { + from := viper.GetString(FlagFromChain) + to := viper.GetString(FlagToChain) + err := assertOne(from, to) + if err != nil { + return err + } + + seq := viper.GetInt(FlagSequence) + if seq < 0 { + return errors.Errorf("--%s must be a non-negative number", FlagSequence) + } + + var key []byte + if from != "" { + key = stack.PrefixedKey(ibc.NameIBC, ibc.QueueInPacketKey(from, uint64(seq))) + } else { + key = stack.PrefixedKey(ibc.NameIBC, ibc.QueueOutPacketKey(to, uint64(seq))) + } + + var res ibc.Packet + proof, err := proofcmd.GetAndParseAppProof(key, &res) + if err != nil { + return err + } + + return proofcmd.OutputProof(res, proof.BlockHeight()) +} diff --git a/modules/ibc/keys.go b/modules/ibc/keys.go new file mode 100644 index 000000000..e520d1888 --- /dev/null +++ b/modules/ibc/keys.go @@ -0,0 +1,60 @@ +package ibc + +import ( + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/state" +) + +const ( + // this is the prefix for the list of chains + // we otherwise use the chainid as prefix, so this must not be an + // alpha-numeric byte + prefixChains = "**" + + prefixInput = "i" + prefixOutput = "o" +) + +// HandlerKey is used for the global permission info +func HandlerKey() []byte { + return []byte{0x2} +} + +// ChainsKey is the key to get info on all chains +func ChainsKey() []byte { + return stack.PrefixedKey(prefixChains, state.SetKey()) +} + +// ChainKey is the key to get info on one chain +func ChainKey(chainID string) []byte { + bkey := state.MakeBKey([]byte(chainID)) + return stack.PrefixedKey(prefixChains, bkey) +} + +// QueueInKey is the key to get newest of the input queue from this chain +func QueueInKey(chainID string) []byte { + return stack.PrefixedKey(chainID, + stack.PrefixedKey(prefixInput, + state.QueueTailKey())) +} + +// QueueOutKey is the key to get v of the output queue from this chain +func QueueOutKey(chainID string) []byte { + return stack.PrefixedKey(chainID, + stack.PrefixedKey(prefixOutput, + state.QueueTailKey())) +} + +// QueueInPacketKey is the key to get given packet from this chain's input queue +func QueueInPacketKey(chainID string, seq uint64) []byte { + return stack.PrefixedKey(chainID, + stack.PrefixedKey(prefixInput, + state.QueueItemKey(seq))) +} + +// QueueOutPacketKey is the key to get given packet from this chain's output queue +func QueueOutPacketKey(chainID string, seq uint64) []byte { + return stack.PrefixedKey(chainID, + stack.PrefixedKey(prefixOutput, + state.QueueItemKey(seq))) +} diff --git a/modules/ibc/store.go b/modules/ibc/store.go index fc85416aa..17506ad65 100644 --- a/modules/ibc/store.go +++ b/modules/ibc/store.go @@ -7,21 +7,6 @@ import ( wire "github.com/tendermint/go-wire" ) -const ( - // this is the prefix for the list of chains - // we otherwise use the chainid as prefix, so this must not be an - // alpha-numeric byte - prefixChains = "**" - - prefixInput = "i" - prefixOutput = "o" -) - -// this is used for the global handler info -var ( - handlerKey = []byte{0x2} -) - // HandlerInfo is the global state of the ibc.Handler type HandlerInfo struct { Registrar basecoin.Actor `json:"registrar"` @@ -30,12 +15,12 @@ type HandlerInfo struct { // Save the HandlerInfo to the store func (h HandlerInfo) Save(store state.KVStore) { b := wire.BinaryBytes(h) - store.Set(handlerKey, b) + store.Set(HandlerKey(), b) } // LoadInfo loads the HandlerInfo from the data store func LoadInfo(store state.KVStore) (h HandlerInfo) { - b := store.Get(handlerKey) + b := store.Get(HandlerKey()) if len(b) > 0 { wire.ReadBinaryBytes(b, &h) } diff --git a/modules/roles/commands/query.go b/modules/roles/commands/query.go index d7c97a8e2..6a333f211 100644 --- a/modules/roles/commands/query.go +++ b/modules/roles/commands/query.go @@ -1,10 +1,9 @@ package commands import ( - "github.com/pkg/errors" "github.com/spf13/cobra" - lcmd "github.com/tendermint/basecoin/client/commands" + "github.com/tendermint/basecoin/client/commands" proofcmd "github.com/tendermint/basecoin/client/commands/proofs" "github.com/tendermint/basecoin/modules/roles" "github.com/tendermint/basecoin/stack" @@ -14,17 +13,15 @@ import ( var RoleQueryCmd = &cobra.Command{ Use: "role [name]", Short: "Get details of a role, with proof", - RunE: lcmd.RequireInit(roleQueryCmd), + RunE: commands.RequireInit(roleQueryCmd), } func roleQueryCmd(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.New("Missing required argument [name]") - } else if len(args) > 1 { - return errors.New("Command only supports one name") + arg, err := commands.GetOneArg(args, "name") + if err != nil { + return err } - - role, err := parseRole(args[0]) + role, err := parseRole(arg) if err != nil { return err } diff --git a/state/queue.go b/state/queue.go index 9460cf1d1..c1b10440c 100644 --- a/state/queue.go +++ b/state/queue.go @@ -8,6 +8,21 @@ var ( dataKey = []byte("d") ) +// QueueHeadKey gives us the key for the height at head of the queue +func QueueHeadKey() []byte { + return headKey +} + +// QueueTailKey gives us the key for the height at tail of the queue +func QueueTailKey() []byte { + return tailKey +} + +// QueueItemKey gives us the key to look up one item by sequence +func QueueItemKey(i uint64) []byte { + return makeKey(i) +} + // Queue allows us to fill up a range of the db, and grab from either end type Queue struct { store KVStore diff --git a/state/set.go b/state/set.go index 98d6a4095..ea3a0ca24 100644 --- a/state/set.go +++ b/state/set.go @@ -7,6 +7,11 @@ import ( wire "github.com/tendermint/go-wire" ) +// SetKey returns the key to get all members of this set +func SetKey() []byte { + return keys +} + // Set allows us to add arbitrary k-v pairs, check existence, // as well as iterate through the set (always in key order) // @@ -29,7 +34,7 @@ func NewSet(store KVStore) *Set { // Set puts a value at a given height. // If the value is nil, or an empty slice, remove the key from the list func (s *Set) Set(key []byte, value []byte) { - s.store.Set(makeBKey(key), value) + s.store.Set(MakeBKey(key), value) if len(value) > 0 { s.addKey(key) } else { @@ -40,7 +45,7 @@ func (s *Set) Set(key []byte, value []byte) { // Get returns the element with a key if it exists func (s *Set) Get(key []byte) []byte { - return s.store.Get(makeBKey(key)) + return s.store.Get(MakeBKey(key)) } // Remove deletes this key from the set (same as setting value = nil) @@ -122,8 +127,8 @@ func (s *Set) storeKeys() { s.store.Set(keys, b) } -// makeBKey prefixes the byte slice for the storage key -func makeBKey(key []byte) []byte { +// MakeBKey prefixes the byte slice for the storage key +func MakeBKey(key []byte) []byte { return append(dataKey, key...) }