diff --git a/client/commands/common.go b/client/commands/common.go index b6781bc3fe..a7ea8da7d7 100644 --- a/client/commands/common.go +++ b/client/commands/common.go @@ -128,3 +128,14 @@ func ParseActors(key string) (signers []basecoin.Actor, err error) { } return } + +// GetOneArg makes sure there is exactly one positional argument +func GetOneArg(args []string, argname string) (string, error) { + if len(args) == 0 { + return "", errors.Errorf("Missing required argument [%s]", argname) + } + if len(args) > 1 { + return "", errors.Errorf("Only accepts one argument [%s]", argname) + } + return args[0], nil +} diff --git a/client/commands/version.go b/client/commands/version.go new file mode 100644 index 0000000000..9ffa6b36dc --- /dev/null +++ b/client/commands/version.go @@ -0,0 +1,17 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/tendermint/basecoin/version" +) + +// VersionCmd - command to show the application version +var VersionCmd = &cobra.Command{ + Use: "version", + Short: "Show version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version.Version) + }, +} diff --git a/cmd/basecli/main.go b/cmd/basecli/main.go index 0a3e8e997c..02335e3904 100644 --- a/cmd/basecli/main.go +++ b/cmd/basecli/main.go @@ -1,12 +1,10 @@ package main import ( - "fmt" "os" "github.com/spf13/cobra" - "github.com/tendermint/abci/version" keycmd "github.com/tendermint/go-crypto/cmd" "github.com/tendermint/tmlibs/cli" @@ -37,15 +35,6 @@ tmcli to work for any custom abci app. `, } -// VersionCmd - command to show the application version -var VersionCmd = &cobra.Command{ - Use: "version", - Short: "Show version info", - Run: func(cmd *cobra.Command, args []string) { - fmt.Println(version.Version) - }, -} - func main() { commands.AddBasicFlags(BaseCli) @@ -56,6 +45,7 @@ func main() { proofs.KeyQueryCmd, coincmd.AccountQueryCmd, noncecmd.NonceQueryCmd, + rolecmd.RoleQueryCmd, ) proofs.TxPresenters.Register("base", txcmd.BaseTxPresenter{}) @@ -73,6 +63,8 @@ func main() { txcmd.RootCmd.AddCommand( // This is the default transaction, optional in your app coincmd.SendTxCmd, + // this enables creating roles + rolecmd.CreateRoleTxCmd, ) // Set up the various commands to use @@ -85,7 +77,7 @@ func main() { proofs.RootCmd, txcmd.RootCmd, proxy.RootCmd, - VersionCmd, + commands.VersionCmd, auto.AutoCompleteCmd, ) diff --git a/modules/coin/commands/query.go b/modules/coin/commands/query.go index 5bea51478c..7a7d4805ad 100644 --- a/modules/coin/commands/query.go +++ b/modules/coin/commands/query.go @@ -6,9 +6,8 @@ import ( lc "github.com/tendermint/light-client" - 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/auth" "github.com/tendermint/basecoin/modules/coin" "github.com/tendermint/basecoin/stack" ) @@ -17,15 +16,19 @@ import ( var AccountQueryCmd = &cobra.Command{ Use: "account [address]", Short: "Get details of an account, with proof", - RunE: lcmd.RequireInit(accountQueryCmd), + RunE: commands.RequireInit(accountQueryCmd), } func accountQueryCmd(cmd *cobra.Command, args []string) error { - addr, err := proofcmd.ParseHexKey(args, "address") + addr, err := commands.GetOneArg(args, "address") if err != nil { return err } - key := stack.PrefixedKey(coin.NameCoin, auth.SigPerm(addr).Bytes()) + act, err := commands.ParseActor(addr) + if err != nil { + return err + } + key := stack.PrefixedKey(coin.NameCoin, act.Bytes()) acc := coin.Account{} proof, err := proofcmd.GetAndParseAppProof(key, &acc) diff --git a/modules/roles/commands/tx.go b/modules/roles/commands/tx.go index b9c39043ce..7104131427 100644 --- a/modules/roles/commands/tx.go +++ b/modules/roles/commands/tx.go @@ -13,8 +13,8 @@ import ( // CreateRoleTxCmd is CLI command to send tokens between basecoin accounts var CreateRoleTxCmd = &cobra.Command{ - Use: "send", - Short: "send tokens from one account to another", + Use: "create-role", + Short: "Create a new role", RunE: commands.RequireInit(createRoleTxCmd), } diff --git a/tests/cli/common.sh b/tests/cli/common.sh index c68a80c56d..f50ef6b9dc 100644 --- a/tests/cli/common.sh +++ b/tests/cli/common.sh @@ -138,6 +138,23 @@ checkAccount() { return $? } +# XXX Ex Usage: checkRole $ROLE $SIGS $NUM_SIGNERS +# Desc: Ensures this named role exists, and has the number of members and required signatures as above +checkRole() { + # make sure sender goes down + ROLE=$(${CLIENT_EXE} query role $1) + if ! assertTrue "line=${LINENO}, role must exist" $?; then + return 1 + fi + + if [ -n "$DEBUG" ]; then echo $ROLE; echo; fi + assertEquals "line=${LINENO}, proper sigs" "$2" $(echo $ROLE | jq .data.min_sigs) + assertEquals "line=${LINENO}, proper app" '"sigs"' $(echo $ROLE | jq '.data.signers[0].app' ) + assertEquals "line=${LINENO}, proper signers" "$3" $(echo $ROLE | jq '.data.signers | length') + return $? +} + + # XXX Ex Usage: txSucceeded $? "$TX" "$RECIEVER" # Desc: Must be called right after the `tx` command, makes sure it got a success response txSucceeded() { @@ -171,6 +188,36 @@ checkSendTx() { return $? } +# XXX Ex Usage: toHex "my-name" +# converts the string into the hex representation of the bytes +toHex() { + echo -n $1 | od -A n -t x1 | sed 's/ //g' | tr 'a-f' 'A-F' +} + +# XXX Ex Usage: checkRoleTx $HASH $HEIGHT $NAME $NUM_SIGNERS +# Desc: This looks up the tx by hash, and makes sure the height and type match +# and that the it refers to the proper role +checkRoleTx() { + TX=$(${CLIENT_EXE} query tx $1) + assertTrue "line=${LINENO}, found tx" $? + if [ -n "$DEBUG" ]; then echo $TX; echo; fi + + + assertEquals "line=${LINENO}, proper height" $2 $(echo $TX | jq .height) + assertEquals "line=${LINENO}, type=sigs/one" '"sigs/one"' $(echo $TX | jq .data.type) + CTX=$(echo $TX | jq .data.data.tx) + assertEquals "line=${LINENO}, type=chain/tx" '"chain/tx"' $(echo $CTX | jq .type) + NTX=$(echo $CTX | jq .data.tx) + assertEquals "line=${LINENO}, type=nonce" '"nonce"' $(echo $NTX | jq .type) + RTX=$(echo $NTX | jq .data.tx) + assertEquals "line=${LINENO}, type=role/create" '"role/create"' $(echo $RTX | jq .type) + HEXNAME=$(toHex $3) + assertEquals "line=${LINENO}, proper name" "\"$HEXNAME\"" $(echo $RTX | jq .data.role) + assertEquals "line=${LINENO}, proper num signers" "$4" $(echo $RTX | jq '.data.signers | length') + return $? +} + + # XXX Ex Usage: checkSendFeeTx $HASH $HEIGHT $SENDER $AMOUNT $FEE # Desc: This is like checkSendTx, but asserts a feetx wrapper with $FEE value. # This looks up the tx by hash, and makes sure the height and type match diff --git a/tests/cli/roles.sh b/tests/cli/roles.sh new file mode 100755 index 0000000000..0932ab7f9c --- /dev/null +++ b/tests/cli/roles.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# These global variables are required for common.sh +SERVER_EXE=basecoin +CLIENT_EXE=basecli +ACCOUNTS=(jae ethan bucky rigel igor) +RICH=${ACCOUNTS[0]} +POOR=${ACCOUNTS[4]} +DUDE=${ACCOUNTS[2]} + +oneTimeSetUp() { + if ! quickSetup .basecoin_test_roles roles-chain; then + exit 1; + fi +} + +oneTimeTearDown() { + quickTearDown +} + +test01SetupRole() { + ONE=$(getAddr $RICH) + TWO=$(getAddr $POOR) + THREE=$(getAddr $DUDE) + MEMBERS=${ONE},${TWO},${THREE} + + assertFalse "line=${LINENO}, missing min-sigs" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=bank --members=${MEMBERS} --sequence=1 --name=$RICH" + assertFalse "line=${LINENO}, missing members" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=bank --min-sigs=2 --sequence=1 --name=$RICH" + assertFalse "line=${LINENO}, missing role" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --min-sigs=2 --members=${MEMBERS} --sequence=1 --name=$RICH" + TX=$(echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=bank --min-sigs=2 --members=${MEMBERS} --sequence=1 --name=$RICH) + txSucceeded $? "$TX" "bank" + HASH=$(echo $TX | jq .hash | tr -d \") + TX_HEIGHT=$(echo $TX | jq .height) + + checkRole bank 2 3 + + # Make sure tx is indexed + checkRoleTx $HASH $TX_HEIGHT "bank" 3 +} + +test02SendTxToRole() { + SENDER=$(getAddr $RICH) + HEXROLE=$(toHex bank) + RECV="role:$HEXROLE" + + TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --fee=90mycoin --amount=10000mycoin --to=$RECV --sequence=2 --name=$RICH) + txSucceeded $? "$TX" "bank" + HASH=$(echo $TX | jq .hash | tr -d \") + TX_HEIGHT=$(echo $TX | jq .height) + + # reduce by 10090 + checkAccount $SENDER "9007199254730902" + checkAccount $RECV "10000" + + checkSendFeeTx $HASH $TX_HEIGHT $SENDER "10000" "90" +} + +# test02SendTxWithFee() { +# SENDER=$(getAddr $RICH) +# RECV=$(getAddr $POOR) + +# # Test to see if the auto-sequencing works, the sequence here should be calculated to be 2 +# TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --to=$RECV --name=$RICH) +# txSucceeded $? "$TX" "$RECV" +# HASH=$(echo $TX | jq .hash | tr -d \") +# TX_HEIGHT=$(echo $TX | jq .height) + +# # deduct 100 from sender, add 90 to receiver... fees "vanish" +# checkAccount $SENDER "9007199254739900" +# checkAccount $RECV "1082" + +# # Make sure tx is indexed +# checkSendFeeTx $HASH $TX_HEIGHT $SENDER "90" "10" + +# # assert replay protection +# TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=90mycoin --fee=10mycoin --sequence=2 --to=$RECV --name=$RICH 2>/dev/null) +# assertFalse "line=${LINENO}, replay: $TX" $? +# checkAccount $SENDER "9007199254739900" +# checkAccount $RECV "1082" + +# # make sure we can query the proper nonce +# NONCE=$(${CLIENT_EXE} query nonce $SENDER) +# if [ -n "$DEBUG" ]; then echo $NONCE; echo; fi +# # TODO: note that cobra returns error code 0 on parse failure, +# # so currently this check passes even if there is no nonce query command +# if assertTrue "line=${LINENO}, no nonce query" $?; then +# assertEquals "line=${LINENO}, proper nonce" "2" $(echo $NONCE | jq .data) +# fi +# } + + +# Load common then run these tests with shunit2! +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory +. $DIR/common.sh +. $DIR/shunit2