Merge pull request #115 from tendermint/feature/remove-client-from-basecoin

Feature/remove client from basecoin
This commit is contained in:
Ethan Frey
2017-06-16 17:40:25 +02:00
committed by GitHub
37 changed files with 831 additions and 1531 deletions
+27
View File
@@ -1,5 +1,32 @@
# Changelog
## 0.6.0 (???)
Make the basecli command the only way to use client-side, to enforce best
security practices. Lots of enhancements to get it up to production quality.
BREAKING CHANGES:
- basecli
- `basecli proof state get` -> `basecli query key`
- `basecli proof tx get` -> `basecli query tx`
- `basecli proof state get --app=account` -> `basecli query account`
- use `--chain-id` not `--chainid` for consistency
- update to use `--trace` not `--debug` for stack traces on errors
- complete overhaul on how tx and query subcommands are added. (see counter or trackomatron for examples)
- no longer supports counter app (see new countercli)
- basecoin
- removed all client side functionality from it (use basecli now for proofs)
ENHANCEMENTS:
- intergrates tendermint 0.10.0 (not the rc-2, but the real thing)
- commands return error code (1) on failure for easier script testing
- add `reset_all` to basecli, and never delete keys on `init`
- new shutil based unit tests, with better coverage of the cli actions
BUG FIXES:
- no longer panics on missing app_options in genesis (thanks, anton)
## 0.5.2 (June 2, 2017)
BUG FIXES:
+17 -20
View File
@@ -1,9 +1,8 @@
GOTOOLS = \
github.com/mitchellh/gox \
github.com/Masterminds/glide
GOTOOLS = github.com/mitchellh/gox \
github.com/Masterminds/glide
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
all: get_vendor_deps test install
all: get_vendor_deps install test
build:
go build ./cmd/...
@@ -15,36 +14,34 @@ dist:
@bash scripts/dist.sh
@bash scripts/publish.sh
clitest/shunit2:
wget "https://raw.githubusercontent.com/kward/shunit2/master/source/2.1/src/shunit2" \
-q -O clitest/shunit2
test: test_unit test_cli
test_cli: clitest/shunit2
# sudo apt-get install jq
@./clitest/basictx.sh
# @./clitest/ibc.sh
test:
test_unit:
go test $(PACKAGES)
#go run tests/tendermint/*.go
# get_deps:
# go get -d ./...
# update_deps:
# go get -d -u ./...
test_cli: tests/cli/shunit2
# sudo apt-get install jq
@./tests/cli/basictx.sh
@./tests/cli/counter.sh
@./tests/cli/ibc.sh
get_vendor_deps: tools
glide install
build-docker:
docker run -it --rm -v "$(PWD):/go/src/github.com/tendermint/basecoin" -w "/go/src/github.com/tendermint/basecoin" -e "CGO_ENABLED=0" golang:alpine go build ./cmd/basecoin
docker run -it --rm -v "$(PWD):/go/src/github.com/tendermint/basecoin" -w \
"/go/src/github.com/tendermint/basecoin" -e "CGO_ENABLED=0" golang:alpine go build ./cmd/basecoin
docker build -t "tendermint/basecoin" .
tests/cli/shunit2:
wget "https://raw.githubusercontent.com/kward/shunit2/master/source/2.1/src/shunit2" \
-q -O tests/cli/shunit2
tools:
go get -u -v $(GOTOOLS)
clean:
@rm -f ./basecoin
.PHONY: all build install test get_deps update_deps get_vendor_deps build-docker clean
.PHONY: all build install test test_cli test_unit get_vendor_deps build-docker clean
+1 -2
View File
@@ -19,9 +19,8 @@ dependencies:
test:
override:
- "cd $REPO && glide install && go install ./cmd/basecoin"
- "cd $REPO && glide install && go install ./cmd/..."
- ls $GOPATH/bin
- "cd $REPO && make test"
- "cd $REPO/demo && bash start.sh"
-143
View File
@@ -1,143 +0,0 @@
#!/bin/bash
oneTimeSetUp() {
BASE_DIR=$HOME/.basecoin_test_basictx
LOG=$BASE_DIR/test.log
SERVER_LOG=$BASE_DIR/basecoin.log
rm -rf $BASE_DIR
mkdir -p $BASE_DIR
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[1]}
# set up client
prepareClient
# start basecoin server (with counter)
initServer
echo pid $PID_SERVER
initClient
echo "...Testing may begin!"
echo
echo
echo
}
oneTimeTearDown() {
echo
echo
echo "stopping basecoin test server..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
prepareClient() {
echo "Preparing client keys..."
export BC_HOME=$BASE_DIR/client
basecli reset_all
assertTrue $?
for i in "${!ACCOUNTS[@]}"; do
newKey ${ACCOUNTS[$i]}
done
}
initServer() {
echo "Setting up genesis..."
SERVE_DIR=$BASE_DIR/server
rm -rf $SERVE_DIR 2>/dev/null
basecoin init --home=$SERVE_DIR >>$SERVER_LOG
#change the genesis to the first account
GENKEY=$(basecli keys get ${RICH} -o json | jq .pubkey.data)
GENJSON=$(cat $SERVE_DIR/genesis.json)
echo $GENJSON | jq '.app_options.accounts[0].pub_key.data='$GENKEY > $SERVE_DIR/genesis.json
echo "Starting server..."
basecoin start --home=$SERVE_DIR >>$SERVER_LOG 2>&1 &
sleep 5
PID_SERVER=$!
}
initClient() {
echo "Attaching client..."
# hard-code the expected validator hash
basecli init --chain-id=test_chain_id --node=tcp://localhost:46657 --valhash=EB168E17E45BAEB194D4C79067FFECF345C64DE6
assertTrue "initialized light-client" $?
}
# newKeys makes a key for a given username, second arg optional password
newKey(){
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
(echo $KEYPASS; echo $KEYPASS) | basecli keys new $1 >>$LOG 2>/dev/null
assertTrue "created $1" $?
assertTrue "$1 doesn't exist" "basecli keys get $1"
}
# getAddr gets the address for a key name
getAddr() {
assertNotNull "keyname required" "$1"
RAW=$(basecli keys get $1)
assertTrue "no key for $1" $?
# print the addr
echo $RAW | cut -d' ' -f2
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "requires arg" "basecli query account"
ACCT=$(basecli query account $SENDER)
assertTrue "must have proper genesis account" $?
assertEquals "no tx" "0" $(echo $ACCT | jq .data.sequence)
assertEquals "has money" "9007199254740992" $(echo $ACCT | jq .data.coins[0].amount)
ACCT2=$(basecli query account $RECV)
assertFalse "has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "missing dest" "basecli tx send --amount=992mycoin --sequence=1 2>/dev/null"
assertFalse "bad password" "echo foo | basecli tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null"
# we have to remove the password request from stdout, to just get the json
RES=$(echo qwertyuiop | basecli tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null | tail -n +2)
assertTrue "sent tx" $?
HASH=$(echo $RES | jq .hash | tr -d \")
TX_HEIGHT=$(echo $RES | jq .height)
assertEquals "good check" "0" $(echo $RES | jq .check_tx.code)
assertEquals "good deliver" "0" $(echo $RES | jq .deliver_tx.code)
# make sure sender goes down
ACCT=$(basecli query account $SENDER)
assertTrue "must have genesis account" $?
assertEquals "one tx" "1" $(echo $ACCT | jq .data.sequence)
assertEquals "has money" "9007199254740000" $(echo $ACCT | jq .data.coins[0].amount)
# make sure recipient goes up
ACCT2=$(basecli query account $RECV)
assertTrue "must have new account" $?
assertEquals "no tx" "0" $(echo $ACCT2 | jq .data.sequence)
assertEquals "has money" "992" $(echo $ACCT2 | jq .data.coins[0].amount)
# make sure tx is indexed
TX=$(basecli query tx $HASH)
assertTrue "found tx" $?
assertEquals "proper height" $TX_HEIGHT $(echo $TX | jq .height)
assertEquals "type=send" '"send"' $(echo $TX | jq .data.type)
assertEquals "proper sender" "\"$SENDER\"" $(echo $TX | jq .data.data.inputs[0].address)
assertEquals "proper out amount" "992" $(echo $TX | jq .data.data.outputs[0].coins[0].amount)
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
. $DIR/shunit2
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
echo "ibc test not implemented"
+95 -64
View File
@@ -2,6 +2,7 @@ package commands
import (
"encoding/hex"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -10,41 +11,48 @@ import (
"github.com/tendermint/light-client/commands"
txcmd "github.com/tendermint/light-client/commands/txs"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cmn "github.com/tendermint/tmlibs/common"
btypes "github.com/tendermint/basecoin/types"
)
/*** Here is the sendtx command ***/
/******** SendTx *********/
// SendTxCmd is CLI command to send tokens between basecoin accounts
var SendTxCmd = &cobra.Command{
Use: "send",
Short: "send tokens from one account to another",
RunE: doSendTx,
}
//nolint
const (
ToFlag = "to"
AmountFlag = "amount"
FeeFlag = "fee"
GasFlag = "gas"
SequenceFlag = "sequence"
FlagTo = "to"
FlagAmount = "amount"
FlagFee = "fee"
FlagGas = "gas"
FlagSequence = "sequence"
)
func init() {
flags := SendTxCmd.Flags()
flags.String(ToFlag, "", "Destination address for the bits")
flags.String(AmountFlag, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
flags.String(FeeFlag, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
flags.Int64(GasFlag, 0, "Amount of gas for this transaction")
flags.Int(SequenceFlag, -1, "Sequence number for this transaction")
flags.String(FlagTo, "", "Destination address for the bits")
flags.String(FlagAmount, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
flags.String(FlagFee, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
flags.Int64(FlagGas, 0, "Amount of gas for this transaction")
flags.Int(FlagSequence, -1, "Sequence number for this transaction")
}
// runDemo is an example of how to make a tx
func doSendTx(cmd *cobra.Command, args []string) error {
tx := new(btypes.SendTx)
// load data from json or flags
tx := new(btypes.SendTx)
found, err := txcmd.LoadJSON(tx)
if err != nil {
return err
}
if !found {
err = readSendTxFlags(tx)
}
@@ -52,6 +60,7 @@ func doSendTx(cmd *cobra.Command, args []string) error {
return err
}
// Wrap and add signer
send := &SendTx{
chainID: commands.GetChainID(),
Tx: tx,
@@ -64,34 +73,34 @@ func doSendTx(cmd *cobra.Command, args []string) error {
return err
}
// output result
// Output result
return txcmd.OutputTx(bres)
}
func readSendTxFlags(tx *btypes.SendTx) error {
// parse to address
to, err := ParseHexFlag(ToFlag)
if err != nil {
return errors.Errorf("To address is invalid hex: %v\n", err)
}
//parse the fee and amounts into coin types
tx.Fee, err = btypes.ParseCoin(viper.GetString(FeeFlag))
to, err := parseChainAddress(viper.GetString(FlagTo))
if err != nil {
return err
}
amountCoins, err := btypes.ParseCoins(viper.GetString(AmountFlag))
//parse the fee and amounts into coin types
tx.Fee, err = btypes.ParseCoin(viper.GetString(FlagFee))
if err != nil {
return err
}
amountCoins, err := btypes.ParseCoins(viper.GetString(FlagAmount))
if err != nil {
return err
}
// set the gas
tx.Gas = viper.GetInt64(GasFlag)
tx.Gas = viper.GetInt64(FlagGas)
// craft the inputs and outputs
tx.Inputs = []btypes.TxInput{{
Coins: amountCoins,
Sequence: viper.GetInt(SequenceFlag),
Sequence: viper.GetInt(FlagSequence),
}}
tx.Outputs = []btypes.TxOutput{{
Address: to,
@@ -101,41 +110,81 @@ func readSendTxFlags(tx *btypes.SendTx) error {
return nil
}
func parseChainAddress(toFlag string) ([]byte, error) {
var toHex string
var chainPrefix string
spl := strings.Split(toFlag, "/")
switch len(spl) {
case 1:
toHex = spl[0]
case 2:
chainPrefix = spl[0]
toHex = spl[1]
default:
return nil, errors.Errorf("To address has too many slashes")
}
// convert destination address to bytes
to, err := hex.DecodeString(cmn.StripHex(toHex))
if err != nil {
return nil, errors.Errorf("To address is invalid hex: %v\n", err)
}
if chainPrefix != "" {
to = []byte(chainPrefix + "/" + string(to))
}
return to, nil
}
/******** AppTx *********/
// BroadcastAppTx wraps, signs, and executes an app tx basecoin transaction
func BroadcastAppTx(tx *btypes.AppTx) (*ctypes.ResultBroadcastTxCommit, error) {
// Generate app transaction to be broadcast
appTx := WrapAppTx(tx)
appTx.AddSigner(txcmd.GetSigner())
// Sign if needed and post to the node. This it the work-horse
return txcmd.SignAndPostTx(appTx)
}
// AddAppTxFlags adds flags required by apptx
func AddAppTxFlags(fs *flag.FlagSet) {
fs.String(AmountFlag, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.String(FeeFlag, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
fs.Int64(GasFlag, 0, "Amount of gas for this transaction")
fs.Int(SequenceFlag, -1, "Sequence number for this transaction")
fs.String(FlagAmount, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.String(FlagFee, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
fs.Int64(FlagGas, 0, "Amount of gas for this transaction")
fs.Int(FlagSequence, -1, "Sequence number for this transaction")
}
// ReadAppTxFlags reads in the standard flags
// your command should parse info to set tx.Name and tx.Data
func ReadAppTxFlags(tx *btypes.AppTx) error {
//parse the fee and amounts into coin types
var err error
tx.Fee, err = btypes.ParseCoin(viper.GetString(FeeFlag))
func ReadAppTxFlags() (gas int64, fee btypes.Coin, txInput btypes.TxInput, err error) {
// Set the gas
gas = viper.GetInt64(FlagGas)
// Parse the fee and amounts into coin types
fee, err = btypes.ParseCoin(viper.GetString(FlagFee))
if err != nil {
return err
return
}
amountCoins, err := btypes.ParseCoins(viper.GetString(AmountFlag))
// craft the inputs
var amount btypes.Coins
amount, err = btypes.ParseCoins(viper.GetString(FlagAmount))
if err != nil {
return err
return
}
txInput = btypes.TxInput{
Coins: amount,
Sequence: viper.GetInt(FlagSequence),
}
// set the gas
tx.Gas = viper.GetInt64(GasFlag)
// craft the inputs and outputs
tx.Input = btypes.TxInput{
Coins: amountCoins,
Sequence: viper.GetInt(SequenceFlag),
}
return nil
return
}
// WrapAppTx wraps the transaction with chain id
func WrapAppTx(tx *btypes.AppTx) *AppTx {
return &AppTx{
chainID: commands.GetChainID(),
@@ -145,25 +194,7 @@ func WrapAppTx(tx *btypes.AppTx) *AppTx {
/** TODO copied from basecoin cli - put in common somewhere? **/
// ParseHexFlag parses a flag string to byte array
func ParseHexFlag(flag string) ([]byte, error) {
return hex.DecodeString(StripHex(viper.GetString(flag)))
}
// Returns true for non-empty hex-string prefixed with "0x"
func isHex(s string) bool {
if len(s) > 2 && s[:2] == "0x" {
_, err := hex.DecodeString(s[2:])
if err != nil {
return false
}
return true
}
return false
}
func StripHex(s string) string {
if isHex(s) {
return s[2:]
}
return s
return hex.DecodeString(cmn.StripHex(viper.GetString(flag)))
}
+2 -1
View File
@@ -97,7 +97,8 @@ func (s *SendTx) ValidateBasic() error {
}
}
for _, out := range s.Tx.Outputs {
if len(out.Address) != 20 {
// we now allow chain/addr, so it can be more than 20 bytes
if len(out.Address) < 20 {
return errors.Errorf("Invalid output address length: %d", len(out.Address))
}
if !out.Coins.IsValid() {
-79
View File
@@ -1,79 +0,0 @@
package counter
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
wire "github.com/tendermint/go-wire"
txcmd "github.com/tendermint/light-client/commands/txs"
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
"github.com/tendermint/basecoin/plugins/counter"
btypes "github.com/tendermint/basecoin/types"
)
var CounterTxCmd = &cobra.Command{
Use: "counter",
Short: "add a vote to the counter",
Long: `Add a vote to the counter.
You must pass --valid for it to count and the countfee will be added to the counter.`,
RunE: doCounterTx,
}
const (
CountFeeFlag = "countfee"
ValidFlag = "valid"
)
func init() {
fs := CounterTxCmd.Flags()
bcmd.AddAppTxFlags(fs)
fs.String(CountFeeFlag, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.Bool(ValidFlag, false, "Is count valid?")
}
func doCounterTx(cmd *cobra.Command, args []string) error {
tx := new(btypes.AppTx)
// Note: we don't support loading apptx from json currently, so skip that
// read the standard flags
err := bcmd.ReadAppTxFlags(tx)
if err != nil {
return err
}
// now read the app-specific flags
err = readCounterFlags(tx)
if err != nil {
return err
}
app := bcmd.WrapAppTx(tx)
app.AddSigner(txcmd.GetSigner())
// Sign if needed and post. This it the work-horse
bres, err := txcmd.SignAndPostTx(app)
if err != nil {
return err
}
// output result
return txcmd.OutputTx(bres)
}
// readCounterFlags sets the app-specific data in the AppTx
func readCounterFlags(tx *btypes.AppTx) error {
countFee, err := btypes.ParseCoins(viper.GetString(CountFeeFlag))
if err != nil {
return err
}
ctx := counter.CounterTx{
Valid: viper.GetBool(ValidFlag),
Fee: countFee,
}
tx.Name = counter.New().Name()
tx.Data = wire.BinaryBytes(ctx)
return nil
}
-42
View File
@@ -1,42 +0,0 @@
package counter
import (
"github.com/spf13/cobra"
proofcmd "github.com/tendermint/light-client/commands/proofs"
"github.com/tendermint/basecoin/plugins/counter"
)
var CounterQueryCmd = &cobra.Command{
Use: "counter",
Short: "Query counter state, with proof",
RunE: doCounterQuery,
}
func doCounterQuery(cmd *cobra.Command, args []string) error {
key := counter.New().StateKey()
var cp counter.CounterPluginState
proof, err := proofcmd.GetAndParseAppProof(key, &cp)
if err != nil {
return err
}
return proofcmd.OutputProof(cp, proof.BlockHeight())
}
/*** doesn't seem to be needed anymore??? ***/
// type CounterPresenter struct{}
// func (_ CounterPresenter) MakeKey(str string) ([]byte, error) {
// key := counter.New().StateKey()
// return key, nil
// }
// func (_ CounterPresenter) ParseData(raw []byte) (interface{}, error) {
// var cp counter.CounterPluginState
// err := wire.ReadBinaryBytes(raw, &cp)
// return cp, err
// }
+4 -12
View File
@@ -14,7 +14,6 @@ import (
"github.com/tendermint/tmlibs/cli"
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
bcount "github.com/tendermint/basecoin/cmd/basecli/counter"
)
// BaseCli represents the base command when called without any subcommands
@@ -32,26 +31,19 @@ tmcli to work for any custom abci app.
func main() {
commands.AddBasicFlags(BaseCli)
// prepare queries
// Prepare queries
pr := proofs.RootCmd
// these are default parsers, but you optional in your app
// These are default parsers, but optional in your app (you can remove key)
pr.AddCommand(proofs.TxCmd)
pr.AddCommand(proofs.KeyCmd)
pr.AddCommand(bcmd.AccountQueryCmd)
pr.AddCommand(bcount.CounterQueryCmd)
// here is how you would add the custom txs... but don't really add demo in your app
// you will always want this for the base send command
proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{})
tr := txs.RootCmd
tr.AddCommand(bcmd.SendTxCmd)
tr.AddCommand(bcount.CounterTxCmd)
// TODO
// txs.Register("send", bcmd.SendTxMaker{})
// txs.Register("counter", bcount.CounterTxMaker{})
// set up the various commands to use
// Set up the various commands to use
BaseCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
-5
View File
@@ -20,11 +20,6 @@ func main() {
commands.StartCmd,
commands.RelayCmd,
commands.TxCmd,
commands.QueryCmd,
commands.KeyCmd,
commands.VerifyCmd,
commands.BlockCmd,
commands.AccountCmd,
commands.UnsafeResetAllCmd,
commands.VersionCmd,
)
+3 -206
View File
@@ -1,7 +1,6 @@
package commands
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
@@ -12,8 +11,6 @@ import (
"github.com/tendermint/basecoin/plugins/ibc"
"github.com/tendermint/go-wire"
"github.com/tendermint/merkleeyes/iavl"
tmtypes "github.com/tendermint/tendermint/types"
)
// returns a new IBC plugin to be registered with Basecoin
@@ -33,85 +30,25 @@ var (
Short: "Register a blockchain via IBC",
RunE: ibcRegisterTxCmd,
}
IBCUpdateTxCmd = &cobra.Command{
Use: "update",
Short: "Update the latest state of a blockchain via IBC",
RunE: ibcUpdateTxCmd,
}
IBCPacketTxCmd = &cobra.Command{
Use: "packet",
Short: "Send a new packet via IBC",
}
IBCPacketCreateTxCmd = &cobra.Command{
Use: "create",
Short: "Create an egress IBC packet",
RunE: ibcPacketCreateTxCmd,
}
IBCPacketPostTxCmd = &cobra.Command{
Use: "post",
Short: "Deliver an IBC packet to another chain",
RunE: ibcPacketPostTxCmd,
}
)
//flags
var (
ibcChainIDFlag string
ibcGenesisFlag string
ibcHeaderFlag string
ibcCommitFlag string
ibcFromFlag string
ibcToFlag string
ibcTypeFlag string
ibcPayloadFlag string
ibcPacketFlag string
ibcProofFlag string
ibcSequenceFlag int
ibcHeightFlag int
ibcChainIDFlag string
ibcGenesisFlag string
)
func init() {
// register flags
registerFlags := []Flag2Register{
{&ibcChainIDFlag, "ibc_chain_id", "", "ChainID for the new blockchain"},
{&ibcGenesisFlag, "genesis", "", "Genesis file for the new blockchain"},
}
updateFlags := []Flag2Register{
{&ibcHeaderFlag, "header", "", "Block header for an ibc update"},
{&ibcCommitFlag, "commit", "", "Block commit for an ibc update"},
}
fromFlagReg := Flag2Register{&ibcFromFlag, "ibc_from", "", "Source ChainID"}
packetCreateFlags := []Flag2Register{
fromFlagReg,
{&ibcToFlag, "to", "", "Destination ChainID"},
{&ibcTypeFlag, "type", "", "IBC packet type (eg. coin)"},
{&ibcPayloadFlag, "payload", "", "IBC packet payload"},
{&ibcSequenceFlag, "ibc_sequence", -1, "sequence number for IBC packet"},
}
packetPostFlags := []Flag2Register{
fromFlagReg,
{&ibcHeightFlag, "height", 0, "Height the packet became egress in source chain"},
{&ibcPacketFlag, "packet", "", "hex-encoded IBC packet"},
{&ibcProofFlag, "proof", "", "hex-encoded proof of IBC packet from source chain"},
}
RegisterFlags(IBCRegisterTxCmd, registerFlags)
RegisterFlags(IBCUpdateTxCmd, updateFlags)
RegisterFlags(IBCPacketCreateTxCmd, packetCreateFlags)
RegisterFlags(IBCPacketPostTxCmd, packetPostFlags)
//register commands
IBCTxCmd.AddCommand(IBCRegisterTxCmd, IBCUpdateTxCmd, IBCPacketTxCmd)
IBCPacketTxCmd.AddCommand(IBCPacketCreateTxCmd, IBCPacketPostTxCmd)
IBCTxCmd.AddCommand(IBCRegisterTxCmd)
RegisterTxSubcommand(IBCTxCmd)
}
@@ -148,143 +85,3 @@ func ibcRegisterTxCmd(cmd *cobra.Command, args []string) error {
return AppTx(name, data)
}
func ibcUpdateTxCmd(cmd *cobra.Command, args []string) error {
headerBytes, err := hex.DecodeString(StripHex(ibcHeaderFlag))
if err != nil {
return errors.Errorf("Header (%v) is invalid hex: %v\n", ibcHeaderFlag, err)
}
commitBytes, err := hex.DecodeString(StripHex(ibcCommitFlag))
if err != nil {
return errors.Errorf("Commit (%v) is invalid hex: %v\n", ibcCommitFlag, err)
}
header := new(tmtypes.Header)
commit := new(tmtypes.Commit)
err = wire.ReadBinaryBytes(headerBytes, &header)
if err != nil {
return errors.Errorf("Error unmarshalling header: %v\n", err)
}
err = wire.ReadBinaryBytes(commitBytes, &commit)
if err != nil {
return errors.Errorf("Error unmarshalling commit: %v\n", err)
}
ibcTx := ibc.IBCUpdateChainTx{
Header: *header,
Commit: *commit,
}
out, err := json.Marshal(ibcTx)
if err != nil {
return err
}
fmt.Printf("IBCTx: %s\n", string(out))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
name := "IBC"
return AppTx(name, data)
}
func ibcPacketCreateTxCmd(cmd *cobra.Command, args []string) error {
fromChain, toChain := ibcFromFlag, ibcToFlag
packetType := ibcTypeFlag
payloadBytes, err := hex.DecodeString(StripHex(ibcPayloadFlag))
if err != nil {
return errors.Errorf("Payload (%v) is invalid hex: %v\n", ibcPayloadFlag, err)
}
sequence, err := ibcSequenceCmd()
if err != nil {
return err
}
var payload ibc.Payload
if err := wire.ReadBinaryBytes(payloadBytes, &payload); err != nil {
return err
}
ibcTx := ibc.IBCPacketCreateTx{
Packet: ibc.Packet{
SrcChainID: fromChain,
DstChainID: toChain,
Sequence: sequence,
Type: packetType,
Payload: payload,
},
}
out, err := json.Marshal(ibcTx)
if err != nil {
return err
}
fmt.Printf("IBCTx: %s\n", string(out))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
return AppTx("IBC", data)
}
func ibcPacketPostTxCmd(cmd *cobra.Command, args []string) error {
fromChain, fromHeight := ibcFromFlag, ibcHeightFlag
packetBytes, err := hex.DecodeString(StripHex(ibcPacketFlag))
if err != nil {
return errors.Errorf("Packet (%v) is invalid hex: %v\n", ibcPacketFlag, err)
}
proofBytes, err := hex.DecodeString(StripHex(ibcProofFlag))
if err != nil {
return errors.Errorf("Proof (%v) is invalid hex: %v\n", ibcProofFlag, err)
}
var packet ibc.Packet
proof := new(iavl.IAVLProof)
err = wire.ReadBinaryBytes(packetBytes, &packet)
if err != nil {
return errors.Errorf("Error unmarshalling packet: %v\n", err)
}
err = wire.ReadBinaryBytes(proofBytes, &proof)
if err != nil {
return errors.Errorf("Error unmarshalling proof: %v\n", err)
}
ibcTx := ibc.IBCPacketPostTx{
FromChainID: fromChain,
FromChainHeight: uint64(fromHeight),
Packet: packet,
Proof: proof,
}
out, err := json.Marshal(ibcTx)
if err != nil {
return err
}
fmt.Printf("IBCTx: %s\n", string(out))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
return AppTx("IBC", data)
}
func ibcSequenceCmd() (uint64, error) {
if ibcSequenceFlag >= 0 {
return uint64(ibcSequenceFlag), nil
}
// TODO: get sequence
return 0, nil
}
+1 -44
View File
@@ -9,42 +9,13 @@ import (
"strings"
//"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/go-crypto"
"github.com/tendermint/tmlibs/cli"
)
//commands
var (
KeyCmd = &cobra.Command{
Use: "key",
Short: "Manage keys",
}
NewKeyCmd = &cobra.Command{
Use: "new",
Short: "Create a new private key",
RunE: newKeyCmd,
}
)
func newKeyCmd(cmd *cobra.Command, args []string) error {
key := genKey()
keyJSON, err := json.MarshalIndent(key, "", "\t")
if err != nil {
return err
}
fmt.Println(string(keyJSON))
return nil
}
func init() {
//register commands
KeyCmd.AddCommand(NewKeyCmd)
}
//---------------------------------------------
// simple implementation of a key
@@ -74,20 +45,6 @@ func (k *Key) Sign(msg []byte) crypto.Signature {
return k.PrivKey.Sign(msg)
}
// Generates a new validator with private key.
func genKey() *Key {
privKey := crypto.GenPrivKeyEd25519()
pubKey := privKey.PubKey()
addrBytes := pubKey.Address()
var addr Address
copy(addr[:], addrBytes)
return &Key{
Address: addr,
PubKey: pubKey,
PrivKey: privKey.Wrap(),
}
}
func LoadKey(keyFile string) (*Key, error) {
filePath := keyFile
-5
View File
@@ -19,11 +19,6 @@ func RegisterStartPlugin(name string, newPlugin func() types.Plugin) {
plugins = append(plugins, plugin{name: name, newPlugin: newPlugin})
}
// Register a subcommand of QueryCmd for plugin specific query functionality
func RegisterQuerySubcommand(cmd *cobra.Command) {
QueryCmd.AddCommand(cmd)
}
// Register a subcommand of TxCmd to craft transactions for plugins
func RegisterTxSubcommand(cmd *cobra.Command) {
TxCmd.AddCommand(cmd)
-234
View File
@@ -1,234 +0,0 @@
package commands
import (
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/tendermint/rpc/client"
tmtypes "github.com/tendermint/tendermint/types"
)
//commands
var (
QueryCmd = &cobra.Command{
Use: "query [key]",
Short: "Query the merkle tree",
RunE: queryCmd,
}
AccountCmd = &cobra.Command{
Use: "account [address]",
Short: "Get details of an account",
RunE: accountCmd,
}
BlockCmd = &cobra.Command{
Use: "block [height]",
Short: "Get the header and commit of a block",
RunE: blockCmd,
}
VerifyCmd = &cobra.Command{
Use: "verify",
Short: "Verify the IAVL proof",
RunE: verifyCmd,
}
)
//flags
var (
nodeFlag string
proofFlag string
keyFlag string
valueFlag string
rootFlag string
)
func init() {
commonFlags := []Flag2Register{
{&nodeFlag, "node", "tcp://localhost:46657", "Tendermint RPC address"},
}
verifyFlags := []Flag2Register{
{&proofFlag, "proof", "", "hex-encoded IAVL proof"},
{&keyFlag, "key", "", "key to the IAVL tree"},
{&valueFlag, "value", "", "value in the IAVL tree"},
{&rootFlag, "root", "", "root hash of the IAVL tree"},
}
RegisterFlags(QueryCmd, commonFlags)
RegisterFlags(AccountCmd, commonFlags)
RegisterFlags(BlockCmd, commonFlags)
RegisterFlags(VerifyCmd, verifyFlags)
}
func queryCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("query command requires an argument ([key])") //never stack trace
}
keyString := args[0]
key := []byte(keyString)
if isHex(keyString) {
// convert key to bytes
var err error
key, err = hex.DecodeString(StripHex(keyString))
if err != nil {
return errors.Errorf("Query key (%v) is invalid hex: %v\n", keyString, err)
}
}
resp, err := Query(nodeFlag, key)
if err != nil {
return errors.Errorf("Query returns error: %v\n", err)
}
if !resp.Code.IsOK() {
return errors.Errorf("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log)
}
val := resp.Value
proof := resp.Proof
height := resp.Height
out, err := json.Marshal(struct {
Value data.Bytes `json:"value"`
Proof data.Bytes `json:"proof"`
Height uint64 `json:"height"`
}{val, proof, height})
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
func accountCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("account command requires an argument ([address])") //never stack trace
}
addrHex := StripHex(args[0])
// convert destination address to bytes
addr, err := hex.DecodeString(addrHex)
if err != nil {
return errors.Errorf("Account address (%v) is invalid hex: %v\n", addrHex, err)
}
httpClient := client.NewHTTP(nodeFlag, "/websocket")
acc, err := getAccWithClient(httpClient, addr)
if err != nil {
return err
}
out, err := json.Marshal(acc)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
func blockCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("block command requires an argument ([height])") //never stack trace
}
heightString := args[0]
height, err := strconv.Atoi(heightString)
if err != nil {
return errors.Errorf("Height must be an int, got %v: %v\n", heightString, err)
}
header, commit, err := getHeaderAndCommit(nodeFlag, height)
if err != nil {
return err
}
out, err := json.Marshal(struct {
Hex BlockHex `json:"hex"`
JSON BlockJSON `json:"json"`
}{
BlockHex{
Header: wire.BinaryBytes(header),
Commit: wire.BinaryBytes(commit),
},
BlockJSON{
Header: header,
Commit: commit,
},
})
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
type BlockHex struct {
Header data.Bytes `json:"header"`
Commit data.Bytes `json:"commit"`
}
type BlockJSON struct {
Header *tmtypes.Header `json:"header"`
Commit *tmtypes.Commit `json:"commit"`
}
func verifyCmd(cmd *cobra.Command, args []string) error {
keyString, valueString := keyFlag, valueFlag
var err error
key := []byte(keyString)
if isHex(keyString) {
key, err = hex.DecodeString(StripHex(keyString))
if err != nil {
return errors.Errorf("Key (%v) is invalid hex: %v\n", keyString, err)
}
}
value := []byte(valueString)
if isHex(valueString) {
value, err = hex.DecodeString(StripHex(valueString))
if err != nil {
return errors.Errorf("Value (%v) is invalid hex: %v\n", valueString, err)
}
}
root, err := hex.DecodeString(StripHex(rootFlag))
if err != nil {
return errors.Errorf("Root (%v) is invalid hex: %v\n", rootFlag, err)
}
proofBytes, err := hex.DecodeString(StripHex(proofFlag))
if err != nil {
return errors.Errorf("Proof (%v) is invalid hex: %v\n", proofFlag, err)
}
proof, err := iavl.ReadProof(proofBytes)
if err != nil {
return errors.Errorf("Error unmarshalling proof: %v\n", err)
}
if proof.Verify(key, value, root) {
fmt.Println("OK")
} else {
return errors.New("Proof does not verify")
}
return nil
}
-118
View File
@@ -1,9 +1,7 @@
package commands
import (
"encoding/hex"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -20,18 +18,6 @@ var (
Use: "tx",
Short: "Create, sign, and broadcast a transaction",
}
SendTxCmd = &cobra.Command{
Use: "send",
Short: "A SendTx transaction, for sending tokens around",
RunE: sendTxCmd,
}
AppTxCmd = &cobra.Command{
Use: "app",
Short: "An AppTx transaction, for sending raw data to plugins",
RunE: appTxCmd,
}
)
var (
@@ -43,11 +29,6 @@ var (
gasFlag int
feeFlag string
chainIDFlag string
//non-persistent flags
toFlag string
dataFlag string
nameFlag string
)
func init() {
@@ -62,106 +43,7 @@ func init() {
{&feeFlag, "fee", "0coin", "Coins for the transaction fee of the format <amt><coin>"},
{&seqFlag, "sequence", -1, "Sequence number for the account (-1 to autocalculate)"},
}
sendTxFlags := []Flag2Register{
{&toFlag, "to", "", "Destination address for the transaction"},
}
appTxFlags := []Flag2Register{
{&nameFlag, "name", "", "Plugin to send the transaction to"},
{&dataFlag, "data", "", "Data to send with the transaction"},
}
RegisterPersistentFlags(TxCmd, cmdTxFlags)
RegisterFlags(SendTxCmd, sendTxFlags)
RegisterFlags(AppTxCmd, appTxFlags)
//register commands
TxCmd.AddCommand(SendTxCmd, AppTxCmd)
}
func sendTxCmd(cmd *cobra.Command, args []string) error {
var toHex string
var chainPrefix string
spl := strings.Split(toFlag, "/")
switch len(spl) {
case 1:
toHex = spl[0]
case 2:
chainPrefix = spl[0]
toHex = spl[1]
default:
return errors.Errorf("To address has too many slashes")
}
// convert destination address to bytes
to, err := hex.DecodeString(StripHex(toHex))
if err != nil {
return errors.Errorf("To address is invalid hex: %v\n", err)
}
if chainPrefix != "" {
to = []byte(chainPrefix + "/" + string(to))
}
// load the priv key
privKey, err := LoadKey(fromFlag)
if err != nil {
return err
}
// get the sequence number for the tx
sequence, err := getSeq(privKey.Address[:])
if err != nil {
return err
}
//parse the fee and amounts into coin types
feeCoin, err := types.ParseCoin(feeFlag)
if err != nil {
return err
}
amountCoins, err := types.ParseCoins(amountFlag)
if err != nil {
return err
}
// craft the tx
input := types.NewTxInput(privKey.PubKey, amountCoins, sequence)
output := newOutput(to, amountCoins)
tx := &types.SendTx{
Gas: int64(gasFlag),
Fee: feeCoin,
Inputs: []types.TxInput{input},
Outputs: []types.TxOutput{output},
}
// sign that puppy
signBytes := tx.SignBytes(chainIDFlag)
tx.Inputs[0].Signature = privKey.Sign(signBytes)
out := wire.BinaryBytes(tx)
fmt.Println("Signed SendTx:")
fmt.Printf("%X\n", out)
// broadcast the transaction to tendermint
data, log, err := broadcastTx(tx)
if err != nil {
return err
}
fmt.Printf("Response: %X ; %s\n", data, log)
return nil
}
func appTxCmd(cmd *cobra.Command, args []string) error {
// convert data to bytes
data := []byte(dataFlag)
if isHex(dataFlag) {
data, _ = hex.DecodeString(dataFlag)
}
name := nameFlag
return AppTx(name, data)
}
func AppTx(name string, data []byte) error {
-5
View File
@@ -19,11 +19,6 @@ func main() {
commands.InitCmd,
commands.StartCmd,
commands.TxCmd,
commands.QueryCmd,
commands.KeyCmd,
commands.VerifyCmd,
commands.BlockCmd,
commands.AccountCmd,
commands.UnsafeResetAllCmd,
commands.VersionCmd,
)
+83
View File
@@ -0,0 +1,83 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
wire "github.com/tendermint/go-wire"
txcmd "github.com/tendermint/light-client/commands/txs"
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
"github.com/tendermint/basecoin/plugins/counter"
btypes "github.com/tendermint/basecoin/types"
)
//CounterTxCmd is the CLI command to execute the counter
// through the appTx Command
var CounterTxCmd = &cobra.Command{
Use: "counter",
Short: "add a vote to the counter",
Long: `Add a vote to the counter.
You must pass --valid for it to count and the countfee will be added to the counter.`,
RunE: counterTxCmd,
}
const (
flagCountFee = "countfee"
flagValid = "valid"
)
func init() {
fs := CounterTxCmd.Flags()
bcmd.AddAppTxFlags(fs)
fs.String(flagCountFee, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.Bool(flagValid, false, "Is count valid?")
}
func counterTxCmd(cmd *cobra.Command, args []string) error {
// Note: we don't support loading apptx from json currently, so skip that
// Read the app-specific flags
name, data, err := getAppData()
if err != nil {
return err
}
// Read the standard app-tx flags
gas, fee, txInput, err := bcmd.ReadAppTxFlags()
if err != nil {
return err
}
// Create AppTx and broadcast
tx := &btypes.AppTx{
Gas: gas,
Fee: fee,
Name: name,
Input: txInput,
Data: data,
}
res, err := bcmd.BroadcastAppTx(tx)
if err != nil {
return err
}
// Output result
return txcmd.OutputTx(res)
}
func getAppData() (name string, data []byte, err error) {
countFee, err := btypes.ParseCoins(viper.GetString(flagCountFee))
if err != nil {
return
}
ctx := counter.CounterTx{
Valid: viper.GetBool(flagValid),
Fee: countFee,
}
name = counter.New().Name()
data = wire.BinaryBytes(ctx)
return
}
+28
View File
@@ -0,0 +1,28 @@
package commands
import (
"github.com/spf13/cobra"
proofcmd "github.com/tendermint/light-client/commands/proofs"
"github.com/tendermint/basecoin/plugins/counter"
)
//CounterQueryCmd CLI command to query the counter state
var CounterQueryCmd = &cobra.Command{
Use: "counter",
Short: "Query counter state, with proof",
RunE: counterQueryCmd,
}
func counterQueryCmd(cmd *cobra.Command, args []string) error {
key := counter.New().StateKey()
var cp counter.CounterPluginState
proof, err := proofcmd.GetAndParseAppProof(key, &cp)
if err != nil {
return err
}
return proofcmd.OutputProof(cp, proof.BlockHeight())
}
+64
View File
@@ -0,0 +1,64 @@
package main
import (
"os"
"github.com/spf13/cobra"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/light-client/commands"
"github.com/tendermint/light-client/commands/proofs"
"github.com/tendermint/light-client/commands/proxy"
"github.com/tendermint/light-client/commands/seeds"
"github.com/tendermint/light-client/commands/txs"
"github.com/tendermint/tmlibs/cli"
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
bcount "github.com/tendermint/basecoin/cmd/countercli/commands"
)
// BaseCli represents the base command when called without any subcommands
var BaseCli = &cobra.Command{
Use: "basecli",
Short: "Light client for tendermint",
Long: `Basecli is an version of tmcli including custom logic to
present a nice (not raw hex) interface to the basecoin blockchain structure.
This is a useful tool, but also serves to demonstrate how one can configure
tmcli to work for any custom abci app.
`,
}
func main() {
commands.AddBasicFlags(BaseCli)
// Prepare queries
pr := proofs.RootCmd
// These are default parsers, but you optional in your app
pr.AddCommand(proofs.TxCmd)
pr.AddCommand(proofs.KeyCmd)
pr.AddCommand(bcmd.AccountQueryCmd)
// IMPORTANT: here is how you add custom query commands in your app
pr.AddCommand(bcount.CounterQueryCmd)
proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{})
tr := txs.RootCmd
tr.AddCommand(bcmd.SendTxCmd)
// IMPORTANT: here is how you add custom tx construction for your app
tr.AddCommand(bcount.CounterTxCmd)
// Set up the various commands to use
BaseCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
keycmd.RootCmd,
seeds.RootCmd,
pr,
tr,
proxy.RootCmd)
cmd := cli.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli"))
cmd.Execute()
}
-10
View File
@@ -1,10 +0,0 @@
#! /bin/bash
killall -9 basecoin tendermint
TMHOME=./data/chain1 tendermint unsafe_reset_all
TMHOME=./data/chain2 tendermint unsafe_reset_all
rm ./*.log
rm ./data/chain1/*.bak
rm ./data/chain2/*.bak
-11
View File
@@ -1,11 +0,0 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "tcp://127.0.0.1:46658"
moniker = "anonymous"
node_laddr = "tcp://0.0.0.0:46656"
seeds = ""
fast_sync = true
db_backend = "leveldb"
log_level = "info"
rpc_laddr = "tcp://0.0.0.0:46657"
-31
View File
@@ -1,31 +0,0 @@
{
"app_hash": "",
"chain_id": "test_chain_1",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"amount": 10,
"name": "",
"pub_key": {
"type": "ed25519",
"data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
}
}
],
"app_options": {
"accounts": [
{
"pub_key": {
"type": "ed25519",
"data": "B3588BDC92015ED3CDB6F57A86379E8C79A7111063610B7E625487C76496F4DF"
},
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}
]
}
}
-12
View File
@@ -1,12 +0,0 @@
{
"address": "D397BC62B435F3CF50570FBAB4340FE52C60858F",
"priv_key": {
"type": "ed25519",
"data": "39E75AA1CF7BC710585977EFC375CD1730519186BD231478C339F2819C3C26E7B3588BDC92015ED3CDB6F57A86379E8C79A7111063610B7E625487C76496F4DF"
},
"pub_key": {
"type": "ed25519",
"data": "B3588BDC92015ED3CDB6F57A86379E8C79A7111063610B7E625487C76496F4DF"
}
}
-1
View File
@@ -1 +0,0 @@
{"address":"EBB0B4A899973C524A6BB18A161056A55F590F41","pub_key":{"type":"ed25519","data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"},"last_height":0,"last_round":0,"last_step":0,"last_signature":null,"priv_key":{"type":"ed25519","data":"5FFDC1EA5FA2CA4A0A5503C86D2D348C5B401AD80FAA1899508F1ED00D8982E8D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"}}
-11
View File
@@ -1,11 +0,0 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "tcp://127.0.0.1:46658"
moniker = "anonymous"
node_laddr = "tcp://0.0.0.0:46656"
seeds = ""
fast_sync = true
db_backend = "leveldb"
log_level = "info"
rpc_laddr = "tcp://0.0.0.0:46657"
-31
View File
@@ -1,31 +0,0 @@
{
"app_hash": "",
"chain_id": "test_chain_2",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"amount": 10,
"name": "",
"pub_key": {
"type": "ed25519",
"data": "9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
}
}
],
"app_options": {
"accounts": [
{
"pub_key": {
"type": "ed25519",
"data": "0628C8E6C2D50B15764B443394E06C6A64F3082CE966A2A8C1A55A4D63D0FC5D"
},
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}
]
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"address": "053BA0F19616AFF975C8756A2CBFF04F408B4D47",
"priv_key": {
"type": "ed25519",
"data": "22920C428043D869987F253D7C9B2305E7010642C40CE88A52C9F6CE5ACC42080628C8E6C2D50B15764B443394E06C6A64F3082CE966A2A8C1A55A4D63D0FC5D"
},
"pub_key": {
"type": "ed25519",
"data": "0628C8E6C2D50B15764B443394E06C6A64F3082CE966A2A8C1A55A4D63D0FC5D"
}
}
-1
View File
@@ -1 +0,0 @@
{"address":"D42CFCB9C42DF9A73143EEA89255D1DF027B6240","pub_key":{"type":"ed25519","data":"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"},"last_height":0,"last_round":0,"last_step":0,"last_signature":null,"priv_key":{"type":"ed25519","data":"6353FAF4ADEB03EA496A9EAE5BE56C4C6A851CB705401788184FDC9198413C2C9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"}}
-198
View File
@@ -1,198 +0,0 @@
#! /bin/bash
set -e
cd $GOPATH/src/github.com/tendermint/basecoin/demo
LOG_DIR="."
TM_VERSION="master"
#TM_VERSION="v0.10.0"
if [[ "$CIRCLECI" == "true" ]]; then
# set log dir
LOG_DIR="${CIRCLE_ARTIFACTS}"
# install tendermint
set +e
go get github.com/tendermint/tendermint
pushd $GOPATH/src/github.com/tendermint/tendermint
git checkout $TM_VERSION
glide install
go install ./cmd/tendermint
popd
set -e
fi
set -u
function ifExit() {
if [[ "$?" != 0 ]]; then
echo "FAIL"
exit 1
fi
}
function removeQuotes() {
temp="${1%\"}"
temp="${temp#\"}"
echo "$temp"
}
function waitForNode() {
addr=$1
set +e
curl -s $addr/status > /dev/null
ERR=$?
i=0
while [ "$ERR" != 0 ]; do
if [[ "$i" == 10 ]]; then
echo "waited too long for chain to start"
exit 1
fi
echo "...... still waiting on $addr"
sleep 1
curl -s $addr/status > /dev/null
ERR=$?
i=$((i+1))
done
set -e
echo "... node $addr is up"
}
function waitForBlock() {
addr=$1
b1=`curl -s $addr/status | jq .result.latest_block_height`
b2=$b1
while [ "$b2" == "$b1" ]; do
echo "Waiting for node $addr to commit a block ..."
sleep 1
b2=`curl -s $addr/status | jq .result.latest_block_height`
done
}
# make basecoin root vars
export BCHOME="."
BCHOME1="./data/chain1"
BCHOME2="./data/chain2"
# grab the chain ids
CHAIN_ID1=$(cat $BCHOME1/genesis.json | jq .chain_id)
CHAIN_ID1=$(removeQuotes $CHAIN_ID1)
CHAIN_ID2=$(cat $BCHOME2/genesis.json | jq .chain_id)
CHAIN_ID2=$(removeQuotes $CHAIN_ID2)
echo "CHAIN_ID1: $CHAIN_ID1"
echo "CHAIN_ID2: $CHAIN_ID2"
# make reusable chain flags
CHAIN_FLAGS1="--chain_id $CHAIN_ID1 --from $BCHOME1/key.json"
CHAIN_FLAGS2="--chain_id $CHAIN_ID2 --from $BCHOME2/key.json --node tcp://localhost:36657"
echo ""
echo "... starting chains"
echo ""
# start the first node
TMHOME=$BCHOME1 tendermint node --p2p.skip_upnp --log_level=info &> $LOG_DIR/chain1_tendermint.log &
ifExit
BCHOME=$BCHOME1 basecoin start --without-tendermint &> $LOG_DIR/chain1_basecoin.log &
ifExit
# start the second node
TMHOME=$BCHOME2 tendermint node --p2p.skip_upnp --log_level=info --p2p.laddr tcp://localhost:36656 --rpc.laddr tcp://localhost:36657 --proxy_app tcp://localhost:36658 &> $LOG_DIR/chain2_tendermint.log &
ifExit
BCHOME=$BCHOME2 basecoin start --address tcp://localhost:36658 --without-tendermint &> $LOG_DIR/chain2_basecoin.log &
ifExit
echo ""
echo "... waiting for chains to start"
echo ""
waitForNode localhost:46657
waitForNode localhost:36657
# TODO: remove the sleep
# Without it we sometimes get "Account bytes are empty for address: 053BA0F19616AFF975C8756A2CBFF04F408B4D47"
sleep 3
echo "... registering chain1 on chain2"
echo ""
# register chain1 on chain2
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis $BCHOME1/genesis.json
ifExit
echo ""
echo "... creating egress packet on chain1"
echo ""
# send coins from chain1 to an address on chain2
# TODO: dont hardcode the address
basecoin tx send --amount 10mycoin $CHAIN_FLAGS1 --to $CHAIN_ID2/053BA0F19616AFF975C8756A2CBFF04F408B4D47
ifExit
# alternative way to create packets (for testing)
# basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 0
echo ""
echo "... querying for packet data"
echo ""
# query for the packet data and proof
# since we only sent one packet, the sequence number is 0
QUERY_RESULT=$(basecoin query ibc,egress,$CHAIN_ID1,$CHAIN_ID2,0)
ifExit
HEIGHT=$(echo $QUERY_RESULT | jq .height)
PACKET=$(echo $QUERY_RESULT | jq .value)
PROOF=$(echo $QUERY_RESULT | jq .proof)
PACKET=$(removeQuotes $PACKET)
PROOF=$(removeQuotes $PROOF)
echo ""
echo "QUERY_RESULT: $QUERY_RESULT"
echo "HEIGHT: $HEIGHT"
echo "PACKET: $PACKET"
echo "PROOF: $PROOF"
# the query returns the height of the next block, which contains the app hash
# but which may not be committed yet, so we have to wait for it to query the commit
echo ""
echo "... waiting for a block to be committed"
echo ""
waitForBlock localhost:46657
waitForBlock localhost:36657
echo ""
echo "... querying for block data"
echo ""
# get the header and commit for the height
HEADER_AND_COMMIT=$(basecoin block $HEIGHT)
ifExit
HEADER=$(echo $HEADER_AND_COMMIT | jq .hex.header)
HEADER=$(removeQuotes $HEADER)
COMMIT=$(echo $HEADER_AND_COMMIT | jq .hex.commit)
COMMIT=$(removeQuotes $COMMIT)
echo ""
echo "HEADER_AND_COMMIT: $HEADER_AND_COMMIT"
echo "HEADER: $HEADER"
echo "COMMIT: $COMMIT"
echo ""
echo "... updating state of chain1 on chain2"
echo ""
# update the state of chain1 on chain2
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x$HEADER --commit 0x$COMMIT
ifExit
echo ""
echo "... posting packet from chain1 on chain2"
echo ""
# post the packet from chain1 to chain2
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF
ifExit
echo ""
echo "... checking if the packet is present on chain2"
echo ""
# query for the packet on chain2
basecoin query --node tcp://localhost:36657 ibc,ingress,test_chain_2,test_chain_1,0
ifExit
echo ""
echo "DONE!"
-62
View File
@@ -1,62 +0,0 @@
package main
import (
"github.com/spf13/cobra"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/basecoin/cmd/commands"
"github.com/tendermint/basecoin/types"
)
var (
//CLI Flags
validFlag bool
//CLI Plugin Commands
ExamplePluginTxCmd = &cobra.Command{
Use: "example",
Short: "Create, sign, and broadcast a transaction to the example plugin",
RunE: examplePluginTxCmd,
}
)
//Called during CLI initialization
func init() {
//Set the Plugin Flags
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
//Register a plugin specific CLI command as a subcommand of the tx command
commands.RegisterTxSubcommand(ExamplePluginTxCmd)
//Register the example with basecoin at start
commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() })
}
//Send a transaction
func examplePluginTxCmd(cmd *cobra.Command, args []string) error {
// Create a transaction using the flag.
// The tx passes on custom information to the plugin
exampleTx := ExamplePluginTx{validFlag}
// The tx is passed to the plugin in the form of
// a byte array. This is achieved by serializing the object using go-wire.
// Once received in the plugin, these exampleTxBytes are decoded back
// into the original ExamplePluginTx struct
exampleTxBytes := wire.BinaryBytes(exampleTx)
// Send the transaction and return any errors.
// Here exampleTxBytes is packaged in the `tx.Data` field of an AppTx,
// and passed on to the plugin through the following sequence:
// - passed as `data` to `commands.AppTx` (cmd/commands/tx.go)
// - set as the `tx.Data` field of an AppTx, which is then passed to commands.broadcastTx (cmd/commands/tx.go)
// - the tx is broadcast to Tendermint, which runs it through app.CheckTx (app/app.go)
// - after passing CheckTx, it will eventually be included in a block and run through app.DeliverTx (app/app.go)
// - DeliverTx receives txBytes, which is the serialization of the full AppTx (app/app.go)
// - Once deserialized, the tx is passed to `state.ExecTx` (state/execution.go)
// - If the tx passes various checks, the `tx.Data` is forwarded as `txBytes` to `plugin.RunTx` (docs/guide/src/example-plugin/plugin.go)
// - Finally, it deserialized back to the ExamplePluginTx
return commands.AppTx("example-plugin", exampleTxBytes)
}
-35
View File
@@ -1,35 +0,0 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/basecoin/cmd/commands"
"github.com/tendermint/tmlibs/cli"
)
func main() {
//Initialize example-plugin root command
var RootCmd = &cobra.Command{
Use: "example-plugin",
Short: "example-plugin usage description",
}
//Add the default basecoin commands to the root command
RootCmd.AddCommand(
commands.InitCmd,
commands.StartCmd,
commands.TxCmd,
commands.QueryCmd,
commands.KeyCmd,
commands.VerifyCmd,
commands.BlockCmd,
commands.AccountCmd,
commands.UnsafeResetAllCmd,
)
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin-example-plugin"))
cmd.Execute()
}
-134
View File
@@ -1,134 +0,0 @@
package main
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/basecoin/types"
"github.com/tendermint/go-wire"
)
//-----------------------------------------
// Structs
// * Note the fields in each struct may be expanded/modified
// Plugin State Struct
// * Intended to store the current state of the plugin
// * This example contains a field which holds the execution count
// * Serialized (by go-wire) and stored within the KVStore using the key retrieved
// from the ExamplePlugin.StateKey() function/
// * All fields must be exposed for serialization by external libs (here go-wire)
type ExamplePluginState struct {
Counter int
}
// Transaction Struct
// * Stores transaction-specific plugin-customized information
// * This example contains a dummy field 'Valid' intended to specify
// if the transaction is a valid and should proceed
// * Deserialized (by go-wire) from txBytes in ExamplePlugin.RunTx
// * All fields must be exposed for serialization by external libs (here go-wire)
type ExamplePluginTx struct {
Valid bool
}
// Plugin Struct
// * Struct which satisfies the basecoin Plugin interface
// * Stores global plugin settings, in this example just the plugin name
type ExamplePlugin struct {
name string
}
//-----------------------------------------
// Non-Mandatory Functions
// Return a new ExamplePlugin pointer with a hard-coded name
func NewExamplePlugin() *ExamplePlugin {
return &ExamplePlugin{
name: "example-plugin",
}
}
// Return a byte array unique to this plugin which is used as the key
// to store the plugin state (ExamplePluginState)
func (ep *ExamplePlugin) StateKey() []byte {
return []byte("ExamplePlugin.State")
}
//-----------------------------------------
// Basecoin Plugin Interface Functions
//Return the name of the plugin
func (ep *ExamplePlugin) Name() string {
return ep.name
}
// SetOption may be called during genesis of basecoin and can be used to set
// initial plugin parameters. Within genesis.json file entries are made in
// the format: "<plugin>/<key>", "<value>" Where <plugin> is the plugin name,
// in this file ExamplePlugin.name, and <key> and <value> are the strings passed
// into the plugin SetOption function. This function is intended to be used to
// set plugin specific information such as the plugin state. Within this example
// SetOption is left unimplemented.
func (ep *ExamplePlugin) SetOption(store types.KVStore, key string, value string) (log string) {
return ""
}
// The core tx logic of the app is containted within the RunTx function
// Input fields:
// - store types.KVStore
// - This term provides read/write capabilities to the merkelized data store
// which holds the basecoin state and is accessible to all plugins
// - ctx types.CallContext
// - The ctx contains the callers address, a pointer to the callers account,
// and an amount of coins sent with the transaction
// - txBytes []byte
// - Used to send customized information to your plugin
//
// Other more complex plugins may have a variant on the process order within this
// example including loading and saving multiple or variable states, or not
// including a state stored in the KVStore whatsoever.
func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
// Decode txBytes using go-wire. Attempt to write the txBytes to the variable
// tx, if the txBytes have not been properly encoded from a ExamplePluginTx
// struct wire will produce an error.
var tx ExamplePluginTx
err := wire.ReadBinaryBytes(txBytes, &tx)
if err != nil {
return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
}
// Perform Transaction Validation
if !tx.Valid {
return abci.ErrInternalError.AppendLog("Valid must be true")
}
// Load PluginState
var pluginState ExamplePluginState
stateBytes := store.Get(ep.StateKey())
// If the state does not exist, stateBytes will be initialized
// as an empty byte array with length of zero
if len(stateBytes) > 0 {
err = wire.ReadBinaryBytes(stateBytes, &pluginState) //decode using go-wire
if err != nil {
return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error())
}
}
//App Logic
pluginState.Counter += 1
// Save PluginState
store.Set(ep.StateKey(), wire.BinaryBytes(pluginState))
return abci.OK
}
func (ep *ExamplePlugin) InitChain(store types.KVStore, vals []*abci.Validator) {
}
func (ep *ExamplePlugin) BeginBlock(store types.KVStore, hash []byte, header *abci.Header) {
}
func (ep *ExamplePlugin) EndBlock(store types.KVStore, height uint64) abci.ResponseEndBlock {
return abci.ResponseEndBlock{}
}
+78
View File
@@ -0,0 +1,78 @@
#!/bin/bash
# these are two globals to control all scripts (can use eg. counter instead)
SERVER_EXE=basecoin
CLIENT_EXE=basecli
oneTimeSetUp() {
# these are passed in as args
BASE_DIR=$HOME/.basecoin_test_basictx
CHAIN_ID=my-test-chain
rm -rf $BASE_DIR 2>/dev/null
mkdir -p $BASE_DIR
# set up client - make sure you use the proper prefix if you set
# a custom CLIENT_EXE
export BC_HOME=${BASE_DIR}/client
prepareClient
# start basecoin server (with counter)
initServer $BASE_DIR $CHAIN_ID 3456
PID_SERVER=$!
initClient $CHAIN_ID 34567
echo "...Testing may begin!"
echo
echo
echo
}
oneTimeTearDown() {
echo
echo
echo "stopping $SERVER_EXE test server..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "requires arg" "${CLIENT_EXE} query account"
checkAccount $SENDER "0" "9007199254740992"
ACCT2=$(${CLIENT_EXE} query account $RECV)
assertFalse "has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "missing dest" "${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 2>/dev/null"
assertFalse "bad password" "echo foo | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null"
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null)
txSucceeded $? "$RES"
TX=`echo $RES | cut -d: -f2-`
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "1" "9007199254740000"
checkAccount $RECV "0" "992"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# load common helpers
. $DIR/common.sh
. $DIR/shunit2
+122
View File
@@ -0,0 +1,122 @@
# this is not executable, but helper functions for the other scripts
# these are general accounts to be prepared
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
prepareClient() {
echo "Preparing client keys..."
${CLIENT_EXE} reset_all
assertTrue $?
for i in "${!ACCOUNTS[@]}"; do
newKey ${ACCOUNTS[$i]}
done
}
# initServer takes two args - (root dir, chain_id)
# and optionally port prefix as a third arg (default is 4665{6,7,8})
# it grabs the first account to give it the money
initServer() {
echo "Setting up genesis..."
SERVE_DIR=$1/server
assertNotNull "no chain" $2
CHAIN=$2
SERVER_LOG=$1/${SERVER_EXE}.log
${SERVER_EXE} init --home=$SERVE_DIR >>$SERVER_LOG
#change the genesis to the first account
GENKEY=$(${CLIENT_EXE} keys get ${RICH} -o json | jq .pubkey.data)
GENJSON=$(cat $SERVE_DIR/genesis.json)
echo $GENJSON | jq '.app_options.accounts[0].pub_key.data='$GENKEY \
| jq ".chain_id=\"$2\"" > $SERVE_DIR/genesis.json
# optionally set the port
if [ -n "$3" ]; then
echo "setting port $3"
sed -ie "s/4665/$3/" $SERVE_DIR/config.toml
fi
echo "Starting ${SERVER_EXE} server..."
${SERVER_EXE} start --home=$SERVE_DIR >>$SERVER_LOG 2>&1 &
sleep 5
}
# initClient requires chain_id arg, port is optional (default 46657)
initClient() {
echo "Attaching ${CLIENT_EXE} client..."
PORT=${2:-46657}
# hard-code the expected validator hash
${CLIENT_EXE} init --chain-id=$1 --node=tcp://localhost:${PORT} --valhash=EB168E17E45BAEB194D4C79067FFECF345C64DE6
assertTrue "initialized light-client" $?
}
# newKeys makes a key for a given username, second arg optional password
newKey(){
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
(echo $KEYPASS; echo $KEYPASS) | ${CLIENT_EXE} keys new $1 >/dev/null 2>/dev/null
assertTrue "created $1" $?
assertTrue "$1 doesn't exist" "${CLIENT_EXE} keys get $1"
}
# getAddr gets the address for a key name
getAddr() {
assertNotNull "keyname required" "$1"
RAW=$(${CLIENT_EXE} keys get $1)
assertTrue "no key for $1" $?
# print the addr
echo $RAW | cut -d' ' -f2
}
# checkAccount $ADDR $SEQUENCE $BALANCE
# assumes just one coin, checks the balance of first coin in any case
checkAccount() {
# make sure sender goes down
ACCT=$(${CLIENT_EXE} query account $1)
assertTrue "must have genesis account" $?
assertEquals "proper sequence" "$2" $(echo $ACCT | jq .data.sequence)
assertEquals "proper money" "$3" $(echo $ACCT | jq .data.coins[0].amount)
return $?
}
# txSucceeded $? "$RES"
# must be called right after the `tx` command, makes sure it got a success response
txSucceeded() {
if (assertTrue "sent tx: $2" $1); then
TX=`echo $2 | cut -d: -f2-` # strip off first line asking for password
assertEquals "good check: $TX" "0" $(echo $TX | jq .check_tx.code)
assertEquals "good deliver: $TX" "0" $(echo $TX | jq .deliver_tx.code)
else
return 1
fi
}
# checkSendTx $HASH $HEIGHT $SENDER $AMOUNT
# this looks up the tx by hash, and makes sure the height and type match
# and that the first input was from this sender for this amount
checkSendTx() {
TX=$(${CLIENT_EXE} query tx $1)
assertTrue "found tx" $?
assertEquals "proper height" $2 $(echo $TX | jq .height)
assertEquals "type=send" '"send"' $(echo $TX | jq .data.type)
assertEquals "proper sender" "\"$3\"" $(echo $TX | jq .data.data.inputs[0].address)
assertEquals "proper out amount" "$4" $(echo $TX | jq .data.data.outputs[0].coins[0].amount)
return $?
}
# waitForBlock $port
# waits until the block height on that node increases by one
waitForBlock() {
addr=http://localhost:$1
b1=`curl -s $addr/status | jq .result.latest_block_height`
b2=$b1
while [ "$b2" == "$b1" ]; do
echo "Waiting for node $addr to commit a block ..."
sleep 1
b2=`curl -s $addr/status | jq .result.latest_block_height`
done
}
+122
View File
@@ -0,0 +1,122 @@
#!/bin/bash
# these are two globals to control all scripts (can use eg. counter instead)
SERVER_EXE=counter
CLIENT_EXE=countercli
oneTimeSetUp() {
# these are passed in as args
BASE_DIR=$HOME/.basecoin_test_counter
CHAIN_ID="counter-chain"
rm -rf $BASE_DIR 2>/dev/null
mkdir -p $BASE_DIR
# set up client - make sure you use the proper prefix if you set
# a custom CLIENT_EXE
export BC_HOME=${BASE_DIR}/client
prepareClient
# start basecoin server (with counter)
initServer $BASE_DIR $CHAIN_ID 1234
PID_SERVER=$!
initClient $CHAIN_ID 12347
echo "...Testing may begin!"
echo
echo
echo
}
oneTimeTearDown() {
echo
echo
echo "stopping $SERVER_EXE test server..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "requires arg" "${CLIENT_EXE} query account"
checkAccount $SENDER "0" "9007199254740992"
ACCT2=$(${CLIENT_EXE} query account $RECV)
assertFalse "has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "missing dest" "${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 2>/dev/null"
assertFalse "bad password" "echo foo | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null"
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH 2>/dev/null)
txSucceeded $? "$RES"
TX=`echo $RES | cut -d: -f2-`
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "1" "9007199254740000"
checkAccount $RECV "0" "992"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
test02GetCounter() {
COUNT=$(${CLIENT_EXE} query counter)
assertFalse "no default count" $?
}
# checkAccount $COUNT $BALANCE
# assumes just one coin, checks the balance of first coin in any case
checkCounter() {
# make sure sender goes down
ACCT=$(${CLIENT_EXE} query counter)
assertTrue "count is set" $?
assertEquals "proper count" "$1" $(echo $ACCT | jq .data.Counter)
assertEquals "proper money" "$2" $(echo $ACCT | jq .data.TotalFees[0].amount)
}
test02AddCount() {
SENDER=$(getAddr $RICH)
assertFalse "bad password" "echo hi | ${CLIENT_EXE} tx counter --amount=1000mycoin --sequence=2 --name=${RICH} 2>/dev/null"
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx counter --amount=10mycoin --sequence=2 --name=${RICH} --valid --countfee=5mycoin 2>/dev/null)
txSucceeded $? "$RES"
TX=`echo $RES | cut -d: -f2-`
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkCounter "1" "5"
# FIXME: cannot load apptx properly.
# Look at the stack trace
# This cannot be fixed with the current ugly apptx structure...
# Leave for refactoring
# make sure tx is indexed
# echo hash $HASH
# TX=$(${CLIENT_EXE} query tx $HASH --trace)
# echo tx $TX
# if [-z assertTrue "found tx" $?]; then
# assertEquals "proper height" $TX_HEIGHT $(echo $TX | jq .height)
# assertEquals "type=app" '"app"' $(echo $TX | jq .data.type)
# assertEquals "proper sender" "\"$SENDER\"" $(echo $TX | jq .data.data.input.address)
# fi
# echo $TX
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# load common helpers
. $DIR/common.sh
. $DIR/shunit2
+184
View File
@@ -0,0 +1,184 @@
#!/bin/bash
#!/bin/bash
# these are two globals to control all scripts (can use eg. counter instead)
SERVER_EXE=basecoin
CLIENT_EXE=basecli
# just uncomment this line for full stack traces in error output
# CLIENT_EXE="basecli --trace"
oneTimeSetUp() {
# these are passed in as args
BASE_DIR_1=$HOME/.basecoin_test_ibc/chain1
CHAIN_ID_1=test-chain-1
CLIENT_1=${BASE_DIR_1}/client
PREFIX_1=1234
PORT_1=${PREFIX_1}7
BASE_DIR_2=$HOME/.basecoin_test_ibc/chain2
CHAIN_ID_2=test-chain-2
CLIENT_2=${BASE_DIR_2}/client
PREFIX_2=2345
PORT_2=${PREFIX_2}7
# clean up and create the test dirs
rm -rf $BASE_DIR_1 $BASE_DIR_2 2>/dev/null
mkdir -p $BASE_DIR_1 $BASE_DIR_2
# set up client for chain 1- make sure you use the proper prefix if you set
# a custom CLIENT_EXE
BC_HOME=${CLIENT_1} prepareClient
BC_HOME=${CLIENT_2} prepareClient
# start basecoin server, giving money to the key in the first client
BC_HOME=${CLIENT_1} initServer $BASE_DIR_1 $CHAIN_ID_1 $PREFIX_1
PID_SERVER_1=$!
# start second basecoin server, giving money to the key in the second client
BC_HOME=${CLIENT_2} initServer $BASE_DIR_2 $CHAIN_ID_2 $PREFIX_2
PID_SERVER_2=$!
# connect both clients
BC_HOME=${CLIENT_1} initClient $CHAIN_ID_1 $PORT_1
BC_HOME=${CLIENT_2} initClient $CHAIN_ID_2 $PORT_2
echo "...Testing may begin!"
echo
echo
echo
}
oneTimeTearDown() {
echo
echo
echo "stopping both $SERVER_EXE test servers... $PID_SERVER_1 $PID_SERVER_2"
kill -9 $PID_SERVER_1
kill -9 $PID_SERVER_2
sleep 1
}
test00GetAccount() {
SENDER_1=$(BC_HOME=${CLIENT_1} getAddr $RICH)
RECV_1=$(BC_HOME=${CLIENT_1} getAddr $POOR)
export BC_HOME=${CLIENT_1}
assertFalse "requires arg" "${CLIENT_EXE} query account"
assertFalse "has no genesis account" "${CLIENT_EXE} query account $RECV_1"
checkAccount $SENDER_1 "0" "9007199254740992"
export BC_HOME=${CLIENT_2}
SENDER_2=$(getAddr $RICH)
RECV_2=$(getAddr $POOR)
assertFalse "requires arg" "${CLIENT_EXE} query account"
assertFalse "has no genesis account" "${CLIENT_EXE} query account $RECV_2"
checkAccount $SENDER_2 "0" "9007199254740992"
# make sure that they have different addresses on both chains (they are random keys)
assertNotEquals "sender keys must be different" "$SENDER_1" "$SENDER_2"
assertNotEquals "recipient keys must be different" "$RECV_1" "$RECV_2"
}
test01SendIBCTx() {
# trigger a cross-chain sendTx... from RICH on chain1 to POOR on chain2
# we make sure the money was reduced, but nothing arrived
SENDER=$(BC_HOME=${CLIENT_1} getAddr $RICH)
RECV=$(BC_HOME=${CLIENT_2} getAddr $POOR)
# we have to remove the password request from stdout, to just get the json
export BC_HOME=${CLIENT_1}
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20002mycoin --sequence=1 --to=${CHAIN_ID_2}/${RECV} --name=$RICH 2>/dev/null)
txSucceeded $? "$RES"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
TX=`echo $RES | cut -d: -f2-`
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# make sure balance went down and tx is indexed
checkAccount $SENDER "1" "9007199254720990"
checkSendTx $HASH $TX_HEIGHT $SENDER "20002"
# # make sure nothing arrived - yet
waitForBlock ${PORT_1}
assertFalse "no relay running" "BC_HOME=${CLIENT_2} ${CLIENT_EXE} query account $RECV"
# start the relay and wait a few blocks...
# (already sent a tx on chain1, so use higher sequence)
startRelay 2 1
if [ $? != 0 ]; then echo "can't start relay!"; return 1; fi
# give it a little time, then make sure the money arrived
echo "waiting for relay..."
sleep 1
waitForBlock ${PORT_1}
waitForBlock ${PORT_2}
# check the new account
echo "checking ibc recipient..."
BC_HOME=${CLIENT_2} checkAccount $RECV "0" "20002"
# stop relay
kill -9 $PID_RELAY
}
# startRelay $seq1 $seq2
# startRelay hooks up a relay between chain1 and chain2
# it needs the proper sequence number for $RICH on chain1 and chain2 as args
startRelay() {
# send some cash to the default key, so it can send messages
RELAY_KEY=${BASE_DIR_1}/server/key.json
RELAY_ADDR=$(cat $RELAY_KEY | jq .address | tr -d \")
# get paid on chain1
export BC_HOME=${CLIENT_1}
SENDER=$(getAddr $RICH)
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=100000mycoin --sequence=$1 --to=$RELAY_ADDR --name=$RICH 2>/dev/null)
txSucceeded $? "$RES"
if [ $? != 0 ]; then echo "can't pay chain1!"; return 1; fi
# get paid on chain2
export BC_HOME=${CLIENT_2}
SENDER=$(getAddr $RICH)
RES=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=100000mycoin --sequence=$2 --to=$RELAY_ADDR --name=$RICH 2>/dev/null)
txSucceeded $? "$RES"
if [ $? != 0 ]; then echo "can't pay chain2!"; return 1; fi
# now we need to register the chains
# TODO: do this with basecli!!!!
basecoin tx ibc --amount 10mycoin --from=$RELAY_KEY --chain_id=$CHAIN_ID_2 \
--node=tcp://localhost:${PORT_2} \
register --ibc_chain_id=$CHAIN_ID_1 --genesis=$BASE_DIR_1/server/genesis.json \
>/dev/null
if [ $? != 0 ]; then echo "can't register chain1 on chain 2"; return 1; fi
basecoin tx ibc --amount 10mycoin --from=$RELAY_KEY --chain_id=$CHAIN_ID_1 \
--node=tcp://localhost:${PORT_1} \
register --ibc_chain_id=$CHAIN_ID_2 --genesis=$BASE_DIR_2/server/genesis.json \
>/dev/null
if [ $? != 0 ]; then echo "can't register chain2 on chain 1"; return 1; fi
# now start the relay! (this remains a server command)
# TODO: bucky, why does this die if I don't provide home???
# It doesn't use the --from flag????
${SERVER_EXE} relay --chain1-id=$CHAIN_ID_1 --chain2-id=$CHAIN_ID_2 \
--chain1-addr=tcp://localhost:${PORT_1} --chain2-addr=tcp://localhost:${PORT_2} \
--home=${BASE_DIR_1}/server --from=$RELAY_KEY > ${BASE_DIR_1}/../relay.log &
PID_RELAY=$!
echo starting relay $PID_RELAY ...
# return an error if it dies in the first two seconds to make sure it is running
sleep 2
ps $PID_RELAY >/dev/null
return $?
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# load common helpers
. $DIR/common.sh
. $DIR/shunit2