Moved basecoin into examples

This commit is contained in:
Ethan Frey
2017-09-04 16:50:09 +02:00
parent b274494474
commit 96f96ffc3d
26 changed files with 70 additions and 26 deletions
+21
View File
@@ -0,0 +1,21 @@
LINKER_FLAGS:="-X github.com/cosmos/cosmos-sdk/client/commands.CommitHash=`git rev-parse --short HEAD`"
install:
@go install -ldflags $(LINKER_FLAGS) ./cmd/...
test: test_unit test_cli
test_unit:
@go test `glide novendor`
test_cli:
./tests/cli/keys.sh
./tests/cli/rpc.sh
./tests/cli/init.sh
./tests/cli/basictx.sh
./tests/cli/roles.sh
./tests/cli/restart.sh
./tests/cli/rest.sh
./tests/cli/ibc.sh
.PHONY: install test test_unit test_cli
@@ -0,0 +1,58 @@
# Run your own (super) lightweight node
In addition to providing command-line tooling that goes cryptographic verification
on all the data your receive from the node, we have implemented a proxy mode, that
allows you to run a super lightweight node. It does not follow the chain on
every block or even every header, but only as needed. But still providing the
same security as running a full non-validator node on your local machine.
Basically, it runs as a proxy that exposes the same rpc interface as the full node
and connects to a (potentially untrusted) full node. Every response is cryptographically
verified before being passed through, returning an error if it doesn't match.
You can expect 2 rpc calls for every query plus <= 1 query for each validator set
change. Going offline for a while allows you to verify multiple validator set changes
with one call. Cuz at 1 block/sec and 1000 tx/block, it just doesn't make sense
to run a full node just to get security
## Setup
Just initialize your client with the proper validator set as in the [README](README.md)
```
$ export BCHOME=~/.lightnode
$ basecli init --node tcp://<host>:<port> --chain-id <chain>
```
## Running
```
$ basecli proxy --serve tcp://localhost:7890
...
curl localhost:7890/status
curl localhost:7890/block\?height=20
```
You can even subscribe to events over websockets and they are all verified
before passing them though. Though if you want every block, you might as
well run a full (nonvalidating) node.
## Seeds
Every time the validator set changes, the light node verifies if it is legal,
and then creates a seed at that point. These "seeds" are verified checkpoints
that we can trace any proof back to, starting with one on `init`.
To make sure you are based on the most recent header, you can run:
```
basecli seeds update
basecli seeds show
```
## Feedback
This is the first release of basecli and the light-weight proxy. It is secure, but
may not be useful for your workflow. Please try it out and open github issues
for any enhancements or bugs you find. I am aiming to make this a very useful
tool by tendermint 0.11, for which I need community feedback.
+53
View File
@@ -0,0 +1,53 @@
# Basic run through of using basecli....
To keep things clear, let's have two shells...
`$` is for basecoin (server), `%` is for basecli (client)
## Set up your basecli with a new key
```
% export BCHOME=~/.democli
% basecli keys new demo
% basecli keys get demo -o json
```
And set up a few more keys for fun...
```
% basecli keys new buddy
% basecli keys list
% ME=$(basecli keys get demo | awk '{print $2}')
% YOU=$(basecli keys get buddy | awk '{print $2}')
```
## Set up a clean basecoin, initialized with your account
```
$ export BCHOME=~/.demoserve
$ basecoin init $ME
$ basecoin start
```
## Connect your basecli the first time
```
% basecli init --chain-id test_chain_id --node tcp://localhost:46657
```
## Check your balances...
```
% basecli query account $ME
% basecli query account $YOU
```
## Send the money
```
% basecli tx send --name demo --amount 1000mycoin --sequence 1 --to $YOU
-> copy hash to HASH
% basecli query tx $HASH
% basecli query account $YOU
```
+91
View File
@@ -0,0 +1,91 @@
package main
import (
"os"
"github.com/spf13/cobra"
keycmd "github.com/tendermint/go-crypto/cmd"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/auto"
"github.com/cosmos/cosmos-sdk/client/commands/proxy"
"github.com/cosmos/cosmos-sdk/client/commands/query"
rpccmd "github.com/cosmos/cosmos-sdk/client/commands/rpc"
"github.com/cosmos/cosmos-sdk/client/commands/seeds"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
authcmd "github.com/cosmos/cosmos-sdk/modules/auth/commands"
basecmd "github.com/cosmos/cosmos-sdk/modules/base/commands"
coincmd "github.com/cosmos/cosmos-sdk/modules/coin/commands"
feecmd "github.com/cosmos/cosmos-sdk/modules/fee/commands"
ibccmd "github.com/cosmos/cosmos-sdk/modules/ibc/commands"
noncecmd "github.com/cosmos/cosmos-sdk/modules/nonce/commands"
rolecmd "github.com/cosmos/cosmos-sdk/modules/roles/commands"
)
// BaseCli - main basecoin client command
var BaseCli = &cobra.Command{
Use: "basecli",
Short: "Light client for Tendermint",
Long: `Basecli is a certifying light client for the basecoin abci app.
It leverages the power of the tendermint consensus algorithm get full
cryptographic proof of all queries while only syncing a fraction of the
block headers.`,
}
func main() {
commands.AddBasicFlags(BaseCli)
// Prepare queries
query.RootCmd.AddCommand(
// These are default parsers, but optional in your app (you can remove key)
query.TxQueryCmd,
query.KeyQueryCmd,
coincmd.AccountQueryCmd,
noncecmd.NonceQueryCmd,
rolecmd.RoleQueryCmd,
ibccmd.IBCQueryCmd,
)
// set up the middleware
txcmd.Middleware = txcmd.Wrappers{
feecmd.FeeWrapper{},
rolecmd.RoleWrapper{},
noncecmd.NonceWrapper{},
basecmd.ChainWrapper{},
authcmd.SigWrapper{},
}
txcmd.Middleware.Register(txcmd.RootCmd.PersistentFlags())
// you will always want this for the base send command
txcmd.RootCmd.AddCommand(
// This is the default transaction, optional in your app
coincmd.SendTxCmd,
coincmd.CreditTxCmd,
// this enables creating roles
rolecmd.CreateRoleTxCmd,
// these are for handling ibc
ibccmd.RegisterChainTxCmd,
ibccmd.UpdateChainTxCmd,
ibccmd.PostPacketTxCmd,
)
// Set up the various commands to use
BaseCli.AddCommand(
commands.InitCmd,
commands.ResetCmd,
keycmd.RootCmd,
seeds.RootCmd,
rpccmd.RootCmd,
query.RootCmd,
txcmd.RootCmd,
proxy.RootCmd,
commands.VersionCmd,
auto.AutoCompleteCmd,
)
cmd := cli.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli"))
cmd.Execute()
}
@@ -0,0 +1,8 @@
package commands
// import "github.com/cosmos/cosmos-sdk/plugins/ibc"
// // returns a new IBC plugin to be registered with Basecoin
// func NewIBCPlugin() *ibc.IBCPlugin {
// return ibc.New()
// }
@@ -0,0 +1,147 @@
package commands
import (
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/config"
cmn "github.com/tendermint/tmlibs/common"
)
// InitCmd - node initialization command
var InitCmd = &cobra.Command{
Use: "init [address]",
Short: "Initialize a basecoin blockchain",
RunE: initCmd,
}
//nolint - flags
var (
FlagChainID = "chain-id" //TODO group with other flags or remove? is this already a flag here?
)
func init() {
InitCmd.Flags().String(FlagChainID, "test_chain_id", "Chain ID")
}
// returns 1 iff it set a file, otherwise 0 (so we can add them)
func setupFile(path, data string, perm os.FileMode) (int, error) {
_, err := os.Stat(path)
if !os.IsNotExist(err) { //note, os.IsExist(err) != !os.IsNotExist(err)
return 0, nil
}
err = ioutil.WriteFile(path, []byte(data), perm)
if err != nil {
return 0, err
}
return 1, nil
}
func initCmd(cmd *cobra.Command, args []string) error {
// this will ensure that config.toml is there if not yet created, and create dir
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
if len(args) != 1 {
return fmt.Errorf("`init` takes one argument, a basecoin account address. Generate one using `basecli keys new mykey`")
}
userAddr := args[0]
// verify this account is correct
data, err := hex.DecodeString(cmn.StripHex(userAddr))
if err != nil {
return errors.Wrap(err, "Invalid address")
}
if len(data) != 20 {
return errors.New("Address must be 20-bytes in hex")
}
genesis := GetGenesisJSON(viper.GetString(FlagChainID), userAddr)
return CreateGenesisValidatorFiles(cfg, genesis, cmd.Root().Name())
}
// CreateGenesisValidatorFiles creates a genesis file with these
// contents and a private validator file
func CreateGenesisValidatorFiles(cfg *config.Config, genesis, appName string) error {
genesisFile := cfg.GenesisFile()
privValFile := cfg.PrivValidatorFile()
mod1, err := setupFile(genesisFile, genesis, 0644)
if err != nil {
return err
}
mod2, err := setupFile(privValFile, PrivValJSON, 0400)
if err != nil {
return err
}
if (mod1 + mod2) > 0 {
msg := fmt.Sprintf("Initialized %s", appName)
logger.Info(msg, "genesis", genesisFile, "priv_validator", privValFile)
} else {
logger.Info("Already initialized", "priv_validator", privValFile)
}
return nil
}
// PrivValJSON - validator private key file contents in json
var PrivValJSON = `{
"address": "7A956FADD20D3A5B2375042B2959F8AB172A058F",
"last_height": 0,
"last_round": 0,
"last_signature": null,
"last_signbytes": "",
"last_step": 0,
"priv_key": {
"type": "ed25519",
"data": "D07ABE82A8B15559A983B2DB5D4842B2B6E4D6AF58B080005662F424F17D68C17B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
},
"pub_key": {
"type": "ed25519",
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
}
}`
// GetGenesisJSON returns a new tendermint genesis with Basecoin app_options
// that grant a large amount of "mycoin" to a single address
// TODO: A better UX for generating genesis files
func GetGenesisJSON(chainID, addr string) string {
return fmt.Sprintf(`{
"app_hash": "",
"chain_id": "%s",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"amount": 10,
"name": "",
"pub_key": {
"type": "ed25519",
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
}
}
],
"app_options": {
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}],
"plugin_options": [
"coin/issuer", {"app": "sigs", "addr": "%s"}
]
}
}`, chainID, addr, addr)
}
@@ -0,0 +1,71 @@
package commands
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"path"
"strings"
"github.com/spf13/viper"
"github.com/tendermint/go-crypto"
"github.com/tendermint/tmlibs/cli"
)
//---------------------------------------------
// simple implementation of a key
// Address - public address for a key
type Address [20]byte
// MarshalJSON - marshal the json bytes of the address
func (a Address) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%x"`, a[:])), nil
}
// UnmarshalJSON - unmarshal the json bytes of the address
func (a *Address) UnmarshalJSON(addrHex []byte) error {
addr, err := hex.DecodeString(strings.Trim(string(addrHex), `"`))
if err != nil {
return err
}
copy(a[:], addr)
return nil
}
// Key - full private key
type Key struct {
Address Address `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
PrivKey crypto.PrivKey `json:"priv_key"`
}
// Sign - Implements Signer
func (k *Key) Sign(msg []byte) crypto.Signature {
return k.PrivKey.Sign(msg)
}
// LoadKey - load key from json file
func LoadKey(keyFile string) (*Key, error) {
filePath := keyFile
if !strings.HasPrefix(keyFile, "/") && !strings.HasPrefix(keyFile, ".") {
rootDir := viper.GetString(cli.HomeFlag)
filePath = path.Join(rootDir, keyFile)
}
keyJSONBytes, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
key := new(Key)
err = json.Unmarshal(keyJSONBytes, key)
if err != nil {
return nil, fmt.Errorf("Error reading key from %v: %v", filePath, err) //never stack trace
}
return key, nil
}
@@ -0,0 +1,298 @@
package commands
// import (
// "fmt"
// "io/ioutil"
// "strconv"
// "time"
// "github.com/pkg/errors"
// "github.com/spf13/cobra"
// // "github.com/spf13/viper"
// // "github.com/tendermint/tmlibs/cli"
// // "github.com/tendermint/tmlibs/log"
// "github.com/tendermint/go-wire"
// "github.com/tendermint/merkleeyes/iavl"
// cmn "github.com/tendermint/tmlibs/common"
// "github.com/cosmos/cosmos-sdk/plugins/ibc"
// "github.com/cosmos/cosmos-sdk/types"
// "github.com/tendermint/tendermint/rpc/client"
// tmtypes "github.com/tendermint/tendermint/types"
// )
// var RelayCmd = &cobra.Command{
// Use: "relay",
// Short: "Relay ibc packets between two chains",
// }
// var RelayStartCmd = &cobra.Command{
// Use: "start",
// Short: "Start basecoin relayer to relay IBC packets between chains",
// RunE: relayStartCmd,
// }
// var RelayInitCmd = &cobra.Command{
// Use: "init",
// Short: "Register both chains with each other, to prepare the relayer to run",
// RunE: relayInitCmd,
// }
// //flags
// var (
// chain1AddrFlag string
// chain2AddrFlag string
// chain1IDFlag string
// chain2IDFlag string
// fromFileFlag string
// genesisFile1Flag string
// genesisFile2Flag string
// )
// func init() {
// flags := []Flag2Register{
// {&chain1AddrFlag, "chain1-addr", "tcp://localhost:46657", "Node address for chain1"},
// {&chain2AddrFlag, "chain2-addr", "tcp://localhost:36657", "Node address for chain2"},
// {&chain1IDFlag, "chain1-id", "test_chain_1", "ChainID for chain1"},
// {&chain2IDFlag, "chain2-id", "test_chain_2", "ChainID for chain2"},
// {&fromFileFlag, "from", "key.json", "Path to a private key to sign the transaction"},
// }
// RegisterPersistentFlags(RelayCmd, flags)
// initFlags := []Flag2Register{
// {&genesisFile1Flag, "genesis1", "", "Path to genesis file for chain1"},
// {&genesisFile2Flag, "genesis2", "", "Path to genesis file for chain2"},
// }
// RegisterFlags(RelayInitCmd, initFlags)
// RelayCmd.AddCommand(RelayStartCmd)
// RelayCmd.AddCommand(RelayInitCmd)
// }
// func relayStartCmd(cmd *cobra.Command, args []string) error {
// go loop(chain1AddrFlag, chain2AddrFlag, chain1IDFlag, chain2IDFlag)
// go loop(chain2AddrFlag, chain1AddrFlag, chain2IDFlag, chain1IDFlag)
// cmn.TrapSignal(func() {
// // TODO: Cleanup
// })
// return nil
// }
// func relayInitCmd(cmd *cobra.Command, args []string) error {
// err := registerChain(chain1IDFlag, chain1AddrFlag, chain2IDFlag, genesisFile2Flag, fromFileFlag)
// if err != nil {
// return err
// }
// err = registerChain(chain2IDFlag, chain2AddrFlag, chain1IDFlag, genesisFile1Flag, fromFileFlag)
// return err
// }
// func registerChain(chainID, node, registerChainID, registerGenesis, keyFile string) error {
// genesisBytes, err := ioutil.ReadFile(registerGenesis)
// if err != nil {
// return errors.Errorf("Error reading genesis file %v: %v\n", registerGenesis, err)
// }
// ibcTx := ibc.IBCRegisterChainTx{
// ibc.BlockchainGenesis{
// ChainID: registerChainID,
// Genesis: string(genesisBytes),
// },
// }
// privKey, err := LoadKey(keyFile)
// if err != nil {
// return err
// }
// relay := newRelayer(privKey, chainID, node)
// return relay.appTx(ibcTx)
// }
// func loop(addr1, addr2, id1, id2 string) {
// nextSeq := 0
// // load the priv key
// privKey, err := LoadKey(fromFileFlag)
// if err != nil {
// logger.Error(err.Error())
// cmn.PanicCrisis(err.Error())
// }
// // relay from chain1 to chain2
// thisRelayer := newRelayer(privKey, id2, addr2)
// logger.Info(fmt.Sprintf("Relaying from chain %v on %v to chain %v on %v", id1, addr1, id2, addr2))
// httpClient := client.NewHTTP(addr1, "/websocket")
// OUTER:
// for {
// time.Sleep(time.Second)
// // get the latest ibc packet sequence number
// key := fmt.Sprintf("ibc,egress,%v,%v", id1, id2)
// query, err := queryWithClient(httpClient, []byte(key))
// if err != nil {
// logger.Error("Error querying for latest sequence", "key", key, "error", err.Error())
// continue OUTER
// }
// if len(query.Value) == 0 {
// // nothing yet
// continue OUTER
// }
// seq, err := strconv.ParseUint(string(query.Value), 10, 64)
// if err != nil {
// logger.Error("Error parsing sequence number from query", "query.Value", query.Value, "error", err.Error())
// continue OUTER
// }
// seq -= 1 // seq is the packet count. -1 because 0-indexed
// if nextSeq <= int(seq) {
// logger.Info("Got new packets", "last-sequence", nextSeq-1, "new-sequence", seq)
// }
// // get all packets since the last one we relayed
// for ; nextSeq <= int(seq); nextSeq++ {
// key := fmt.Sprintf("ibc,egress,%v,%v,%d", id1, id2, nextSeq)
// query, err := queryWithClient(httpClient, []byte(key))
// if err != nil {
// logger.Error("Error querying for packet", "seqeuence", nextSeq, "key", key, "error", err.Error())
// continue OUTER
// }
// var packet ibc.Packet
// err = wire.ReadBinaryBytes(query.Value, &packet)
// if err != nil {
// logger.Error("Error unmarshalling packet", "key", key, "query.Value", query.Value, "error", err.Error())
// continue OUTER
// }
// proof := new(iavl.IAVLProof)
// err = wire.ReadBinaryBytes(query.Proof, &proof)
// if err != nil {
// logger.Error("Error unmarshalling proof", "query.Proof", query.Proof, "error", err.Error())
// continue OUTER
// }
// // query.Height is actually for the next block,
// // so wait a block before we fetch the header & commit
// if err := waitForBlock(httpClient); err != nil {
// logger.Error("Error waiting for a block", "addr", addr1, "error", err.Error())
// continue OUTER
// }
// // get the header and commit from the height the query was done at
// res, err := httpClient.Commit(int(query.Height))
// if err != nil {
// logger.Error("Error fetching header and commits", "height", query.Height, "error", err.Error())
// continue OUTER
// }
// // update the chain state on the other chain
// updateTx := ibc.IBCUpdateChainTx{
// Header: *res.Header,
// Commit: *res.Commit,
// }
// logger.Info("Updating chain", "src-chain", id1, "height", res.Header.Height, "appHash", res.Header.AppHash)
// if err := thisRelayer.appTx(updateTx); err != nil {
// logger.Error("Error creating/sending IBCUpdateChainTx", "error", err.Error())
// continue OUTER
// }
// // relay the packet and proof
// logger.Info("Relaying packet", "src-chain", id1, "height", query.Height, "sequence", nextSeq)
// postTx := ibc.IBCPacketPostTx{
// FromChainID: id1,
// FromChainHeight: query.Height,
// Packet: packet,
// Proof: proof,
// }
// if err := thisRelayer.appTx(postTx); err != nil {
// logger.Error("Error creating/sending IBCPacketPostTx", "error", err.Error())
// // dont `continue OUTER` here. the error might be eg. Already exists
// // TODO: catch this programmatically ?
// }
// }
// }
// }
// type relayer struct {
// privKey *Key
// chainID string
// nodeAddr string
// client *client.HTTP
// }
// func newRelayer(privKey *Key, chainID, nodeAddr string) *relayer {
// httpClient := client.NewHTTP(nodeAddr, "/websocket")
// return &relayer{
// privKey: privKey,
// chainID: chainID,
// nodeAddr: nodeAddr,
// client: httpClient,
// }
// }
// func (r *relayer) appTx(ibcTx ibc.IBCTx) error {
// acc, err := getAccWithClient(r.client, r.privKey.Address[:])
// if err != nil {
// return err
// }
// sequence := acc.Sequence + 1
// data := []byte(wire.BinaryBytes(struct {
// ibc.IBCTx `json:"unwrap"`
// }{ibcTx}))
// smallCoins := coin.Coin{"mycoin", 1}
// input := types.NewTxInput(r.privKey.PubKey, coin.Coins{smallCoins}, sequence)
// tx := &types.AppTx{
// Gas: 0,
// Fee: smallCoins,
// Name: "IBC",
// Input: input,
// Data: data,
// }
// tx.Input.Signature = r.privKey.Sign(tx.SignBytes(r.chainID))
// txBytes := []byte(wire.BinaryBytes(struct {
// types.Tx `json:"unwrap"`
// }{tx}))
// data, log, err := broadcastTxWithClient(r.client, txBytes)
// if err != nil {
// return err
// }
// _, _ = data, log
// return nil
// }
// // broadcast the transaction to tendermint
// func broadcastTxWithClient(httpClient *client.HTTP, tx tmtypes.Tx) ([]byte, string, error) {
// res, err := httpClient.BroadcastTxCommit(tx)
// if err != nil {
// return nil, "", errors.Errorf("Error on broadcast tx: %v", err)
// }
// if !res.CheckTx.Code.IsOK() {
// r := res.CheckTx
// return nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
// }
// if !res.DeliverTx.Code.IsOK() {
// r := res.DeliverTx
// return nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
// }
// return res.DeliverTx.Data, res.DeliverTx.Log, nil
// }
@@ -0,0 +1,23 @@
package commands
import (
"github.com/spf13/cobra"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
)
// UnsafeResetAllCmd - extension of the tendermint command, resets initialization
var UnsafeResetAllCmd = &cobra.Command{
Use: "unsafe_reset_all",
Short: "Reset all blockchain data",
RunE: unsafeResetAllCmd,
}
func unsafeResetAllCmd(cmd *cobra.Command, args []string) error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), logger)
return nil
}
@@ -0,0 +1,43 @@
package commands
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tmlibs/cli"
tmflags "github.com/tendermint/tmlibs/cli/flags"
"github.com/tendermint/tmlibs/log"
)
//nolint
const (
defaultLogLevel = "error"
FlagLogLevel = "log_level"
)
var (
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
)
// RootCmd - main node command
var RootCmd = &cobra.Command{
Use: "basecoin",
Short: "A cryptocurrency framework in Golang based on Tendermint-Core",
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
level := viper.GetString(FlagLogLevel)
logger, err = tmflags.ParseLogLevel(level, logger, defaultLogLevel)
if err != nil {
return err
}
if viper.GetBool(cli.TraceFlag) {
logger = log.NewTracingLogger(logger)
}
return nil
},
}
func init() {
RootCmd.PersistentFlags().String(FlagLogLevel, defaultLogLevel, "Log level")
}
@@ -0,0 +1,132 @@
package commands
import (
"fmt"
"os"
"path"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/abci/server"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/app"
)
// StartCmd - command to start running the basecoin node!
var StartCmd = &cobra.Command{
Use: "start",
Short: "Start basecoin",
RunE: startCmd,
}
// nolint TODO: move to config file
const EyesCacheSize = 10000
//nolint
const (
FlagAddress = "address"
FlagWithoutTendermint = "without-tendermint"
)
var (
// Handler - use a global to store the handler, so we can set it in main.
// TODO: figure out a cleaner way to register plugins
Handler sdk.Handler
)
func init() {
flags := StartCmd.Flags()
flags.String(FlagAddress, "tcp://0.0.0.0:46658", "Listen address")
flags.Bool(FlagWithoutTendermint, false, "Only run basecoin abci app, assume external tendermint process")
// add all standard 'tendermint node' flags
tcmd.AddNodeFlags(StartCmd)
}
func startCmd(cmd *cobra.Command, args []string) error {
rootDir := viper.GetString(cli.HomeFlag)
store, err := app.NewStore(
path.Join(rootDir, "data", "merkleeyes.db"),
EyesCacheSize,
logger.With("module", "store"),
)
if err != nil {
return err
}
// Create Basecoin app
basecoinApp := app.NewBasecoin(Handler, store, logger.With("module", "app"))
// if chain_id has not been set yet, load the genesis.
// else, assume it's been loaded
if basecoinApp.GetChainID() == "" {
// If genesis file exists, set key-value options
genesisFile := path.Join(rootDir, "genesis.json")
if _, err := os.Stat(genesisFile); err == nil {
err := basecoinApp.LoadGenesis(genesisFile)
if err != nil {
return errors.Errorf("Error in LoadGenesis: %v\n", err)
}
} else {
fmt.Printf("No genesis file at %s, skipping...\n", genesisFile)
}
}
chainID := basecoinApp.GetChainID()
if viper.GetBool(FlagWithoutTendermint) {
logger.Info("Starting Basecoin without Tendermint", "chain_id", chainID)
// run just the abci app/server
return startBasecoinABCI(basecoinApp)
}
logger.Info("Starting Basecoin with Tendermint", "chain_id", chainID)
// start the app with tendermint in-process
return startTendermint(rootDir, basecoinApp)
}
func startBasecoinABCI(basecoinApp *app.Basecoin) error {
// Start the ABCI listener
addr := viper.GetString(FlagAddress)
svr, err := server.NewServer(addr, "socket", basecoinApp)
if err != nil {
return errors.Errorf("Error creating listener: %v\n", err)
}
svr.SetLogger(logger.With("module", "abci-server"))
svr.Start()
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
svr.Stop()
})
return nil
}
func startTendermint(dir string, basecoinApp *app.Basecoin) error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
// Create & start tendermint node
privValidator := types.LoadOrGenPrivValidator(cfg.PrivValidatorFile(), logger)
n := node.NewNode(cfg, privValidator, proxy.NewLocalClientCreator(basecoinApp), logger.With("module", "node"))
_, err = n.Start()
if err != nil {
return err
}
// Trap signal, run forever.
n.RunForever()
return nil
}
+60
View File
@@ -0,0 +1,60 @@
package main
import (
"os"
"github.com/tendermint/tmlibs/cli"
sdk "github.com/cosmos/cosmos-sdk"
client "github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/fee"
"github.com/cosmos/cosmos-sdk/modules/ibc"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/modules/roles"
"github.com/cosmos/cosmos-sdk/stack"
)
// BuildApp constructs the stack we want to use for this app
func BuildApp(feeDenom string) sdk.Handler {
return stack.New(
base.Logger{},
stack.Recovery{},
auth.Signatures{},
base.Chain{},
stack.Checkpoint{OnCheck: true},
nonce.ReplayCheck{},
).
IBC(ibc.NewMiddleware()).
Apps(
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
coin.NewHandler(),
stack.WrapHandler(roles.NewHandler()),
stack.WrapHandler(ibc.NewHandler()),
)
}
func main() {
rt := commands.RootCmd
// require all fees in mycoin - change this in your app!
commands.Handler = BuildApp("mycoin")
rt.AddCommand(
commands.InitCmd,
commands.StartCmd,
//commands.RelayCmd,
commands.UnsafeResetAllCmd,
client.VersionCmd,
)
cmd := cli.PrepareMainCmd(rt, "BC", os.ExpandEnv("$HOME/.basecoin"))
cmd.Execute()
}
+160
View File
@@ -0,0 +1,160 @@
# baseserver
baseserver is the REST counterpart to basecli
## Compiling and running it
```shell
$ go get -u -v github.com/tendermint/basecoin/cmd/baseserver
$ baseserver init
$ baseserver serve --port 8888
```
to run the server at localhost:8888, otherwise if you don't specify --port,
by default the server will be run on port 8998.
## Supported routes
Route | Method | Completed | Description
---|---|---|---
/keys|GET|✔️|Lists all keys
/keys|POST|✔️|Generate a new key. It expects fields: "name", "algo", "passphrase"
/keys/{name}|GET|✔️|Retrieves the specific key
/keys/{name}|POST/PUT|✔️|Updates the named key
/keys/{name}|DELETE|✔️|Deletes the named key
/build/send|POST|✔️|Send a transaction
/sign|POST|✔️|Sign a transaction
/tx|POST|✖️|Post a transaction to the blockchain
/seeds/status|GET|✖️|Returns the information on the last seed
/build/create_role|POST|✔️|Creates a role. Please note that the role MUST be valid hex for example instead of sending "role", send its hex encoded equivalent "726f6c65"
## Preamble:
In the examples below, we assume that URL is set to `http://localhost:8889`
which can be set for example
URL=http://localhost:8889
## Sample usage
- Generate a key
```shell
$ curl -X POST $URL/keys --data '{"algo": "ed25519", "name": "SampleX", "passphrase": "Say no more"}'
```
```json
{
"key": {
"name": "SampleX",
"address": "603EE63C41E322FC7A247864A9CD0181282EB458",
"pubkey": {
"type": "ed25519",
"data": "C050948CFC087F5E1068C7E244DDC30E03702621CC9442A28E6C9EDA7771AA0C"
}
},
"seed_phrase": "border almost future parade speak soccer bulk orange real brisk caution body river chapter"
}
```
- Sign a key
```shell
$ curl -X POST $URL/sign --data '{
"name": "matt",
"password": "Say no more",
"tx": {
"type": "sigs/multi",
"data": {
"tx": {"type":"coin/send","data":{"inputs":[{"address":{"chain":"","app":"role","addr":"62616E6B32"},"coins":[{"denom":"mycoin","amount":900000}]}],"outputs":[{"address":{"chain":"","app":"sigs","addr":"BDADF167E6CF2CDF2D621E590FF1FED2787A40E0"},"coins":[{"denom":"mycoin","amount":900000}]}]}},
"signatures": null
}
}
}'
```
```json
{
"type": "sigs/multi",
"data": {
"tx": {
"type": "coin/send",
"data": {
"inputs": [
{
"address": {
"chain": "",
"app": "role",
"addr": "62616E6B32"
},
"coins": [
{
"denom": "mycoin",
"amount": 900000
}
]
}
],
"outputs": [
{
"address": {
"chain": "",
"app": "sigs",
"addr": "BDADF167E6CF2CDF2D621E590FF1FED2787A40E0"
},
"coins": [
{
"denom": "mycoin",
"amount": 900000
}
]
}
]
}
},
"signatures": [
{
"Sig": {
"type": "ed25519",
"data": "F6FE3053F1E6C236F886A0D525C1AF840F7831B6E50F7E1108C345AA524303920F09945DA110AD5184B3F45717D7114E368B12AFE027FECECC2FC193D4906A0C"
},
"Pubkey": {
"type": "ed25519",
"data": "0D8D19E527BAE9D1256A3D03009E2708171CDCB71CCDEDA2DC52DD9AD23AEE25"
}
}
]
}
}
```
- Create a role
```shell
$ curl -X POST $URL/build/create_role --data \
'{
"role": "deadbeef",
"signers": [{
"addr": "4FF759D47C81754D8F553DCCAC8651D0AF74C7F9",
"app": "role"
}],
"min_sigs": 1,
"seq": 1
}'
```
```json
{
"type": "chain/tx",
"data": {
"chain_id": "test_chain_id",
"expires_at": 0,
"tx": {
"type": "role/create",
"data": {
"role": "DEADBEEF",
"min_sigs": 1,
"signers": [
{
"chain": "",
"app": "role",
"addr": "4FF759D47C81754D8F553DCCAC8651D0AF74C7F9"
}
]
}
}
}
}
```
+95
View File
@@ -0,0 +1,95 @@
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/commands"
rest "github.com/cosmos/cosmos-sdk/client/rest"
coinrest "github.com/cosmos/cosmos-sdk/modules/coin/rest"
noncerest "github.com/cosmos/cosmos-sdk/modules/nonce/rest"
rolerest "github.com/cosmos/cosmos-sdk/modules/roles/rest"
"github.com/tendermint/tmlibs/cli"
)
var srvCli = &cobra.Command{
Use: "baseserver",
Short: "Light REST client for tendermint",
Long: `Baseserver presents a nice (not raw hex) interface to the basecoin blockchain structure.`,
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Serve the light REST client for tendermint",
Long: "Access basecoin via REST",
RunE: serve,
}
const (
envPortFlag = "port"
defaultAlgo = "ed25519"
)
func init() {
_ = serveCmd.PersistentFlags().Int(envPortFlag, 8998, "the port to run the server on")
}
func serve(cmd *cobra.Command, args []string) error {
router := mux.NewRouter()
routeRegistrars := []func(*mux.Router) error{
// rest.Keys handlers
rest.NewDefaultKeysManager(defaultAlgo).RegisterAllCRUD,
// Coin send handler
coinrest.RegisterCoinSend,
// Coin query account handler
coinrest.RegisterQueryAccount,
// Roles createRole handler
rolerest.RegisterCreateRole,
// Basecoin sign transactions handler
rest.RegisterSignTx,
// Basecoin post transaction handler
rest.RegisterPostTx,
// Nonce query handler
noncerest.RegisterQueryNonce,
}
for _, routeRegistrar := range routeRegistrars {
if err := routeRegistrar(router); err != nil {
log.Fatal(err)
}
}
port := viper.GetInt(envPortFlag)
addr := fmt.Sprintf(":%d", port)
log.Printf("Serving on %q", addr)
return http.ListenAndServe(addr, router)
}
func main() {
commands.AddBasicFlags(srvCli)
srvCli.AddCommand(
commands.InitCmd,
commands.VersionCmd,
serveCmd,
)
// this should share the dir with basecli, so you can use the cli and
// the api interchangeably
cmd := cli.PrepareMainCmd(srvCli, "BC", os.ExpandEnv("$HOME/.basecli"))
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
}
+124
View File
@@ -0,0 +1,124 @@
#!/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]}
oneTimeSetUp() {
if ! quickSetup .basecoin_test_basictx basictx-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test00GetAccount() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account"
checkAccount $SENDER "9007199254740992"
ACCT2=$(${CLIENT_EXE} query account $RECV 2>/dev/null)
assertFalse "line=${LINENO}, has no genesis account" $?
}
test01SendTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
assertFalse "line=${LINENO}, missing dest" "${CLIENT_EXE} tx send --amount=992mycoin --sequence=1"
assertFalse "line=${LINENO}, bad password" "echo foo | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "9007199254740000"
# make sure 0x prefix also works
checkAccount "0x$SENDER" "9007199254740000"
checkAccount $RECV "992"
# Make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
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" $?
# checking normally
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
# make sure this works without trust also
OLD_BC_HOME=$BC_HOME
export BC_HOME=/foo
export BC_TRUST_NODE=1
export BC_NODE=localhost:46657
checkSendFeeTx $HASH $TX_HEIGHT $SENDER "90" "10"
checkAccount $SENDER "9007199254739900"
checkAccount $RECV "1082"
unset BC_TRUST_NODE
unset BC_NODE
export BC_HOME=$OLD_BC_HOME
}
test03CreditTx() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
# make sure we are controlled by permissions (only rich can issue credit)
assertFalse "line=${LINENO}, bad password" "echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=1000mycoin --sequence=1 --to=$RECV --name=$POOR"
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=1000mycoin --sequence=3 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# receiver got cash, sender didn't lose any (1000 more than last check)
checkAccount $RECV "2082"
checkAccount $SENDER "9007199254739900"
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2
+361
View File
@@ -0,0 +1,361 @@
#!/bin/bash
#!/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]}
# For full stack traces in error output, run
# BC_TRACE=1 ./ibc.sh
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
if [ $? != 0 ]; then exit 1; fi
PID_SERVER_1=$PID_SERVER
# 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
if [ $? != 0 ]; then exit 1; fi
PID_SERVER_2=$PID_SERVER
# Connect both clients
BC_HOME=${CLIENT_1} initClient $CHAIN_ID_1 $PORT_1
if [ $? != 0 ]; then exit 1; fi
BC_HOME=${CLIENT_2} initClient $CHAIN_ID_2 $PORT_2
if [ $? != 0 ]; then exit 1; fi
printf "...Testing may begin!\n\n\n"
}
oneTimeTearDown() {
printf "\n\nstopping both $SERVER_EXE test servers... $PID_SERVER_1 $PID_SERVER_2"
kill -9 $PID_SERVER_1
kill -9 $PID_SERVER_2
sleep 1
}
test00GetAccount() {
export BC_HOME=${CLIENT_1}
SENDER_1=$(getAddr $RICH)
RECV_1=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account 2>/dev/null"
assertFalse "line=${LINENO}, has no genesis account" "${CLIENT_EXE} query account $RECV_1 2>/dev/null"
checkAccount $SENDER_1 "9007199254740992"
export BC_HOME=${CLIENT_2}
SENDER_2=$(getAddr $RICH)
RECV_2=$(getAddr $POOR)
assertFalse "line=${LINENO}, requires arg" "${CLIENT_EXE} query account 2>/dev/null"
assertFalse "line=${LINENO}, has no genesis account" "${CLIENT_EXE} query account $RECV_2 2>/dev/null"
checkAccount $SENDER_2 "9007199254740992"
# Make sure that they have different addresses on both chains (they are random keys)
assertNotEquals "line=${LINENO}, sender keys must be different" "$SENDER_1" "$SENDER_2"
assertNotEquals "line=${LINENO}, recipient keys must be different" "$RECV_1" "$RECV_2"
}
test01RegisterChains() {
# let's get the root seeds to cross-register them
ROOT_1="$BASE_DIR_1/root_seed.json"
${CLIENT_EXE} seeds export $ROOT_1 --home=${CLIENT_1}
assertTrue "line=${LINENO}, export seed failed" $?
ROOT_2="$BASE_DIR_2/root_seed.json"
${CLIENT_EXE} seeds export $ROOT_2 --home=${CLIENT_2}
assertTrue "line=${LINENO}, export seed failed" $?
# register chain2 on chain1
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-register \
--sequence=1 --seed=${ROOT_2} --name=$POOR --home=${CLIENT_1})
txSucceeded $? "$TX" "register chain2 on chain 1"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# this is used later to check data
REG_HEIGHT=$(echo $TX | jq .height)
# register chain1 on chain2 (no money needed... yet)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-register \
--sequence=1 --seed=${ROOT_1} --name=$POOR --home=${CLIENT_2})
txSucceeded $? "$TX" "register chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
}
test02UpdateChains() {
# let's get the root seeds to cross-register them
UPDATE_1="$BASE_DIR_1/seed_1.json"
${CLIENT_EXE} seeds update --home=${CLIENT_1} > /dev/null
${CLIENT_EXE} seeds export $UPDATE_1 --home=${CLIENT_1}
assertTrue "line=${LINENO}, export seed failed" $?
# make sure it is newer than the other....
assertNewHeight "line=${LINENO}" $ROOT_1 $UPDATE_1
UPDATE_2="$BASE_DIR_2/seed_2.json"
${CLIENT_EXE} seeds update --home=${CLIENT_2} > /dev/null
${CLIENT_EXE} seeds export $UPDATE_2 --home=${CLIENT_2}
assertTrue "line=${LINENO}, export seed failed" $?
assertNewHeight "line=${LINENO}" $ROOT_2 $UPDATE_2
# this is used later to check query data
REGISTER_2_HEIGHT=$(cat $ROOT_2 | jq .checkpoint.header.height)
UPDATE_2_HEIGHT=$(cat $UPDATE_2 | jq .checkpoint.header.height)
# update chain2 on chain1
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--sequence=2 --seed=${UPDATE_2} --name=$POOR --home=${CLIENT_1})
txSucceeded $? "$TX" "update chain2 on chain 1"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# update chain1 on chain2 (no money needed... yet)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--sequence=2 --seed=${UPDATE_1} --name=$POOR --home=${CLIENT_2})
txSucceeded $? "$TX" "update chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
}
# make sure all query commands about ibc work...
test03QueryIBC() {
# just test on one chain, as they are all symetrical
export BC_HOME=${CLIENT_1}
# make sure we can list all chains
CHAINS=$(${CLIENT_EXE} query ibc chains)
assertTrue "line=${LINENO}, cannot query chains" $?
assertEquals "1" $(echo $CHAINS | jq '.data | length')
assertEquals "line=${LINENO}" "\"$CHAIN_ID_2\"" $(echo $CHAINS | jq '.data[0]')
# error on unknown chain, data on proper chain
assertFalse "line=${LINENO}, unknown chain" "${CLIENT_EXE} query ibc chain random 2>/dev/null"
CHAIN_INFO=$(${CLIENT_EXE} query ibc chain $CHAIN_ID_2)
assertTrue "line=${LINENO}, cannot query chain $CHAIN_ID_2" $?
assertEquals "line=${LINENO}, register height" $REG_HEIGHT $(echo $CHAIN_INFO | jq .data.registered_at)
assertEquals "line=${LINENO}, tracked height" $UPDATE_2_HEIGHT $(echo $CHAIN_INFO | jq .data.remote_block)
}
# Trigger a cross-chain sendTx... from RICH on chain1 to POOR on chain2
# we make sure the money was reduced, but nothing arrived
test04SendIBCPacket() {
export BC_HOME=${CLIENT_1}
# make sure there are no packets yet
PACKETS=$(${CLIENT_EXE} query ibc packets --to=$CHAIN_ID_2 2>/dev/null)
assertFalse "line=${LINENO}, packet query" $?
SENDER=$(getAddr $RICH)
RECV=$(BC_HOME=${CLIENT_2} getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20002mycoin \
--to=${CHAIN_ID_2}::${RECV} --name=$RICH)
txSucceeded $? "$TX" "${CHAIN_ID_2}::${RECV}"
# quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# Make sure balance went down and tx is indexed
checkAccount $SENDER "9007199254720990"
checkSendTx $HASH $TX_HEIGHT $SENDER "20002"
# look, we wrote a packet
PACKETS=$(${CLIENT_EXE} query ibc packets --to=$CHAIN_ID_2)
assertTrue "line=${LINENO}, packets query" $?
assertEquals "line=${LINENO}, packet count" 1 $(echo $PACKETS | jq .data)
# and look at the packet itself
PACKET=$(${CLIENT_EXE} query ibc packet --to=$CHAIN_ID_2 --sequence=0)
assertTrue "line=${LINENO}, packet query" $?
assertEquals "line=${LINENO}, proper src" "\"$CHAIN_ID_1\"" $(echo $PACKET | jq .src_chain)
assertEquals "line=${LINENO}, proper dest" "\"$CHAIN_ID_2\"" $(echo $PACKET | jq .packet.dest_chain)
assertEquals "line=${LINENO}, proper sequence" "0" $(echo $PACKET | jq .packet.sequence)
# nothing arrived
ARRIVED=$(${CLIENT_EXE} query ibc packets --from=$CHAIN_ID_1 --home=$CLIENT_2 2>/dev/null)
assertFalse "line=${LINENO}, packet query" $?
assertFalse "line=${LINENO}, no relay running" "BC_HOME=${CLIENT_2} ${CLIENT_EXE} query account $RECV"
}
test05ReceiveIBCPacket() {
export BC_HOME=${CLIENT_2}
# make some credit, so we can accept the packet
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx credit --amount=60006mycoin --to=$CHAIN_ID_1:: --name=$RICH)
txSucceeded $? "$TX" "${CHAIN_ID_1}::"
checkAccount $CHAIN_ID_1:: "60006"
# now, we try to post it.... (this is PACKET from last test)
# get the seed and post it
SRC_HEIGHT=$(echo $PACKET | jq .src_height)
# FIXME: this should auto-update on proofs...
${CLIENT_EXE} seeds update --height=$SRC_HEIGHT --home=${CLIENT_1} > /dev/null
assertTrue "line=${LINENO}, update seed failed" $?
PACKET_SEED="$BASE_DIR_1/packet_seed.json"
${CLIENT_EXE} seeds export $PACKET_SEED --home=${CLIENT_1} #--height=$SRC_HEIGHT
assertTrue "line=${LINENO}, export seed failed" $?
# echo "**** SEED ****"
# cat $PACKET_SEED | jq .
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-update \
--seed=${PACKET_SEED} --name=$POOR)
txSucceeded $? "$TX" "prepare packet chain1 on chain 2"
# an example to quit early if there is no point in more tests
if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# write the packet to the file
POST_PACKET="$BASE_DIR_1/post_packet.json"
echo $PACKET > $POST_PACKET
# echo "**** POST ****"
# cat $POST_PACKET | jq .
# post it as a tx (cross-fingers)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx ibc-post \
--packet=${POST_PACKET} --name=$POOR)
txSucceeded $? "$TX" "post packet from chain1 on chain 2"
# TODO: more queries on stuff...
# look, we wrote a packet
PACKETS=$(${CLIENT_EXE} query ibc packets --from=$CHAIN_ID_1)
assertTrue "line=${LINENO}, packets query" $?
assertEquals "line=${LINENO}, packet count" 1 $(echo $PACKETS | jq .data)
}
# XXX Ex Usage: assertNewHeight $MSG $SEED_1 $SEED_2
# Desc: Asserts that seed2 has a higher block height than seed 1
assertNewHeight() {
H1=$(cat $2 | jq .checkpoint.header.height)
H2=$(cat $3 | jq .checkpoint.header.height)
assertTrue "$MSG" "test $H2 -gt $H1"
return $?
}
# 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)
# export BC_HOME=${CLIENT_1}
# TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20002mycoin \
# --sequence=1 --to=${CHAIN_ID_2}/${RECV} --name=$RICH)
# txSucceeded $? "$TX" "${CHAIN_ID_2}/${RECV}"
# # an example to quit early if there is no point in more tests
# if [ $? != 0 ]; then echo "aborting!"; return 1; fi
# 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 "line=${LINENO}, 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"; cat ${BASE_DIR_1}/../relay.log; 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
# printf "stoping relay\n"
# 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 \")
# echo starting relay $PID_RELAY ...
# # 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)
# txSucceeded $? "$RES" "$RELAY_ADDR"
# 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)
# txSucceeded $? "$RES" "$RELAY_ADDR"
# if [ $? != 0 ]; then echo "can't pay chain2!"; return 1; fi
# # Initialize the relay (register both chains)
# ${SERVER_EXE} relay init --chain1-id=$CHAIN_ID_1 --chain2-id=$CHAIN_ID_2 \
# --chain1-addr=tcp://localhost:${PORT_1} --chain2-addr=tcp://localhost:${PORT_2} \
# --genesis1=${BASE_DIR_1}/server/genesis.json --genesis2=${BASE_DIR_2}/server/genesis.json \
# --from=$RELAY_KEY > ${BASE_DIR_1}/../relay.log
# if [ $? != 0 ]; then echo "can't initialize relays"; cat ${BASE_DIR_1}/../relay.log; return 1; fi
# # Now start the relay (constantly send packets)
# ${SERVER_EXE} relay start --chain1-id=$CHAIN_ID_1 --chain2-id=$CHAIN_ID_2 \
# --chain1-addr=tcp://localhost:${PORT_1} --chain2-addr=tcp://localhost:${PORT_2} \
# --from=$RELAY_KEY >> ${BASE_DIR_1}/../relay.log &
# sleep 2
# PID_RELAY=$!
# disown
# # Return an error if it dies in the first two seconds to make sure it is running
# ps $PID_RELAY >/dev/null
# return $?
# }
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2
+112
View File
@@ -0,0 +1,112 @@
#!/bin/bash
CLIENT_EXE=basecli
SERVER_EXE=basecoin
oneTimeSetUp() {
BASE=~/.bc_init_test
rm -rf "$BASE"
mkdir -p "$BASE"
SERVER="${BASE}/server"
SERVER_LOG="${BASE}/${SERVER_EXE}.log"
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
${SERVER_EXE} init ${HEX} --home="$SERVER" >> "$SERVER_LOG"
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
GENESIS_FILE=${SERVER}/genesis.json
CHAIN_ID=$(cat ${GENESIS_FILE} | jq .chain_id | tr -d \")
printf "starting ${SERVER_EXE}...\n"
${SERVER_EXE} start --home="$SERVER" >> "$SERVER_LOG" 2>&1 &
sleep 5
PID_SERVER=$!
disown
if ! ps $PID_SERVER >/dev/null; then
echo "**STARTUP FAILED**"
cat $SERVER_LOG
return 1
fi
}
oneTimeTearDown() {
printf "\nstopping ${SERVER_EXE}..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test01goodInit() {
export BCHOME=${BASE}/client-01
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
}
test02badInit() {
export BCHOME=${BASE}/client-02
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# no node where we go
echo y | ${CLIENT_EXE} init --node=tcp://localhost:9999 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
# dir there, but empty...
checkDir $BCHOME 0
# try with invalid chain id
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="bad-chain-id" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
checkDir $BCHOME 0
# reject the response
echo n | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, invalid init" $?
checkDir $BCHOME 0
}
test03noDoubleInit() {
export BCHOME=${BASE}/client-03
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# init properly
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
# try again, and we get an error
echo y | ${CLIENT_EXE} init --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertFalse "line=${LINENO}, warning on re-init" $?
checkDir $BCHOME 3
# unless we --force-reset
echo y | ${CLIENT_EXE} init --force-reset --node=tcp://localhost:46657 --chain-id="${CHAIN_ID}" > /dev/null 2>&1
assertTrue "line=${LINENO}, re-initialized light-client" $?
checkDir $BCHOME 3
}
test04acceptGenesisFile() {
export BCHOME=${BASE}/client-04
assertFalse "line=${LINENO}" "ls ${BCHOME} 2>/dev/null >&2"
# init properly
${CLIENT_EXE} init --node=tcp://localhost:46657 --genesis=${GENESIS_FILE} > /dev/null 2>&1
assertTrue "line=${LINENO}, initialized light-client" $?
checkDir $BCHOME 3
}
# XXX Ex: checkDir $DIR $FILES
# Makes sure directory exists and has the given number of files
checkDir() {
assertTrue "line=${LINENO}" "ls ${1} 2>/dev/null >&2"
assertEquals "line=${LINENO}, no files created" "$2" $(ls $1 | wc -l)
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/shunit2
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
CLIENT_EXE=basecli
oneTimeSetUp() {
PASS=qwertyuiop
export BCHOME=$HOME/.bc_keys_test
${CLIENT_EXE} reset_all
assertTrue "line ${LINENO}" $?
}
newKey(){
assertNotNull "keyname required" "$1"
KEYPASS=${2:-qwertyuiop}
echo $KEYPASS | ${CLIENT_EXE} keys new $1 >/dev/null 2>&1
assertTrue "line ${LINENO}, created $1" $?
}
testMakeKeys() {
USER=demouser
assertFalse "line ${LINENO}, already user $USER" "${CLIENT_EXE} keys get $USER"
newKey $USER
assertTrue "line ${LINENO}, no user $USER" "${CLIENT_EXE} keys get $USER"
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/shunit2
+159
View File
@@ -0,0 +1,159 @@
#!/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]}
BPORT=7000
URL="localhost:${BPORT}"
oneTimeSetUp() {
if ! quickSetup .basecoin_test_rest rest-chain; then
exit 1;
fi
baseserver serve --port $BPORT >/dev/null &
PID_PROXY=$!
disown
sleep 0.1 # for startup
}
oneTimeTearDown() {
quickTearDown
kill -9 $PID_PROXY
}
# XXX Ex Usage: restAddr $NAME
# Desc: Gets the address for a key name via rest
restAddr() {
assertNotNull "line=${LINENO}, keyname required" "$1"
ADDR=$(curl ${URL}/keys/${1} 2>/dev/null | jq .address | tr -d \")
assertNotEquals "line=${LINENO}, null key" "null" "$ADDR"
assertNotEquals "line=${LINENO}, no key" "" "$ADDR"
echo $ADDR
}
# XXX Ex Usage: restAccount $ADDR $AMOUNT
# Desc: Assumes just one coin, checks the balance of first coin in any case
restAccount() {
assertNotNull "line=${LINENO}, address required" "$1"
ACCT=$(curl ${URL}/query/account/sigs:$1 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $ACCT; echo; fi
assertEquals "line=${LINENO}, proper money" "$2" $(echo $ACCT | jq .data.coins[0].amount)
return $?
}
restNoAccount() {
ERROR=$(curl ${URL}/query/account/sigs:$1 2>/dev/null)
assertEquals "line=${LINENO}, should error" 400 $(echo $ERROR | jq .code)
}
test00GetAccount() {
RECV=$(restAddr $POOR)
SENDER=$(restAddr $RICH)
restNoAccount $RECV
restAccount $SENDER "9007199254740992"
}
test01SendTx() {
SENDER=$(restAddr $RICH)
RECV=$(restAddr $POOR)
CMD="{\"from\": {\"app\": \"sigs\", \"addr\": \"$SENDER\"}, \"to\": {\"app\": \"sigs\", \"addr\": \"$RECV\"}, \"amount\": [{\"denom\": \"mycoin\", \"amount\": 992}], \"sequence\": 1}"
UNSIGNED=$(curl -XPOST ${URL}/build/send -d "$CMD" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $UNSIGNED; echo; fi
TOSIGN="{\"name\": \"$RICH\", \"password\": \"qwertyuiop\", \"tx\": $UNSIGNED}"
SIGNED=$(curl -XPOST ${URL}/sign -d "$TOSIGN" 2>/dev/null)
TX=$(curl -XPOST ${URL}/tx -d "$SIGNED" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo $TX; echo; fi
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
restAccount $SENDER "9007199254740000"
restAccount $RECV "992"
# Make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
# XXX Ex Usage: restCreateRole $PAYLOAD $EXPECTED
# Desc: Tests that the first returned signer.addr matches the expected
restCreateRole() {
assertNotNull "line=${LINENO}, data required" "$1"
ROLE=$(curl ${URL}/build/create_role --data "$1" 2>/dev/null)
if [ -n "$DEBUG" ]; then echo -e "$ROLE\n"; fi
assertEquals "line=${LINENO}, role required" "$2" $(echo $ROLE | jq .data.tx.data.signers[0].addr)
return $?
}
test03CreateRole() {
DATA="{\"role\": \"726f6c65\", \"seq\": 1, \"min_sigs\": 1, \"signers\": [{\"addr\": \"4FF759D47C81754D8F553DCCAC8651D0AF74C7F9\", \"app\": \"role\"}]}"
restCreateRole "$DATA" \""4FF759D47C81754D8F553DCCAC8651D0AF74C7F9"\"
}
test04CreateRoleInvalid() {
ERROR=$(curl ${URL}/build/create_role --data '{}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
ERROR=$(curl ${URL}/build/create_role --data '{"role": "foo"}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
ERROR=$(curl ${URL}/build/create_role --data '{"min_sigs": 2, "role": "abcdef"}' 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "failed" > /dev/null && echo 0 || echo 1)
## Non-hex roles should be rejected
ERROR=$(curl ${URL}/build/create_role --data "{\"role\": \"foobar\", \"seq\": 2, \"signers\": [{\"addr\": \"4FF759D47C81754D8F553DCCAC8651D0AF74C7F9\", \"app\": \"role\"}], \"min_sigs\": 1}" 2>/dev/null)
assertEquals "line=${LINENO}, should report validation failed" 0 $(echo $ERROR | grep "invalid hex" > /dev/null && echo 0 || echo 1)
}
# 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
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2
+87
View File
@@ -0,0 +1,87 @@
#!/bin/bash
# these are two globals to control all scripts (can use eg. counter instead)
SERVER_EXE=basecoin
CLIENT_EXE=basecli
ACCOUNTS=(jae ethan bucky rigel igor)
RICH=${ACCOUNTS[0]}
POOR=${ACCOUNTS[4]}
oneTimeSetUp() {
if ! quickSetup .basecoin_test_restart restart-chain; then
exit 1;
fi
}
oneTimeTearDown() {
quickTearDown
}
test00PreRestart() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=992mycoin --sequence=1 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkAccount $SENDER "9007199254740000"
checkAccount $RECV "992"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "992"
}
test01OnRestart() {
SENDER=$(getAddr $RICH)
RECV=$(getAddr $POOR)
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=10000mycoin --sequence=2 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
if [ $? != 0 ]; then echo "can't make tx!"; return 1; fi
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
# wait til we have quite a few blocks... like at least 20,
# so the query command won't just wait for the next eg. 7 blocks to verify the result
echo "waiting to generate lots of blocks..."
sleep 5
echo "done waiting!"
# last minute tx just at the block cut-off...
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=20000mycoin --sequence=3 --to=$RECV --name=$RICH)
txSucceeded $? "$TX" "$RECV"
if [ $? != 0 ]; then echo "can't make second tx!"; return 1; fi
# now we do a restart...
quickTearDown
startServer $BASE_DIR/server $BASE_DIR/${SERVER_EXE}.log
if [ $? != 0 ]; then echo "can't restart server!"; return 1; fi
# make sure queries still work properly, with all 3 tx now executed
echo "Checking state after restart..."
checkAccount $SENDER "9007199254710000"
checkAccount $RECV "30992"
# make sure tx is indexed
checkSendTx $HASH $TX_HEIGHT $SENDER "10000"
# for double-check of logs
if [ -n "$DEBUG" ]; then
cat $BASE_DIR/${SERVER_EXE}.log;
fi
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2
+98
View File
@@ -0,0 +1,98 @@
#!/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]}
ROLE="10CAFE4E"
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}
SIGS=2
assertFalse "line=${LINENO}, missing min-sigs" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=${ROLE} --members=${MEMBERS} --sequence=1 --name=$RICH"
assertFalse "line=${LINENO}, missing members" "echo qwertyuiop | ${CLIENT_EXE} tx create-role --role=${ROLE} --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=${ROLE} --min-sigs=$SIGS --members=${MEMBERS} --sequence=1 --name=$RICH)
txSucceeded $? "$TX" "${ROLE}"
HASH=$(echo $TX | jq .hash | tr -d \")
TX_HEIGHT=$(echo $TX | jq .height)
checkRole "${ROLE}" $SIGS 3
# Make sure tx is indexed
checkRoleTx $HASH $TX_HEIGHT "${ROLE}" 3
}
test02SendTxToRole() {
SENDER=$(getAddr $RICH)
RECV=role:${ROLE}
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx send --fee=90mycoin --amount=10000mycoin --to=$RECV --sequence=2 --name=$RICH)
txSucceeded $? "$TX" "${ROLE}"
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"
}
test03SendMultiFromRole() {
ONE=$(getAddr $RICH)
TWO=$(getAddr $POOR)
THREE=$(getAddr $DUDE)
BANK=role:${ROLE}
# no money to start mr. poor...
assertFalse "line=${LINENO}, has no money yet" "${CLIENT_EXE} query account $TWO 2>/dev/null"
# let's try to send money from the role directly without multisig
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --name=$POOR 2>/dev/null)
assertFalse "need to assume role" $?
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=2 --assume-role=${ROLE} --name=$POOR 2>/dev/null)
assertFalse "need two signatures" $?
# okay, begin a multisig transaction mr. poor...
TX_FILE=$BASE_DIR/tx.json
echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --assume-role=${ROLE} --name=$POOR --multi --prepare=$TX_FILE
assertTrue "line=${LINENO}, successfully prepare tx" $?
# and get some dude to sign it
# FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
# assertFalse "line=${LINENO}, double signing doesn't get bank" $?
# and get some dude to sign it for the full access
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$DUDE)
txSucceeded $? "$TX" "multi-bank"
checkAccount $TWO "6000"
checkAccount $BANK "4000"
}
# Load common then run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/common.sh
. $CLI_DIR/shunit2
+133
View File
@@ -0,0 +1,133 @@
#!/bin/bash
CLIENT_EXE=basecli
SERVER_EXE=basecoin
oneTimeSetUp() {
BASE=~/.bc_init_test
rm -rf "$BASE"
mkdir -p "$BASE"
SERVER="${BASE}/server"
SERVER_LOG="${BASE}/${SERVER_EXE}.log"
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
${SERVER_EXE} init ${HEX} --home="$SERVER" >> "$SERVER_LOG"
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
GENESIS_FILE=${SERVER}/genesis.json
CHAIN_ID=$(cat ${GENESIS_FILE} | jq .chain_id | tr -d \")
printf "starting ${SERVER_EXE}...\n"
${SERVER_EXE} start --home="$SERVER" >> "$SERVER_LOG" 2>&1 &
sleep 5
PID_SERVER=$!
disown
if ! ps $PID_SERVER >/dev/null; then
echo "**STARTUP FAILED**"
cat $SERVER_LOG
return 1
fi
# this sets the base for all client queries in the tests
export BCHOME=${BASE}/client
${CLIENT_EXE} init --node=tcp://localhost:46657 --genesis=${GENESIS_FILE} > /dev/null 2>&1
if ! assertTrue "line=${LINENO}, initialized light-client" "$?"; then
return 1
fi
}
oneTimeTearDown() {
printf "\nstopping ${SERVER_EXE}..."
kill -9 $PID_SERVER >/dev/null 2>&1
sleep 1
}
test01GetInsecure() {
GENESIS=$(${CLIENT_EXE} rpc genesis)
assertTrue "line=${LINENO}, get genesis" "$?"
MYCHAIN=$(echo ${GENESIS} | jq .genesis.chain_id | tr -d \")
assertEquals "line=${LINENO}, genesis chain matches" "${CHAIN_ID}" "${MYCHAIN}"
STATUS=$(${CLIENT_EXE} rpc status)
assertTrue "line=${LINENO}, get status" "$?"
SHEIGHT=$(echo ${STATUS} | jq .latest_block_height)
assertTrue "line=${LINENO}, parsed status" "$?"
assertNotNull "line=${LINENO}, has a height" "${SHEIGHT}"
VALS=$(${CLIENT_EXE} rpc validators)
assertTrue "line=${LINENO}, get validators" "$?"
VHEIGHT=$(echo ${VALS} | jq .block_height)
assertTrue "line=${LINENO}, parsed validators" "$?"
assertTrue "line=${LINENO}, sensible heights: $SHEIGHT / $VHEIGHT" "test $VHEIGHT -ge $SHEIGHT"
VCNT=$(echo ${VALS} | jq '.validators | length')
assertEquals "line=${LINENO}, one validator" "1" "$VCNT"
INFO=$(${CLIENT_EXE} rpc info)
assertTrue "line=${LINENO}, get info" "$?"
DATA=$(echo $INFO | jq .response.data)
assertEquals "line=${LINENO}, basecoin info" '"Basecoin v0.7.0-alpha"' "$DATA"
}
test02GetSecure() {
HEIGHT=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertTrue "line=${LINENO}, get status" "$?"
# check block produces something reasonable
assertFalse "line=${LINENO}, missing height" "${CLIENT_EXE} rpc block"
BLOCK=$(${CLIENT_EXE} rpc block --height=$HEIGHT)
assertTrue "line=${LINENO}, get block" "$?"
MHEIGHT=$(echo $BLOCK | jq .block_meta.header.height)
assertEquals "line=${LINENO}, meta height" "${HEIGHT}" "${MHEIGHT}"
BHEIGHT=$(echo $BLOCK | jq .block.header.height)
assertEquals "line=${LINENO}, meta height" "${HEIGHT}" "${BHEIGHT}"
# check commit produces something reasonable
assertFalse "line=${LINENO}, missing height" "${CLIENT_EXE} rpc commit"
let "CHEIGHT = $HEIGHT - 1"
COMMIT=$(${CLIENT_EXE} rpc commit --height=$CHEIGHT)
assertTrue "line=${LINENO}, get commit" "$?"
HHEIGHT=$(echo $COMMIT | jq .header.height)
assertEquals "line=${LINENO}, commit height" "${CHEIGHT}" "${HHEIGHT}"
assertEquals "line=${LINENO}, canonical" "true" $(echo $COMMIT | jq .canonical)
BSIG=$(echo $BLOCK | jq .block.last_commit)
CSIG=$(echo $COMMIT | jq .commit)
assertEquals "line=${LINENO}, block and commit" "$BSIG" "$CSIG"
# now let's get some headers
# assertFalse "missing height" "${CLIENT_EXE} rpc headers"
HEADERS=$(${CLIENT_EXE} rpc headers --min=$CHEIGHT --max=$HEIGHT)
assertTrue "line=${LINENO}, get headers" "$?"
assertEquals "line=${LINENO}, proper height" "$HEIGHT" $(echo $HEADERS | jq '.block_metas[0].header.height')
assertEquals "line=${LINENO}, two headers" "2" $(echo $HEADERS | jq '.block_metas | length')
# should we check these headers?
CHEAD=$(echo $COMMIT | jq .header)
# most recent first, so the commit header is second....
HHEAD=$(echo $HEADERS | jq .block_metas[1].header)
assertEquals "line=${LINENO}, commit and header" "$CHEAD" "$HHEAD"
}
test03Waiting() {
START=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertTrue "line=${LINENO}, get status" "$?"
let "NEXT = $START + 5"
assertFalse "line=${LINENO}, no args" "${CLIENT_EXE} rpc wait"
assertFalse "line=${LINENO}, too long" "${CLIENT_EXE} rpc wait --height=1234"
assertTrue "line=${LINENO}, normal wait" "${CLIENT_EXE} rpc wait --height=$NEXT"
STEP=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertEquals "line=${LINENO}, wait until height" "$NEXT" "$STEP"
let "NEXT = $STEP + 3"
assertTrue "line=${LINENO}, ${CLIENT_EXE} rpc wait --delta=3"
STEP=$(${CLIENT_EXE} rpc status | jq .latest_block_height)
assertEquals "line=${LINENO}, wait for delta" "$NEXT" "$STEP"
}
# load and run these tests with shunit2!
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
# TODO: how to handle this if we are not in the same directory
CLI_DIR=${DIR}/../../../../tests/cli
. $CLI_DIR/shunit2
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/tendermint/tmlibs/cli"
client "github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/examples/counter/plugins/counter"
)
+1 -1
View File
@@ -8,7 +8,7 @@ import (
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/cosmos/cosmos-sdk/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoin/commands"
)
// InitCmd - node initialization command
+1 -1
View File
@@ -7,7 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk"
client "github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoin/commands"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/eyes"
"github.com/cosmos/cosmos-sdk/stack"