diff --git a/README.md b/README.md index c6303458aa..b765165b61 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ This will create the `basecoin` binary in `$GOPATH/bin`. The basecoin CLI can be used to start a stand-alone basecoin instance (`basecoin start`), or to start basecoin with Tendermint in the same process (`basecoin start --in-proc`). -It can also be used to send transactions, eg. `basecoin tx send --to 0x4793A333846E5104C46DD9AB9A00E31821B2F301 --amount 100` +It can also be used to send transactions, eg. `basecoin tx send --to 0x4793A333846E5104C46DD9AB9A00E31821B2F301 --amount 100btc,10gold` See `basecoin --help` and `basecoin [cmd] --help` for more details`. ## Learn more diff --git a/cmd/commands/flags.go b/cmd/commands/flags.go index 00aae351cd..0c4c477707 100644 --- a/cmd/commands/flags.go +++ b/cmd/commands/flags.go @@ -48,10 +48,10 @@ var ( Usage: "Destination address for the transaction", } - AmountFlag = cli.IntFlag{ + AmountFlag = cli.StringFlag{ Name: "amount", - Value: 0, - Usage: "Amount of coins to send in the transaction", + Value: "", + Usage: "Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver)", } FromFlag = cli.StringFlag{ @@ -66,22 +66,16 @@ var ( Usage: "Sequence number for the account", } - CoinFlag = cli.StringFlag{ - Name: "coin", - Value: "mycoin", - Usage: "Specify a coin denomination", - } - GasFlag = cli.IntFlag{ Name: "gas", Value: 0, Usage: "The amount of gas for the transaction", } - FeeFlag = cli.IntFlag{ + FeeFlag = cli.StringFlag{ Name: "fee", - Value: 0, - Usage: "The transaction fee", + Value: "", + Usage: "Coins for the transaction fee of the format ", } DataFlag = cli.StringFlag{ diff --git a/cmd/commands/query.go b/cmd/commands/query.go index 41635e2882..97eff67277 100644 --- a/cmd/commands/query.go +++ b/cmd/commands/query.go @@ -66,6 +66,11 @@ var ( } ) +// Register a subcommand of QueryCmd for plugin specific query functionality +func RegisterQuerySubcommand(cmd cli.Command) { + QueryCmd.Subcommands = append(QueryCmd.Subcommands, cmd) +} + func cmdQuery(c *cli.Context) error { if len(c.Args()) != 1 { return errors.New("query command requires an argument ([key])") diff --git a/cmd/commands/tx.go b/cmd/commands/tx.go index 9b8ce21cd8..f54b6d0c27 100644 --- a/cmd/commands/tx.go +++ b/cmd/commands/tx.go @@ -23,7 +23,6 @@ var TxFlags = []cli.Flag{ FromFlag, AmountFlag, - CoinFlag, GasFlag, FeeFlag, SeqFlag, @@ -71,9 +70,9 @@ func RegisterTxSubcommand(cmd cli.Command) { func cmdSendTx(c *cli.Context) error { toHex := c.String("to") fromFile := c.String("from") - amount := int64(c.Int("amount")) - coin := c.String("coin") - gas, fee := c.Int("gas"), int64(c.Int("fee")) + amount := c.String("amount") + gas := int64(c.Int("gas")) + fee := c.String("fee") chainID := c.String("chain_id") // convert destination address to bytes @@ -91,12 +90,22 @@ func cmdSendTx(c *cli.Context) error { return err } + //parse the fee and amounts into coin types + feeCoin, err := ParseCoin(fee) + if err != nil { + return err + } + amountCoins, err := ParseCoins(amount) + if err != nil { + return err + } + // craft the tx - input := types.NewTxInput(privKey.PubKey, types.Coins{types.Coin{coin, amount}}, sequence) - output := newOutput(to, coin, amount) + input := types.NewTxInput(privKey.PubKey, amountCoins, sequence) + output := newOutput(to, amountCoins) tx := &types.SendTx{ - Gas: int64(gas), - Fee: types.Coin{coin, fee}, + Gas: gas, + Fee: feeCoin, Inputs: []types.TxInput{input}, Outputs: []types.TxOutput{output}, } @@ -128,9 +137,9 @@ func cmdAppTx(c *cli.Context) error { func AppTx(c *cli.Context, name string, data []byte) error { fromFile := c.String("from") - amount := int64(c.Int("amount")) - coin := c.String("coin") - gas, fee := c.Int("gas"), int64(c.Int("fee")) + amount := c.String("amount") + fee := c.String("fee") + gas := int64(c.Int("gas")) chainID := c.String("chain_id") privKey := tmtypes.LoadPrivValidator(fromFile) @@ -140,10 +149,20 @@ func AppTx(c *cli.Context, name string, data []byte) error { return err } - input := types.NewTxInput(privKey.PubKey, types.Coins{types.Coin{coin, amount}}, sequence) + //parse the fee and amounts into coin types + feeCoin, err := ParseCoin(fee) + if err != nil { + return err + } + amountCoins, err := ParseCoins(amount) + if err != nil { + return err + } + + input := types.NewTxInput(privKey.PubKey, amountCoins, sequence) tx := &types.AppTx{ - Gas: int64(gas), - Fee: types.Coin{coin, fee}, + Gas: gas, + Fee: feeCoin, Name: name, Input: input, Data: data, @@ -205,15 +224,10 @@ func getSeq(c *cli.Context, address []byte) (int, error) { return acc.Sequence + 1, nil } -func newOutput(to []byte, coin string, amount int64) types.TxOutput { +func newOutput(to []byte, amount types.Coins) types.TxOutput { return types.TxOutput{ Address: to, - Coins: types.Coins{ - types.Coin{ - Denom: coin, - Amount: amount, - }, - }, + Coins: amount, } } diff --git a/cmd/commands/utils.go b/cmd/commands/utils.go index 92a805734a..0cbd5c8d76 100644 --- a/cmd/commands/utils.go +++ b/cmd/commands/utils.go @@ -3,9 +3,13 @@ package commands import ( "encoding/hex" "errors" + "regexp" + "strconv" + "strings" "github.com/urfave/cli" + "github.com/tendermint/basecoin/state" "github.com/tendermint/basecoin/types" abci "github.com/tendermint/abci/types" @@ -35,6 +39,44 @@ func StripHex(s string) string { return s } +//regex codes for extracting coins from CLI input +var reDenom = regexp.MustCompile("([^\\d\\W]+)") +var reAmt = regexp.MustCompile("(\\d+)") + +func ParseCoin(str string) (types.Coin, error) { + + var coin types.Coin + + if len(str) > 0 { + amt, err := strconv.Atoi(reAmt.FindString(str)) + if err != nil { + return coin, err + } + denom := reDenom.FindString(str) + coin = types.Coin{denom, int64(amt)} + } + + return coin, nil +} + +func ParseCoins(str string) (types.Coins, error) { + + split := strings.Split(str, ",") + var coins []types.Coin + + for _, el := range split { + if len(el) > 0 { + coin, err := ParseCoin(el) + if err != nil { + return coins, err + } + coins = append(coins, coin) + } + } + + return coins, nil +} + func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) { clientURI := client.NewClientURI(tmAddr) tmResult := new(ctypes.TMResult) @@ -58,7 +100,7 @@ func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) { // fetch the account by querying the app func getAcc(tmAddr string, address []byte) (*types.Account, error) { - key := append([]byte("base/a/"), address...) + key := state.AccountKey(address) response, err := Query(tmAddr, key) if err != nil { return nil, err diff --git a/cmd/commands/utils_test.go b/cmd/commands/utils_test.go new file mode 100644 index 0000000000..c530b04a14 --- /dev/null +++ b/cmd/commands/utils_test.go @@ -0,0 +1,40 @@ +package commands + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/basecoin/types" +) + +//Test the parse coin and parse coins functionality +func TestParse(t *testing.T) { + + makeCoin := func(str string) types.Coin { + coin, err := ParseCoin(str) + if err != nil { + panic(err.Error()) + } + return coin + } + + makeCoins := func(str string) types.Coins { + coin, err := ParseCoins(str) + if err != nil { + panic(err.Error()) + } + return coin + } + + //testing ParseCoin Function + assert.True(t, types.Coin{} == makeCoin(""), "parseCoin makes bad empty coin") + assert.True(t, types.Coin{"fooCoin", 1} == makeCoin("1fooCoin"), "parseCoin makes bad coins") + assert.True(t, types.Coin{"barCoin", 10} == makeCoin("10 barCoin"), "parseCoin makes bad coins") + + //testing ParseCoins Function + assert.True(t, types.Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")), "parseCoins doesn't parse a single coin") + assert.True(t, types.Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")), + "parseCoins doesn't properly parse two coins") + assert.True(t, types.Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")), + "parseCoins doesn't properly parse two coins which use spaces") +} diff --git a/docs/guide/basecoin-basics.md b/docs/guide/basecoin-basics.md index cf88211a66..0dccb32622 100644 --- a/docs/guide/basecoin-basics.md +++ b/docs/guide/basecoin-basics.md @@ -95,14 +95,14 @@ The first account is flush with cash, while the second account doesn't exist. Let's send funds from the first account to the second: ``` -basecoin tx send --to 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 --amount 10 +basecoin tx send --to 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 --amount 10mycoin ``` By default, the CLI looks for a `priv_validator.json` to sign the transaction with, so this will only work if you are in the `$GOPATH/src/github.com/tendermint/basecoin/data`. To specify a different key, we can use the `--from` flag. -Now if we check the second account, it should have `10` coins! +Now if we check the second account, it should have `10` 'mycoin' coins! ``` basecoin account 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 @@ -111,7 +111,7 @@ basecoin account 0x1DA7C74F9C219229FD54CC9F7386D5A3839F0090 We can send some of these coins back like so: ``` -basecoin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --from key2.json --amount 5 +basecoin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --from key2.json --amount 5mycoin ``` Note how we use the `--from` flag to select a different account to send from. @@ -119,7 +119,7 @@ Note how we use the `--from` flag to select a different account to send from. If we try to send too much, we'll get an error: ``` -basecoin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --from key2.json --amount 100 +basecoin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --from key2.json --amount 100mycoin ``` See `basecoin tx send --help` for additional details. diff --git a/docs/guide/example-plugin.md b/docs/guide/example-plugin.md index f62f4de9bc..73a6b4e854 100644 --- a/docs/guide/example-plugin.md +++ b/docs/guide/example-plugin.md @@ -132,10 +132,9 @@ OPTIONS: --node value Tendermint RPC address (default: "tcp://localhost:46657") --chain_id value ID of the chain for replay protection (default: "test_chain_id") --from value Path to a private key to sign the transaction (default: "key.json") - --amount value Amount of coins to send in the transaction (default: 0) - --coin value Specify a coin denomination (default: "mycoin") + --amount value Coins to send in transaction of the format ,,... (eg: 1btc,2gold,5silver) --gas value The amount of gas for the transaction (default: 0) - --fee value The transaction fee (default: 0) + --fee value Coins for the transaction fee of the format --sequence value Sequence number for the account (default: 0) --valid Set this to make the transaction valid ``` @@ -366,25 +365,25 @@ example-plugin start --in-proc In another window, we can try sending some transactions: ``` -example-plugin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --amount 100 --coin gold --chain_id example-chain +example-plugin tx send --to 0x1B1BE55F969F54064628A63B9559E7C21C925165 --amount 100gold --chain_id example-chain ``` -Note the `--coin` and `--chain_id` flags. In the [previous tutorial](basecoin-basics.md), -we didn't need them because we were using the default coin type ("mycoin") and chain ID ("test_chain_id"). -Now that we're using custom values, we need to specify them explicitly on the command line. +Note the `--chain_id` flag. In the [previous tutorial](basecoin-basics.md), +we didn't include it because we were using the default chain ID ("test_chain_id"). +Now that we're using a custom chain, we need to specify the chain explicitly on the command line. Ok, so that's how we can send a `SendTx` transaction using our `example-plugin` CLI, but we were already able to do that with the `basecoin` CLI. With our new CLI, however, we can also send an `ExamplePluginTx`: ``` -example-plugin tx example --amount 1 --coin gold --chain_id example-chain +example-plugin tx example --amount 1gold --chain_id example-chain ``` The transaction is invalid! That's because we didn't specify the `--valid` flag: ``` -example-plugin tx example --valid --amount 1 --coin gold --chain_id example-chain +example-plugin tx example --valid --amount 1gold --chain_id example-chain ``` Tada! We successfuly created, signed, broadcast, and processed our custom transaction type. @@ -404,7 +403,7 @@ which contains only an integer. If we send another transaction, and then query again, we'll see the value increment: ``` -example-plugin tx example --valid --amount 1 --coin gold --chain_id example-chain +example-plugin tx example --valid --amount 1gold --chain_id example-chain example-plugin query ExamplePlugin.State ``` diff --git a/docs/guide/ibc.md b/docs/guide/ibc.md index e65905f7d1..f933c22ea7 100644 --- a/docs/guide/ibc.md +++ b/docs/guide/ibc.md @@ -231,13 +231,13 @@ export CHAIN_FLAGS2="--chain_id $CHAIN_ID2 --from ./data/chain2/basecoin/key.jso Let's start by registering `test_chain_1` on `test_chain_2`: ``` -basecoin tx ibc --amount 10 $CHAIN_FLAGS2 register --chain_id $CHAIN_ID1 --genesis ./data/chain1/tendermint/genesis.json +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --chain_id $CHAIN_ID1 --genesis ./data/chain1/tendermint/genesis.json ``` Now we can create the outgoing packet on `test_chain_1`: ``` -basecoin tx ibc --amount 10 $CHAIN_FLAGS1 packet create --from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload 0xDEADBEEF --sequence 1 +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload 0xDEADBEEF --sequence 1 ``` Note our payload is just `DEADBEEF`. @@ -265,7 +265,7 @@ The former is used as input for later commands; the latter is human-readable, so Let's send this updated information about `test_chain_1` to `test_chain_2`: ``` -basecoin tx ibc --amount 10 $CHAIN_FLAGS2 update --header 0x
--commit 0x +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x
--commit 0x ``` where `
` and `` are the hex-encoded header and commit returned by the previous `block` command. @@ -275,7 +275,7 @@ along with proof the packet was committed on `test_chain_1`. Since `test_chain_2 of `test_chain_1`, it will be able to verify the proof! ``` -basecoin tx ibc --amount 10 $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height --packet 0x --proof 0x +basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height --packet 0x --proof 0x ``` Here, `` is one greater than the height retuned by the previous `query` command, and `` and `` are the diff --git a/plugins/counter/counter_test.go b/plugins/counter/counter_test.go index 0b8ee4a866..a4c4236230 100644 --- a/plugins/counter/counter_test.go +++ b/plugins/counter/counter_test.go @@ -39,7 +39,7 @@ func TestCounterPlugin(t *testing.T) { tx := &types.AppTx{ Gas: gas, Fee: fee, - Name: "counter", + Name: counterPlugin.Name(), Input: types.NewTxInput(test1Acc.PubKey, inputCoins, inputSequence), Data: wire.BinaryBytes(CounterTx{Valid: true, Fee: appFee}), } diff --git a/types/coin.go b/types/coin.go index 23dc8f0868..b64f78bc97 100644 --- a/types/coin.go +++ b/types/coin.go @@ -100,7 +100,7 @@ func (coinsA Coins) IsGTE(coinsB Coins) bool { if len(diff) == 0 { return true } - return diff.IsPositive() + return diff.IsNonnegative() } func (coins Coins) IsZero() bool {