diff --git a/Gopkg.lock b/Gopkg.lock index 56ae6e15..948f3a32 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,7 +2,6 @@ [[projects]] - branch = "master" digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" packages = ["."] @@ -49,7 +48,8 @@ revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" [[projects]] - digest = "1:bb617355120eed649354b41a20d1745b2feb994e4da1a97591259143ad65375a" + branch = "master" + digest = "1:64eb47e3881f808c83f0a3015f626d21ccc4a4eafbc530a6454e97296ededbef" name = "github.com/cosmos/cosmos-sdk" packages = [ "baseapp", @@ -58,6 +58,7 @@ "client/keys", "client/rpc", "client/tx", + "client/utils", "crypto", "crypto/keys", "crypto/keys/bcrypt", @@ -66,28 +67,33 @@ "server", "server/config", "store", + "tests", "types", "version", "wire", "x/auth", "x/auth/client/cli", + "x/auth/client/context", + "x/auth/client/rest", "x/bank", "x/bank/client", "x/bank/client/cli", - "x/gov", - "x/ibc", + "x/bank/client/rest", "x/mock", + "x/params", "x/slashing", "x/slashing/client/cli", + "x/slashing/client/rest", "x/stake", "x/stake/client/cli", + "x/stake/client/rest", "x/stake/keeper", "x/stake/tags", "x/stake/types", ] pruneopts = "UT" - revision = "23e3d5ac12145c02fcb4b4767d7dfccad782aee5" - version = "v0.23.1" + revision = "4a54b3ddbde412bea07250680a09fa6cbf1c3824" + source = "github.com/kava-labs/cosmos-sdk" [[projects]] digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" @@ -437,14 +443,6 @@ pruneopts = "UT" revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445" -[[projects]] - digest = "1:8b7ca9e3745cb049a65364d3adbbab63e98d3c43c83cb101cb450c8dac3c3492" - name = "github.com/tendermint/abci" - packages = ["types"] - pruneopts = "UT" - revision = "68592f4d8ee34e97db94b7a7976b1309efdb7eb9" - version = "v0.10.0" - [[projects]] branch = "master" digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" @@ -458,23 +456,12 @@ revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" [[projects]] - digest = "1:e9113641c839c21d8eaeb2c907c7276af1eddeed988df8322168c56b7e06e0e1" + digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "UT" - revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" - version = "0.10.1" - -[[projects]] - digest = "1:82f70bfe58f1a55601d469b928aec5b10b1d85710e6adde40dfea8d9adba939d" - name = "github.com/tendermint/go-crypto" - packages = [ - ".", - "tmhash", - ] - pruneopts = "UT" - revision = "6a6b591a3d7592a04b46af95451cb5be3b114f76" - version = "v0.9.0" + revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c" + version = "v0.12.0-rc0" [[projects]] digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2" @@ -485,7 +472,7 @@ version = "v0.9.2" [[projects]] - digest = "1:eb0f8bee357e6c28c9ad5fa074545b5085d0dcf580ba0e7024ab8c3285a5c815" + digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -548,35 +535,26 @@ "version", ] pruneopts = "UT" - revision = "d542d2c3945116697f60451e6a407082c41c3cc9" - version = "v0.22.8" + revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e" + version = "v0.23.1-rc0" [[projects]] - digest = "1:68921ddf468c0db04496dc49cc67948f1727a74520b1f1f06100bce5c65fa08b" - name = "github.com/tendermint/tmlibs" - packages = [ - "common", - "db", - "log", - ] - pruneopts = "UT" - revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38" - version = "v0.8.4" - -[[projects]] - digest = "1:5bd938386bd1f61a581bf8cd6ff2b7b2f79c542929176db4ceb44965440dae07" + digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" name = "github.com/zondax/ledger-goclient" packages = ["."] pruneopts = "UT" - revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b" + revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [[projects]] branch = "master" - digest = "1:65a21a9e051d54eb6a3f70c659a765f706a998d9287c302269f4ed8054b2a852" + digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5" name = "golang.org/x/crypto" packages = [ "blowfish", + "chacha20poly1305", "curve25519", + "hkdf", + "internal/chacha20", "internal/subtle", "nacl/box", "nacl/secretbox", @@ -608,9 +586,12 @@ [[projects]] branch = "master" - digest = "1:0316a8ad208917f1d141b077e278980fd5e4594f3a85f835dacbf24d85798252" + digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54" name = "golang.org/x/sys" - packages = ["unix"] + packages = [ + "cpu", + "unix", + ] pruneopts = "UT" revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" @@ -693,40 +674,57 @@ input-imports = [ "github.com/cosmos/cosmos-sdk/baseapp", "github.com/cosmos/cosmos-sdk/client", + "github.com/cosmos/cosmos-sdk/client/context", "github.com/cosmos/cosmos-sdk/client/keys", "github.com/cosmos/cosmos-sdk/client/rpc", "github.com/cosmos/cosmos-sdk/client/tx", + "github.com/cosmos/cosmos-sdk/client/utils", + "github.com/cosmos/cosmos-sdk/crypto/keys", "github.com/cosmos/cosmos-sdk/server", "github.com/cosmos/cosmos-sdk/server/config", + "github.com/cosmos/cosmos-sdk/tests", "github.com/cosmos/cosmos-sdk/types", "github.com/cosmos/cosmos-sdk/version", "github.com/cosmos/cosmos-sdk/wire", "github.com/cosmos/cosmos-sdk/x/auth", "github.com/cosmos/cosmos-sdk/x/auth/client/cli", + "github.com/cosmos/cosmos-sdk/x/auth/client/context", + "github.com/cosmos/cosmos-sdk/x/auth/client/rest", "github.com/cosmos/cosmos-sdk/x/bank", "github.com/cosmos/cosmos-sdk/x/bank/client/cli", - "github.com/cosmos/cosmos-sdk/x/gov", - "github.com/cosmos/cosmos-sdk/x/ibc", + "github.com/cosmos/cosmos-sdk/x/bank/client/rest", + "github.com/cosmos/cosmos-sdk/x/mock", + "github.com/cosmos/cosmos-sdk/x/params", "github.com/cosmos/cosmos-sdk/x/slashing", "github.com/cosmos/cosmos-sdk/x/slashing/client/cli", + "github.com/cosmos/cosmos-sdk/x/slashing/client/rest", "github.com/cosmos/cosmos-sdk/x/stake", "github.com/cosmos/cosmos-sdk/x/stake/client/cli", + "github.com/cosmos/cosmos-sdk/x/stake/client/rest", + "github.com/gorilla/mux", + "github.com/pkg/errors", "github.com/spf13/cobra", "github.com/spf13/pflag", "github.com/spf13/viper", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", - "github.com/tendermint/abci/types", - "github.com/tendermint/go-crypto", + "github.com/tendermint/go-amino", "github.com/tendermint/tendermint/abci/types", + "github.com/tendermint/tendermint/config", "github.com/tendermint/tendermint/crypto", + "github.com/tendermint/tendermint/crypto/ed25519", + "github.com/tendermint/tendermint/libs/bech32", "github.com/tendermint/tendermint/libs/cli", "github.com/tendermint/tendermint/libs/common", "github.com/tendermint/tendermint/libs/db", "github.com/tendermint/tendermint/libs/log", + "github.com/tendermint/tendermint/node", + "github.com/tendermint/tendermint/p2p", + "github.com/tendermint/tendermint/privval", + "github.com/tendermint/tendermint/proxy", + "github.com/tendermint/tendermint/rpc/core/types", + "github.com/tendermint/tendermint/rpc/lib/server", "github.com/tendermint/tendermint/types", - "github.com/tendermint/tmlibs/db", - "github.com/tendermint/tmlibs/log", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index e06b8bf1..bd062831 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,6 +1,7 @@ [[constraint]] name = "github.com/cosmos/cosmos-sdk" - version="=0.23.1" + branch="master" + source="github.com/kava-labs/cosmos-sdk" # Copied from cosmos-sdk, constraints switched to overrides @@ -35,7 +36,7 @@ [[override]] name = "github.com/tendermint/go-amino" - version = "=0.10.1" + version = "=v0.12.0-rc0" [[override]] name = "github.com/tendermint/iavl" @@ -43,16 +44,17 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "=v0.22.8" + version = "=v0.23.1-rc0" [[override]] name = "github.com/bartekn/go-bip39" - branch = "master" + revision = "a05967ea095d81c8fe4833776774cfaff8e5036c" [[override]] name = "github.com/zondax/ledger-goclient" - revision = "39ba4728c137c75718a21f9b4b3280fa31b9139b" + revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [prune] go-tests = true unused-packages = true + \ No newline at end of file diff --git a/README.md b/README.md index a8be4b41..c45341cc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Providing a base layer currency to settle interoperable payments at high through Proudly building on the work of [Cosmos](https://github.com/cosmos/cosmos-sdk) and [Interledger](https://github.com/interledger/rfcs). # Project Status -[![Testnet](https://img.shields.io/badge/testnet-live-brightgreen.svg)](http://validator.connector.kava.io:26657/abci_info) +[![Testnet](https://img.shields.io/badge/testnet-live-brightgreen.svg)](http://kava-test-3.node.connector.kava.io:17127/abci_info) [![Go Report Card](https://goreportcard.com/badge/github.com/kava-labs/kava)](https://goreportcard.com/report/github.com/kava-labs/kava) [![GitHub](https://img.shields.io/github/license/kava-labs/kava.svg)](https://github.com/Kava-Labs/kava/blob/master/LICENSE.md) @@ -27,5 +27,7 @@ If you're interested in being a validator join the chat and checkout the setup i # Find Out More - [Installation and Setup](docs/setup.md) + - [Upgrading to a New Testnet](docs/upgrade.md) - [Basic Usage](docs/usage.md) - [Payment Channels](docs/paychans.md) + diff --git a/cmd/kvcli/main.go b/cmd/kvcli/main.go index d125d58b..1e3ad2a4 100644 --- a/cmd/kvcli/main.go +++ b/cmd/kvcli/main.go @@ -16,14 +16,13 @@ import ( "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - //govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - //ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" - paychancmd "github.com/kava-labs/kava/internal/x/paychan/client/cmd" + paychancmd "github.com/kava-labs/kava/internal/x/paychan/client/cli" "github.com/kava-labs/kava/internal/app" - //"github.com/kava-labs/kava/internal/lcd" + "github.com/kava-labs/kava/internal/lcd" ) var ( @@ -53,17 +52,6 @@ func main() { ) tx.AddCommands(tendermintCmd, cdc) - // Add IBC commands - // ibcCmd := &cobra.Command{ - // Use: "ibc", - // Short: "Inter-Blockchain Communication subcommands", - // } - // ibcCmd.AddCommand( - // client.PostCommands( - // ibccmd.IBCTransferCmd(cdc), - // ibccmd.IBCRelayCmd(cdc), - // )...) - advancedCmd := &cobra.Command{ Use: "advanced", Short: "Advanced subcommands", @@ -71,8 +59,7 @@ func main() { advancedCmd.AddCommand( tendermintCmd, - //ibcCmd, - //lcd.ServeCommand(cdc), + lcd.ServeCommand(cdc), ) rootCmd.AddCommand( advancedCmd, @@ -90,6 +77,10 @@ func main() { stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc), + stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc), + stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc), + stakecmd.GetCmdQueryRedelegation("stake", cdc), + stakecmd.GetCmdQueryRedelegations("stake", cdc), slashingcmd.GetCmdQuerySigningInfo("slashing", cdc), )...) stakeCmd.AddCommand( @@ -105,27 +96,6 @@ func main() { stakeCmd, ) - // Add gov commands - // govCmd := &cobra.Command{ - // Use: "gov", - // Short: "Governance and voting subcommands", - // } - // govCmd.AddCommand( - // client.GetCommands( - // govcmd.GetCmdQueryProposal("gov", cdc), - // govcmd.GetCmdQueryVote("gov", cdc), - // govcmd.GetCmdQueryVotes("gov", cdc), - // )...) - // govCmd.AddCommand( - // client.PostCommands( - // govcmd.GetCmdSubmitProposal(cdc), - // govcmd.GetCmdDeposit(cdc), - // govcmd.GetCmdVote(cdc), - // )...) - // rootCmd.AddCommand( - // govCmd, - // ) - // Add auth and bank commands rootCmd.AddCommand( client.GetCommands( diff --git a/cmd/kvd/main.go b/cmd/kvd/main.go index 4bd9f3fa..bbaa9155 100644 --- a/cmd/kvd/main.go +++ b/cmd/kvd/main.go @@ -5,7 +5,12 @@ package main import ( "encoding/json" + "fmt" "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" "github.com/cosmos/cosmos-sdk/baseapp" @@ -44,6 +49,9 @@ func main() { server.AddCommands(ctx, cdc, rootCmd, appInit, appCreator, appExporter) + // Add custom init command + rootCmd.AddCommand(initTestnetCmd()) + // handle envs and add some flags and stuff executor := cli.PrepareBaseCmd(rootCmd, "KV", app.DefaultNodeHome) @@ -62,3 +70,130 @@ func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, traceStore io.W tempApp := app.NewKavaApp(logger, db, traceStore) return tempApp.ExportAppStateAndValidators() } + +func initTestnetCmd() *cobra.Command { + flagChainID := server.FlagChainID + flagName := server.FlagName + cmd := &cobra.Command{ + Use: "init-testnet", + Short: "Setup genesis and config to join testnet.", + Long: "Copy the genesis.json and config.toml files from the testnets folder into the default config directories. Also set the validator moniker.", + RunE: func(cmd *cobra.Command, args []string) error { + // This only works with default config locations + testnetVersion := viper.GetString(flagChainID) + genesisFileName := "genesis.json" + configFileName := "config.toml" + configPath := "config" + testnetsPath := os.ExpandEnv("$GOPATH/src/github.com/kava-labs/kava/testnets/") + + // Copy genesis file from testnet folder to config directories + // Copied to .kvcli to enable automatic reading of chain-id + genesis := filepath.Join(testnetsPath, testnetVersion, genesisFileName) + err := copyFile(genesis, filepath.Join(app.DefaultNodeHome, configPath, genesisFileName)) + if err != nil { + return err + } + err = copyFile(genesis, filepath.Join(app.DefaultCLIHome, configPath, genesisFileName)) + if err != nil { + return err + } + // Copy config file from testnet folder to config directories + // Custom config file specifies seeds and altered ports + // Also add back validator moniker to config file + config := filepath.Join(testnetsPath, testnetVersion, configFileName) + monikerPattern, err := regexp.Compile("moniker = \"[^\n]*\"") // anything that's not a new line + if err != nil { + return err + } + monikerReplaceString := fmt.Sprintf("moniker = \"%v\"", viper.GetString(flagName)) + + err = copyFile(config, filepath.Join(app.DefaultNodeHome, configPath, configFileName)) + if err != nil { + return err + } + err = replaceStringInFile( + filepath.Join(app.DefaultNodeHome, configPath, configFileName), + monikerPattern, + monikerReplaceString) + if err != nil { + return err + } + + err = copyFile(config, filepath.Join(app.DefaultCLIHome, configPath, configFileName)) + if err != nil { + return err + } + err = replaceStringInFile( + filepath.Join(app.DefaultCLIHome, configPath, configFileName), + monikerPattern, + monikerReplaceString) + if err != nil { + return err + } + + return nil + }, + } + cmd.Flags().String(flagChainID, "", "testnet chain-id, required") + cmd.Flags().String(flagName, "", "validator moniker, required") + return cmd +} + +func copyFile(src string, dst string) error { + // read in source file + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + // create destination file (and any necessary directories)(overwriting if it exists already) + path := filepath.Dir(dst) + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return err + } + out, err := os.Create(dst) + if err != nil { + return err + } + defer func() { + cerr := out.Close() + if err == nil { + err = cerr + } + }() + + // copy file contents + if _, err = io.Copy(out, in); err != nil { + return err + } + // write to disk + err = out.Sync() + return err +} + +// replaceStringInFile finds strings matching a regexp in a file and replaces them with a new string, before saving file. +func replaceStringInFile(filePath string, re *regexp.Regexp, replace string) error { + // get permissions of file + fileInfo, err := os.Stat(filePath) + if err != nil { + return err + } + + // read in file contents + in, err := ioutil.ReadFile(filePath) + if err != nil { + return err + } + + // replace string + newContents := re.ReplaceAll(in, []byte(replace)) + + // write file + err = ioutil.WriteFile(filePath, newContents, fileInfo.Mode()) + if err != nil { + return err + } + return nil +} diff --git a/docker-compose.yml b/docker-compose.yml index 4a4a2ddd..c62d9e03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: image: kava/kava command: ["kvd", "start"] ports: - - 26657:26657 + - 17127:17127 volumes: - ~/.kvd:/root/.kvd - ~/.kvcli:/root/.kvcli diff --git a/docs/paychans.md b/docs/paychans.md index aa5f19ec..9036dd56 100644 --- a/docs/paychans.md +++ b/docs/paychans.md @@ -10,12 +10,12 @@ This initial implementation is for unidirectional channels. Channels can be open ## Create a channel - kvcli paychan create --from --to --amount 100KVA --chain-id + kvcli paychan create --from --to --amount 100KVA ## Send an off-chain payment Send a payment for 10 KVA. - kvcli paychan pay --from --sen-amt 90KVA --rec-amt 10KVA --chan-id --filename payment.json --chain-id + kvcli paychan pay --from --sen-amt 90KVA --rec-amt 10KVA --chan-id --filename payment.json Send the file `payment.json` to your receiver. Then they run the following to verify. @@ -24,11 +24,11 @@ Send the file `payment.json` to your receiver. Then they run the following to ve ## Close a channel The receiver can close immediately at any time. - kvcli paychan submit --from --payment payment.json --chain-id + kvcli paychan submit --from --payment payment.json The sender can submit a close request, causing the channel will close automatically after a dispute period. During this period a receiver can still close immediately, overruling the sender's request. - kvcli paychan submit --from --payment payment.json --chain-id + kvcli paychan submit --from --payment payment.json >Note: The dispute period on the testnet is 30 seconds for ease of testing. diff --git a/docs/setup.md b/docs/setup.md index f015ba07..abb2c801 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -4,7 +4,7 @@ #### Who this guide is for The code currently consists of a full node daemon (`kvd`) and it's command line interface (`kvcli`). -Full nodes are fairly resource intensive and are designed to be run continuously on servers, primarily by people validating the network. While it is possible to run locally it is not recommended unless for development purposes. In the future light clients will enable secure transactions for clients. +Full nodes are fairly resource intensive and are designed to be run continuously on servers, primarily by people validating the network. While it is possible to run locally it is not recommended except for development purposes. In the future light clients will enable secure transactions for clients. A **full node** syncs with the blockchain and processes transactions. A **validator** is a full node that has declared itself to be a "validator" on chain. This obligates it to participate in consensus by proposing and signing blocks and maintaining uptime. By not following the protocol, the validator's stake will be slashed. @@ -14,22 +14,22 @@ Use these instructions to set up a full node, and to optionally declare yourself Requirements: go installed and set up (version 1.10+). - 0. If installing from a new Ubuntu server (16.04 or 18.04), here's how to setup go: - - sudo apt update - sudo apt upgrade -y - sudo apt install git gcc make wget -y - wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz - sudo tar -xvf go1.10.3.linux-amd64.tar.gz - sudo mv go /usr/local - - cat >> ~/.profile <If installing from a new Ubuntu server (16.04 or 18.04), here's how to setup go. +> +> sudo apt update +> sudo apt upgrade -y +> sudo apt install git gcc make wget -y +> wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz +> sudo tar -xvf go1.10.3.linux-amd64.tar.gz +> sudo mv go /usr/local +> +> cat >> ~/.profile < export GOROOT=/usr/local/go +> export GOPATH=\$HOME/go +> export PATH=\$GOPATH/bin:\$GOROOT/bin:\$PATH +> EOF +> +> source ~/.profile 1. Get the code. @@ -37,7 +37,6 @@ Requirements: go installed and set up (version 1.10+). cd $GOPATH/src/github.com/kava-labs git clone https://github.com/kava-labs/kava cd kava - git checkout 8c9406c 2. Install the dependencies. @@ -45,58 +44,59 @@ Requirements: go installed and set up (version 1.10+). curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure -vendor-only -3. Install the code + 3. Install the code. go install ./cmd/kvd go install ./cmd/kvcli ## Run a Full Node - kvd init --name --chain-id kava-test-2 + kvd init --name -This will generate config and keys in `$HOME/.kvd` and `$HOME/.kvcli`. The default password is 'password'. +Enter a new password for your validator key. This will generate generic config and private keys in `$HOME/.kvd` and `$HOME/.kvcli`. > Note: Make sure `GOBIN` is set and added to your path if you want to be able to run installed go programs from any folder. -Copy the testnet genesis file (from https://raw.githubusercontent.com/Kava-Labs/kava/master/testnets/kava-test-2/genesis.json) into `$HOME/.kvd/config/`, replacing the existing one. +Setup the correct config for the current testnet -Add the kava node address, `5c2bc5a95b014e4b2897791565398ee6bfd0a04a@validator.connector.kava.io:26656`, to `seeds` in `$HOME/.kvd/config/config.toml` + kvd init-testnet --name --chain-id kava-test-3 Start your full node kvd start -Or, to start in the background and capture output to a log file: +> Note: It might take a while to fully sync. Check the latest block height [here](http://validator.connector.kava.io:17127/abci_info). + +### Running in the Background +It's often more convenient to start `kvd` in the background and capture output to a log file. kvd start &> kvd.log & -To see the output of the log: +To see the output of the log. tail -f kvd.log -> Note: It might take a while to fully sync. Check the latest block height [here](http://validator.connector.kava.io:26657/abci_info). + ## Become a Validator -Join the [validator chat](https://riot.im/app/#/room/#kava-validators:matrix.org). Follow setup for a full node above. +Join the [validator chat](https://riot.im/app/#/room/#kava-validators:matrix.org). -Get you address with `kvcli keys list`. +Follow the setup for a full node above. -Ask @rhuairahrighairidh in the chat to give you some coins. +Get your address with `kvcli keys list` and your validator pubkey with `kvd tendermint show_validator`. -Get your validator pubkey with `kvd tendermint show_validator` +Get some testnet coins from [the faucet](http://kava-test-3.faucet.connector.kava.io). -Then, your full running in the background or separate window, run: +Then, with your full node running in the background or separate window, run: kvcli stake create-validator \ --amount 900KVA \ --pubkey \ - --address-validator \ --moniker "" \ - --chain-id kava-test-2 \ --from \ --gas 1000000 -> Note You'll need to type in the default password "password" +> Note You'll need to type in your password you set earlier. Now your full node should be participating in consensus and validating blocks! @@ -104,9 +104,7 @@ Running a validator requires that you keep validating blocks. If you stop, your In order to stop validating, first remove yourself as validator, then you can stop your node. kvcli stake unbond begin \ - --address-delegator \ --address-validator \ - --chain-id kava-test-2 \ --shares-percent 1 \ --from \ --gas 1000000 diff --git a/docs/upgrade.md b/docs/upgrade.md new file mode 100644 index 00000000..b570b3c9 --- /dev/null +++ b/docs/upgrade.md @@ -0,0 +1,27 @@ + +# Validator Upgrade + +These are some guidelines to upgrade to a new testnet if you where validating on a previous one. + + 1. Stop the current validator. + + 1. Remove the old config. + + rm -r $HOME/.kvd + rm -r $HOME/.kvcli + + 1. Get the latest code. + + cd $GOPATH/src/github.com/kava-labs/kava + git pull + + 1. Get the latest dependencies. + + dep ensure -vendor-only + + 1. Install. + + go install ./cmd/kvd + go install ./cmd/kvcli + + 1. Follow the installation instructions for running a full node and becoming a validator. diff --git a/docs/usage.md b/docs/usage.md index a24287fd..c6ed500e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,5 +1,6 @@ # Basic Usage >The following commands require communication with a full node. By default they expect one to be running locally (accessible on localhost), but a remote can be provided with the `--node` flag. + ## View Your Address List locally stored account addresses and their names. The name is used in other commands to specify which address to use to sign the transaction. @@ -13,7 +14,6 @@ List locally stored account addresses and their names. The name is used in other kvcli send --from \ --to
\ - --amount 100KVA \ - --chain-id kava-test- + --amount 100KVA diff --git a/internal/app/app.go b/internal/app/app.go index b6ac8043..fa69e198 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -19,8 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" - //"github.com/cosmos/cosmos-sdk/x/gov" - //"github.com/cosmos/cosmos-sdk/x/ibc" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/kava-labs/kava/internal/x/paychan" @@ -41,25 +40,23 @@ type KavaApp struct { cdc *wire.Codec // keys to access the substores - keyMain *sdk.KVStoreKey - keyAccount *sdk.KVStoreKey - - //keyIBC *sdk.KVStoreKey - keyStake *sdk.KVStoreKey - keySlashing *sdk.KVStoreKey - //keyGov *sdk.KVStoreKey + keyMain *sdk.KVStoreKey + keyAccount *sdk.KVStoreKey + keyStake *sdk.KVStoreKey + keySlashing *sdk.KVStoreKey keyFeeCollection *sdk.KVStoreKey + keyParams *sdk.KVStoreKey + tkeyParams *sdk.TransientStoreKey keyPaychan *sdk.KVStoreKey // keepers accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper + stakeKeeper stake.Keeper + slashingKeeper slashing.Keeper + paramsKeeper params.Keeper paychanKeeper paychan.Keeper - //ibcMapper ibc.Mapper - stakeKeeper stake.Keeper - slashingKeeper slashing.Keeper - //govKeeper gov.Keeper } // Creates a new KavaApp. @@ -69,49 +66,46 @@ func NewKavaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio cdc := CreateKavaAppCodec() // Create a new base app - bApp := bam.NewBaseApp(appName, cdc, logger, db, baseAppOptions...) + bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) // Create the kava app, extending baseApp var app = &KavaApp{ - BaseApp: bApp, - cdc: cdc, - keyMain: sdk.NewKVStoreKey("main"), - keyAccount: sdk.NewKVStoreKey("acc"), - keyPaychan: sdk.NewKVStoreKey("paychan"), - //keyIBC: sdk.NewKVStoreKey("ibc"), - keyStake: sdk.NewKVStoreKey("stake"), - keySlashing: sdk.NewKVStoreKey("slashing"), - //keyGov: sdk.NewKVStoreKey("gov"), + BaseApp: bApp, + cdc: cdc, + keyMain: sdk.NewKVStoreKey("main"), + keyAccount: sdk.NewKVStoreKey("acc"), + keyStake: sdk.NewKVStoreKey("stake"), + keySlashing: sdk.NewKVStoreKey("slashing"), keyFeeCollection: sdk.NewKVStoreKey("fee"), + keyParams: sdk.NewKVStoreKey("params"), + tkeyParams: sdk.NewTransientStoreKey("transient_params"), + keyPaychan: sdk.NewKVStoreKey("paychan"), } // Define the accountMapper and base account app.accountMapper = auth.NewAccountMapper( - cdc, + app.cdc, app.keyAccount, auth.ProtoBaseAccount, ) // Create the keepers app.coinKeeper = bank.NewKeeper(app.accountMapper) - app.paychanKeeper = paychan.NewKeeper(app.cdc, app.keyPaychan, app.coinKeeper) - //app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) + app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace)) - //app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) + app.paychanKeeper = paychan.NewKeeper(app.cdc, app.keyPaychan, app.coinKeeper) // Register the message handlers app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). - //AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)). AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). AddRoute("paychan", paychan.NewHandler(app.paychanKeeper)) - //AddRoute("gov", gov.NewHandler(app.govKeeper)) - // Set func to initialze the chain from appState in genesis file + // Set func to initialize the chain from appState in genesis file app.SetInitChainer(app.initChainer) // Set functions that run before and after txs / blocks @@ -120,7 +114,8 @@ func NewKavaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) // Mount stores - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing, app.keyFeeCollection, app.keyPaychan) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing, app.keyFeeCollection, app.keyParams, app.keyPaychan) + app.MountStore(app.tkeyParams, sdk.StoreTypeTransient) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) @@ -132,11 +127,9 @@ func NewKavaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio func CreateKavaAppCodec() *wire.Codec { cdc := wire.NewCodec() paychan.RegisterWire(cdc) - //ibc.RegisterWire(cdc) bank.RegisterWire(cdc) stake.RegisterWire(cdc) slashing.RegisterWire(cdc) - //gov.RegisterWire(cdc) auth.RegisterWire(cdc) sdk.RegisterWire(cdc) wire.RegisterCrypto(cdc) @@ -154,18 +147,18 @@ func (app *KavaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) ab // The function baseapp runs on receipt of a EndBlock ABCI message func (app *KavaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - paychan.EndBlocker(ctx, app.paychanKeeper) - validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) + tags := paychan.EndBlocker(ctx, app.paychanKeeper) - //tags, _ := gov.EndBlocker(ctx, app.govKeeper) + validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) + app.slashingKeeper.AddValidators(ctx, validatorUpdates) return abci.ResponseEndBlock{ ValidatorUpdates: validatorUpdates, - //Tags: tags, + Tags: tags, } } -// Initialzes the app db from the appState in the genesis file. Baseapp runs this on receipt of an InitChain ABCI message +// Initializes the app db from the appState in the genesis file. Baseapp runs this on receipt of an InitChain ABCI message func (app *KavaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { stateJSON := req.AppStateBytes @@ -183,14 +176,16 @@ func (app *KavaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci } // load the initial stake information - err = stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) + validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) if err != nil { panic(err) } - //gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState()) + slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) - return abci.ResponseInitChain{} + return abci.ResponseInitChain{ + Validators: validators, + } } // diff --git a/internal/app/genesis.go b/internal/app/genesis.go index 9646b48e..2f4cd056 100644 --- a/internal/app/genesis.go +++ b/internal/app/genesis.go @@ -6,7 +6,10 @@ package app import ( "encoding/json" "errors" + "fmt" + "time" + "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/pflag" "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" @@ -19,6 +22,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" ) +const defaultPassword = "password" + var ( // Tokens given to genesis validators and accounts numStartingTokensValidators = int64(1000) @@ -61,7 +66,7 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { } } -// Create the appInit stuct for server init command +// Create the appInit struct for server init command func KavaAppInit() server.AppInit { fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError) fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError) @@ -86,22 +91,41 @@ type KavaGenTx struct { PubKey string `json:"pub_key"` } -// Generate a genesis transsction +// Generate a genesis transaction func KavaAppGenTx(cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx) ( appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { - // Generate address and secret key for the validator + // check a name for the new validator has been specified if genTxConfig.Name == "" { return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)") } - var addr sdk.AccAddress - var secret string - addr, secret, err = server.GenerateSaveCoinKey(genTxConfig.CliRoot, genTxConfig.Name, "password", genTxConfig.Overwrite) + + // get a new password for the new account + buf := client.BufferStdin() + prompt := fmt.Sprintf("Password for account '%s' (default %s):", genTxConfig.Name, defaultPassword) + keyPass, err := client.GetPassword(prompt, buf) + if err != nil && keyPass != "" { + // An error was returned that either failed to read the password from + // STDIN or the given password is not empty but failed to meet minimum + // length requirements. + return appGenTx, cliPrint, validator, err + } + if keyPass == "" { + keyPass = defaultPassword + } + + // generate a new address and secret key + addr, secret, err := server.GenerateSaveCoinKey( + genTxConfig.CliRoot, + genTxConfig.Name, + defaultPassword, + genTxConfig.Overwrite, + ) if err != nil { return } - // Create string to print out + // Create message string to print out mm := map[string]string{"secret": secret} var bz []byte bz, err = cdc.MarshalJSON(mm) @@ -154,6 +178,8 @@ func KavaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState stakeData := stake.DefaultGenesisState() // change denom of staking coin stakeData.Params.BondDenom = "KVA" + // drastically shorten unbonding time for test purposes + stakeData.Params.UnbondingTime = time.Second * 60 * 5 // 5 minutes // get genesis flag account information genaccs := make([]GenesisAccount, len(appGenTxs)) diff --git a/internal/lcd/decode.go b/internal/lcd/decode.go index 59bb5f9a..a932e611 100644 --- a/internal/lcd/decode.go +++ b/internal/lcd/decode.go @@ -1,27 +1,24 @@ package lcd -/* import ( - "net/http" - //"encoding/hex" "encoding/base64" "encoding/json" + "net/http" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" ) -*/ -type TxBody struct { +type txBody struct { TxBase64 string `json:"txbase64"` } // Decode a tx from base64 into json -func DecodeTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc { +func DecodeTxRequestHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // get the input base64 string - var m TxBody + var m txBody decoder := json.NewDecoder(r.Body) err := decoder.Decode(&m) if err != nil { diff --git a/internal/lcd/lcd_test.go b/internal/lcd/lcd_test.go new file mode 100644 index 00000000..961eef54 --- /dev/null +++ b/internal/lcd/lcd_test.go @@ -0,0 +1,695 @@ +// Copyright 2016 All in Bits, inc +// Modifications copyright 2018 Kava Labs +package lcd + +import ( + "encoding/hex" + "fmt" + "net/http" + "regexp" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/client/tx" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" + p2p "github.com/tendermint/tendermint/p2p" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + client "github.com/cosmos/cosmos-sdk/client" + keys "github.com/cosmos/cosmos-sdk/client/keys" + rpc "github.com/cosmos/cosmos-sdk/client/rpc" + tests "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/client/rest" +) + +func init() { + cryptoKeys.BcryptSecurityParameter = 1 +} + +func TestKeys(t *testing.T) { + name, password := "test", "1234567890" + addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + // get seed + // TODO Do we really need this endpoint? + res, body := Request(t, port, "GET", "/keys/seed", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + reg, err := regexp.Compile(`([a-z]+ ){12}`) + require.Nil(t, err) + match := reg.MatchString(seed) + require.True(t, match, "Returned seed has wrong format", seed) + + newName := "test_newname" + newPassword := "0987654321" + + // add key + jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed)) + res, body = Request(t, port, "POST", "/keys", jsonStr) + + require.Equal(t, http.StatusOK, res.StatusCode, body) + var resp keys.KeyOutput + err = wire.Cdc.UnmarshalJSON([]byte(body), &resp) + require.Nil(t, err, body) + + addr2Bech32 := resp.Address.String() + _, err = sdk.AccAddressFromBech32(addr2Bech32) + require.NoError(t, err, "Failed to return a correct bech32 address") + + // test if created account is the correct account + expectedInfo, _ := GetKeyBase(t).CreateKey(newName, seed, newPassword) + expectedAccount := sdk.AccAddress(expectedInfo.GetPubKey().Address().Bytes()) + assert.Equal(t, expectedAccount.String(), addr2Bech32) + + // existing keys + res, body = Request(t, port, "GET", "/keys", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var m [2]keys.KeyOutput + err = cdc.UnmarshalJSON([]byte(body), &m) + require.Nil(t, err) + + addrBech32 := addr.String() + + require.Equal(t, name, m[0].Name, "Did not serve keys name correctly") + require.Equal(t, addrBech32, m[0].Address.String(), "Did not serve keys Address correctly") + require.Equal(t, newName, m[1].Name, "Did not serve keys name correctly") + require.Equal(t, addr2Bech32, m[1].Address.String(), "Did not serve keys Address correctly") + + // select key + keyEndpoint := fmt.Sprintf("/keys/%s", newName) + res, body = Request(t, port, "GET", keyEndpoint, nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var m2 keys.KeyOutput + err = cdc.UnmarshalJSON([]byte(body), &m2) + require.Nil(t, err) + + require.Equal(t, newName, m2.Name, "Did not serve keys name correctly") + require.Equal(t, addr2Bech32, m2.Address.String(), "Did not serve keys Address correctly") + + // update key + jsonStr = []byte(fmt.Sprintf(`{ + "old_password":"%s", + "new_password":"12345678901" + }`, newPassword)) + + res, body = Request(t, port, "PUT", keyEndpoint, jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + // here it should say unauthorized as we changed the password before + res, body = Request(t, port, "PUT", keyEndpoint, jsonStr) + require.Equal(t, http.StatusUnauthorized, res.StatusCode, body) + + // delete key + jsonStr = []byte(`{"password":"12345678901"}`) + res, body = Request(t, port, "DELETE", keyEndpoint, jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) +} + +func TestVersion(t *testing.T) { + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + // node info + res, body := Request(t, port, "GET", "/version", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`) + require.Nil(t, err) + match := reg.MatchString(body) + require.True(t, match, body) + + // node info + res, body = Request(t, port, "GET", "/node_version", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`) + require.Nil(t, err) + match = reg.MatchString(body) + require.True(t, match, body) +} + +func TestNodeStatus(t *testing.T) { + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + // node info + res, body := Request(t, port, "GET", "/node_info", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var nodeInfo p2p.NodeInfo + err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) + require.Nil(t, err, "Couldn't parse node info") + + require.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res) + + // syncing + res, body = Request(t, port, "GET", "/syncing", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + // we expect that there is no other node running so the syncing state is "false" + require.Equal(t, "false", body) +} + +func TestBlock(t *testing.T) { + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + var resultBlock ctypes.ResultBlock + + res, body := Request(t, port, "GET", "/blocks/latest", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &resultBlock) + require.Nil(t, err, "Couldn't parse block") + + require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) + + // -- + + res, body = Request(t, port, "GET", "/blocks/1", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = wire.Cdc.UnmarshalJSON([]byte(body), &resultBlock) + require.Nil(t, err, "Couldn't parse block") + + require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) + + // -- + + res, body = Request(t, port, "GET", "/blocks/1000000000", nil) + require.Equal(t, http.StatusNotFound, res.StatusCode, body) +} + +func TestValidators(t *testing.T) { + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + var resultVals rpc.ResultValidatorsOutput + + res, body := Request(t, port, "GET", "/validatorsets/latest", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err := cdc.UnmarshalJSON([]byte(body), &resultVals) + require.Nil(t, err, "Couldn't parse validatorset") + + require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) + + require.Contains(t, resultVals.Validators[0].Address.String(), "kvaladdr") + require.Contains(t, resultVals.Validators[0].PubKey, "kvalpub") + + // -- + + res, body = Request(t, port, "GET", "/validatorsets/1", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &resultVals) + require.Nil(t, err, "Couldn't parse validatorset") + + require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals) + + // -- + + res, body = Request(t, port, "GET", "/validatorsets/1000000000", nil) + require.Equal(t, http.StatusNotFound, res.StatusCode, body) +} + +func TestCoinSend(t *testing.T) { + name, password := "test", "1234567890" + addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6") + require.NoError(t, err) + someFakeAddr := sdk.AccAddress(bz) + + // query empty + res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", someFakeAddr), nil) + require.Equal(t, http.StatusNoContent, res.StatusCode, body) + + acc := getAccount(t, port, addr) + initialBalance := acc.GetCoins() + + // create TX + receiveAddr, resultTx := doSend(t, port, seed, name, password, addr) + tests.WaitForHeight(resultTx.Height+1, port) + + // check if tx was committed + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // query sender + acc = getAccount(t, port, addr) + coins := acc.GetCoins() + mycoins := coins[0] + + require.Equal(t, "KVA", mycoins.Denom) + require.Equal(t, initialBalance[0].Amount.SubRaw(1), mycoins.Amount) + + // query receiver + acc = getAccount(t, port, receiveAddr) + coins = acc.GetCoins() + mycoins = coins[0] + + require.Equal(t, "KVA", mycoins.Denom) + require.Equal(t, int64(1), mycoins.Amount.Int64()) +} + +func TestTxs(t *testing.T) { + name, password := "test", "1234567890" + addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + // query wrong + res, body := Request(t, port, "GET", "/txs", nil) + require.Equal(t, http.StatusBadRequest, res.StatusCode, body) + + // query empty + res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "kaccaddr1z2my792ekdthxfkwyks0pwl69jucf9wjq3txwj"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.Equal(t, "[]", body) + + // create TX + receiveAddr, resultTx := doSend(t, port, seed, name, password, addr) + + tests.WaitForHeight(resultTx.Height+1, port) + + // check if tx is findable + res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var indexedTxs []tx.Info + + // check if tx is queryable + res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NotEqual(t, "[]", body) + + err := cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + require.Equal(t, 1, len(indexedTxs)) + + // XXX should this move into some other testfile for txs in general? + // test if created TX hash is the correct hash + require.Equal(t, resultTx.Hash, indexedTxs[0].Hash) + + // query sender + // also tests url decoding + res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32=%%27%s%%27", addr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + require.Equal(t, 1, len(indexedTxs), "%v", indexedTxs) // there are 2 txs created with doSend + require.Equal(t, resultTx.Height, indexedTxs[0].Height) + + // query recipient + res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &indexedTxs) + require.NoError(t, err) + require.Equal(t, 1, len(indexedTxs)) + require.Equal(t, resultTx.Height, indexedTxs[0].Height) +} + +func TestValidatorsQuery(t *testing.T) { + cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + require.Equal(t, 1, len(pks)) + + validators := getValidators(t, port) + require.Equal(t, len(validators), 1) + + // make sure all the validators were found (order unknown because sorted by owner addr) + foundVal := false + pkBech := sdk.MustBech32ifyValPub(pks[0]) + if validators[0].PubKey == pkBech { + foundVal = true + } + require.True(t, foundVal, "pkBech %v, owner %v", pkBech, validators[0].Owner) +} + +func TestValidatorQuery(t *testing.T) { + cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + require.Equal(t, 1, len(pks)) + + validator1Owner := sdk.AccAddress(pks[0].Address()) + + validator := getValidator(t, port, validator1Owner) + bech32ValAddress, err := sdk.Bech32ifyValPub(pks[0]) + require.NoError(t, err) + assert.Equal(t, validator.PubKey, bech32ValAddress, "The returned validator does not hold the correct data") +} + +func TestBonding(t *testing.T) { + name, password, denom := "test", "1234567890", "KVA" + addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + validator1Owner := sdk.AccAddress(pks[0].Address()) + + // create bond TX + resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner) + tests.WaitForHeight(resultTx.Height+1, port) + + // check if tx was committed + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // query sender + acc := getAccount(t, port, addr) + coins := acc.GetCoins() + + require.Equal(t, int64(40), coins.AmountOf(denom).Int64()) + + // query validator + bond := getDelegation(t, port, addr, validator1Owner) + require.Equal(t, "60.0000000000", bond.Shares) + + ////////////////////// + // testing unbonding + + // create unbond TX + resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner) + tests.WaitForHeight(resultTx.Height+1, port) + + // query validator + bond = getDelegation(t, port, addr, validator1Owner) + require.Equal(t, "30.0000000000", bond.Shares) + + // check if tx was committed + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // should the sender should have not received any coins as the unbonding has only just begun + // query sender + acc = getAccount(t, port, addr) + coins = acc.GetCoins() + require.Equal(t, int64(40), coins.AmountOf("KVA").Int64()) + + // query unbonding delegation + validatorAddr := sdk.AccAddress(pks[0].Address()) + unbondings := getUndelegations(t, port, addr, validatorAddr) + assert.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") + assert.Equal(t, "30", unbondings[0].Balance.Amount.String()) + + // query summary + summary := getDelegationSummary(t, port, addr) + + assert.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations") + assert.Equal(t, "30.0000000000", summary.Delegations[0].Shares) + assert.Len(t, summary.UnbondingDelegations, 1, "Delegation summary holds all unbonding-delegations") + assert.Equal(t, "30", summary.UnbondingDelegations[0].Balance.Amount.String()) + + // TODO add redelegation, need more complex capabilities such to mock context and + // TODO check summary for redelegation + // assert.Len(t, summary.Redelegations, 1, "Delegation summary holds all redelegations") + + // query txs + txs := getBondingTxs(t, port, addr, "") + assert.Len(t, txs, 2, "All Txs found") + + txs = getBondingTxs(t, port, addr, "bond") + assert.Len(t, txs, 1, "All bonding txs found") + + txs = getBondingTxs(t, port, addr, "unbond") + assert.Len(t, txs, 1, "All unbonding txs found") +} + +func TestUnrevoke(t *testing.T) { + _, password := "test", "1234567890" + addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) + cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + // XXX: any less than this and it fails + tests.WaitForHeight(3, port) + pkString, _ := sdk.Bech32ifyValPub(pks[0]) + signingInfo := getSigningInfo(t, port, pkString) + tests.WaitForHeight(4, port) + require.Equal(t, true, signingInfo.IndexOffset > 0) + require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil) + require.Equal(t, true, signingInfo.SignedBlocksCounter > 0) +} + +//_____________________________________________________________________________ +// get the account to get the sequence +func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { + res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", addr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var acc auth.Account + err := cdc.UnmarshalJSON([]byte(body), &acc) + require.Nil(t, err) + return acc +} + +func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { + + // create receive address + kb := client.MockKeyBase() + receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1")) + require.Nil(t, err) + receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) + + acc := getAccount(t, port, addr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + // send + coinbz, err := cdc.MarshalJSON(sdk.NewInt64Coin("KVA", 1)) + if err != nil { + panic(err) + } + + jsonStr := []byte(fmt.Sprintf(`{ + "name":"%s", + "password":"%s", + "account_number":"%d", + "sequence":"%d", + "gas": "10000", + "amount":[%s], + "chain_id":"%s" + }`, name, password, accnum, sequence, coinbz, chainID)) + res, body := Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + err = cdc.UnmarshalJSON([]byte(body), &resultTx) + require.Nil(t, err) + + return receiveAddr, resultTx +} + +func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { + res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/signing_info/%s", validatorPubKey), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var signingInfo slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(body), &signingInfo) + require.Nil(t, err) + return signingInfo +} + +// ============= Stake Module ================ + +func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) rest.DelegationWithoutRat { + + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var bond rest.DelegationWithoutRat + err := cdc.UnmarshalJSON([]byte(body), &bond) + require.Nil(t, err) + return bond +} + +func getUndelegations(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) []stake.UnbondingDelegation { + + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var unbondings []stake.UnbondingDelegation + err := cdc.UnmarshalJSON([]byte(body), &unbondings) + require.Nil(t, err) + return unbondings +} + +func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary { + + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var summary rest.DelegationSummary + err := cdc.UnmarshalJSON([]byte(body), &summary) + require.Nil(t, err) + return summary +} + +func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { + + // get the account to get the sequence + var res *http.Response + var body string + if len(query) > 0 { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil) + } else { + res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil) + } + require.Equal(t, http.StatusOK, res.StatusCode, body) + var txs []tx.Info + err := cdc.UnmarshalJSON([]byte(body), &txs) + require.Nil(t, err) + return txs +} + +func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { + // get the account to get the sequence + acc := getAccount(t, port, delegatorAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + + // send + jsonStr := []byte(fmt.Sprintf(`{ + "name": "%s", + "password": "%s", + "account_number": "%d", + "sequence": "%d", + "gas": "10000", + "chain_id": "%s", + "delegations": [ + { + "delegator_addr": "%s", + "validator_addr": "%s", + "delegation": { "denom": "%s", "amount": "60" } + } + ], + "begin_unbondings": [], + "complete_unbondings": [], + "begin_redelegates": [], + "complete_redelegates": [] + }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, "KVA")) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err := cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +func doBeginUnbonding(t *testing.T, port, seed, name, password string, + delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { + + // get the account to get the sequence + acc := getAccount(t, port, delegatorAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + + // send + jsonStr := []byte(fmt.Sprintf(`{ + "name": "%s", + "password": "%s", + "account_number": "%d", + "sequence": "%d", + "gas": "20000", + "chain_id": "%s", + "delegations": [], + "begin_unbondings": [ + { + "delegator_addr": "%s", + "validator_addr": "%s", + "shares": "30" + } + ], + "complete_unbondings": [], + "begin_redelegates": [], + "complete_redelegates": [] + }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr)) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err := cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +func doBeginRedelegation(t *testing.T, port, seed, name, password string, + delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) { + + // get the account to get the sequence + acc := getAccount(t, port, delegatorAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + chainID := viper.GetString(client.FlagChainID) + + // send + jsonStr := []byte(fmt.Sprintf(`{ + "name": "%s", + "password": "%s", + "account_number": "%d", + "sequence": "%d", + "gas": "10000", + "chain_id": "%s", + "delegations": [], + "begin_unbondings": [], + "complete_unbondings": [], + "begin_redelegates": [ + { + "delegator_addr": "%s", + "validator_src_addr": "%s", + "validator_dst_addr": "%s", + "shares": "30" + } + ], + "complete_redelegates": [] + }`, name, password, accnum, sequence, chainID, delegatorAddr, validatorSrcAddr, validatorDstAddr)) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results []ctypes.ResultBroadcastTxCommit + err := cdc.UnmarshalJSON([]byte(body), &results) + require.Nil(t, err) + + return results[0] +} + +func getValidators(t *testing.T, port string) []stake.BechValidator { + // get the account to get the sequence + res, body := Request(t, port, "GET", "/stake/validators", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var validators []stake.BechValidator + err := cdc.UnmarshalJSON([]byte(body), &validators) + require.Nil(t, err) + return validators +} + +func getValidator(t *testing.T, port string, validatorAddr sdk.AccAddress) stake.BechValidator { + // get the account to get the sequence + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var validator stake.BechValidator + err := cdc.UnmarshalJSON([]byte(body), &validator) + require.Nil(t, err) + return validator +} diff --git a/internal/lcd/root.go b/internal/lcd/root.go index d879f4f0..50d22bdb 100644 --- a/internal/lcd/root.go +++ b/internal/lcd/root.go @@ -3,32 +3,27 @@ package lcd -/* import ( "net/http" "os" - "github.com/gorilla/mux" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tmlibs/log" - - tmserver "github.com/tendermint/tendermint/rpc/lib/server" - cmn "github.com/tendermint/tmlibs/common" - client "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" keys "github.com/cosmos/cosmos-sdk/client/keys" rpc "github.com/cosmos/cosmos-sdk/client/rpc" tx "github.com/cosmos/cosmos-sdk/client/tx" - version "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - //ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" - //stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" + slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" + "github.com/gorilla/mux" + "github.com/spf13/cobra" + "github.com/spf13/viper" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + tmserver "github.com/tendermint/tendermint/rpc/lib/server" ) -*/ // ServeCommand will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar @@ -36,6 +31,7 @@ import ( func ServeCommand(cdc *wire.Codec) *cobra.Command { flagListenAddr := "laddr" flagCORS := "cors" + flagMaxOpenConnections := "max-open" cmd := &cobra.Command{ Use: "rest-server", @@ -43,48 +39,59 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { listenAddr := viper.GetString(flagListenAddr) handler := createHandler(cdc) - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). - With("module", "rest-server") - listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") + maxOpen := viper.GetInt(flagMaxOpenConnections) + + listener, err := tmserver.StartHTTPServer( + listenAddr, handler, logger, + tmserver.Config{MaxOpenConnections: maxOpen}, + ) if err != nil { return err } + logger.Info("REST server started") - // Wait forever and cleanup + // wait forever and cleanup cmn.TrapSignal(func() { err := listener.Close() - logger.Error("Error closing listener", "err", err) + logger.Error("error closing listener", "err", err) }) + return nil }, } - cmd.Flags().StringP(flagListenAddr, "a", "tcp://localhost:1317", "Address for server to listen on") - cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") - cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + + cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") + cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") + cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") + cmd.Flags().String(client.FlagNode, "tcp://localhost:17127", "Address of the node to connect to") + cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") + return cmd } func createHandler(cdc *wire.Codec) http.Handler { r := mux.NewRouter() - r.HandleFunc("/version", version.RequestHandler).Methods("GET") kb, err := keys.GetKeyBase() //XXX if err != nil { panic(err) } - ctx := context.NewCoreContextFromViper() + cliCtx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout) + + r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") + r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") + r.HandleFunc("/decode_tx", DecodeTxRequestHandlerFn(cliCtx, cdc)).Methods("POST") - // TODO make more functional? aka r = keys.RegisterRoutes(r) keys.RegisterRoutes(r) - rpc.RegisterRoutes(ctx, r) - tx.RegisterRoutes(ctx, r, cdc) - auth.RegisterRoutes(ctx, r, cdc, "acc") - bank.RegisterRoutes(ctx, r, cdc, kb) - //ibc.RegisterRoutes(ctx, r, cdc, kb) - //stake.RegisterRoutes(ctx, r, cdc, kb) - r.HandleFunc("/decode_tx", DecodeTxRequestHandlerFn(ctx, cdc)).Methods("POST") + rpc.RegisterRoutes(cliCtx, r) + tx.RegisterRoutes(cliCtx, r, cdc) + auth.RegisterRoutes(cliCtx, r, cdc, "acc") + bank.RegisterRoutes(cliCtx, r, cdc, kb) + stake.RegisterRoutes(cliCtx, r, cdc, kb) + slashing.RegisterRoutes(cliCtx, r, cdc, kb) + return r } diff --git a/internal/lcd/test_helpers.go b/internal/lcd/test_helpers.go new file mode 100644 index 00000000..03a8f30f --- /dev/null +++ b/internal/lcd/test_helpers.go @@ -0,0 +1,275 @@ +// Copyright 2016 All in Bits, inc +// Modifications copyright 2018 Kava Labs + +package lcd + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/client" + keys "github.com/cosmos/cosmos-sdk/client/keys" + crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" + kapp "github.com/kava-labs/kava/internal/app" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + tmcfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/cli" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + nm "github.com/tendermint/tendermint/node" + pvm "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" + tmrpc "github.com/tendermint/tendermint/rpc/lib/server" + tmtypes "github.com/tendermint/tendermint/types" +) + +// makePathname creates a unique pathname for each test. It will panic if it +// cannot get the current working directory. +func makePathname() string { + p, err := os.Getwd() + if err != nil { + panic(err) + } + + sep := string(filepath.Separator) + return strings.Replace(p, sep, "_", -1) +} + +// GetConfig returns a Tendermint config for the test cases. +func GetConfig() *tmcfg.Config { + pathname := makePathname() + config := tmcfg.ResetTestRoot(pathname) + + tmAddr, _, err := server.FreeTCPAddr() + if err != nil { + panic(err) + } + + rcpAddr, _, err := server.FreeTCPAddr() + if err != nil { + panic(err) + } + + grpcAddr, _, err := server.FreeTCPAddr() + if err != nil { + panic(err) + } + + config.P2P.ListenAddress = tmAddr + config.RPC.ListenAddress = rcpAddr + config.RPC.GRPCListenAddress = grpcAddr + + return config +} + +// GetKeyBase returns the LCD test keybase. It also requires that a directory +// could be made and a keybase could be fetched. +// +// NOTE: memDB cannot be used because the request is expecting to interact with +// the default location. +func GetKeyBase(t *testing.T) crkeys.Keybase { + dir, err := ioutil.TempDir("", "lcd_test") + require.NoError(t, err) + + viper.Set(cli.HomeFlag, dir) + + keybase, err := keys.GetKeyBase() + require.NoError(t, err) + + return keybase +} + +// CreateAddr adds an address to the key store and returns an address and seed. +// It also requires that the key could be created. +func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) { + var ( + err error + info crkeys.Info + seed string + ) + + info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1) + require.NoError(t, err) + + return sdk.AccAddress(info.GetPubKey().Address()), seed +} + +// InitializeTestLCD starts Tendermint and the LCD in process, listening on +// their respective sockets where nValidators is the total number of validators +// and initAddrs are the accounts to initialize with some KVA tokens. It +// returns a cleanup function, a set of validator public keys, and a port. +func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) { + config := GetConfig() + config.Consensus.TimeoutCommit = 100 + config.Consensus.SkipTimeoutCommit = false + config.TxIndex.IndexAllTags = true + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger = log.NewFilter(logger, log.AllowError()) + + privValidatorFile := config.PrivValidatorFile() + privVal := pvm.LoadOrGenFilePV(privValidatorFile) + privVal.Reset() + + db := dbm.NewMemDB() + app := kapp.NewKavaApp(logger, db, nil) + cdc = kapp.CreateKavaAppCodec() + + genesisFile := config.GenesisFile() + genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) + require.NoError(t, err) + + if nValidators < 1 { + panic("InitializeTestLCD must use at least one validator") + } + + for i := 1; i < nValidators; i++ { + genDoc.Validators = append(genDoc.Validators, + tmtypes.GenesisValidator{ + PubKey: ed25519.GenPrivKey().PubKey(), + Power: 1, + Name: "val", + }, + ) + } + + var validatorsPKs []crypto.PubKey + + // NOTE: It's bad practice to reuse public key address for the owner + // address but doing in the test for simplicity. + var appGenTxs []json.RawMessage + for _, gdValidator := range genDoc.Validators { + pk := gdValidator.PubKey + validatorsPKs = append(validatorsPKs, pk) + + appGenTx, _, _, err := kapp.KavaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1") + require.NoError(t, err) + + appGenTxs = append(appGenTxs, appGenTx) + } + + genesisState, err := kapp.KavaAppGenState(cdc, appGenTxs[:]) + require.NoError(t, err) + + // add some tokens to init accounts + for _, addr := range initAddrs { + accAuth := auth.NewBaseAccountWithAddress(addr) + accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("KVA", 100)} + acc := kapp.NewGenesisAccount(&accAuth) + genesisState.Accounts = append(genesisState.Accounts, acc) + genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100)) + } + + appState, err := wire.MarshalJSONIndent(cdc, genesisState) + require.NoError(t, err) + genDoc.AppState = appState + + listenAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + + // XXX: Need to set this so LCD knows the tendermint node address! + viper.Set(client.FlagNode, config.RPC.ListenAddress) + viper.Set(client.FlagChainID, genDoc.ChainID) + + node, err := startTM(config, logger, genDoc, privVal, app) + require.NoError(t, err) + + lcd, err := startLCD(logger, listenAddr, cdc) + require.NoError(t, err) + + tests.WaitForLCDStart(port) + tests.WaitForHeight(1, port) + + cleanup := func() { + logger.Debug("cleaning up LCD initialization") + node.Stop() + node.Wait() + lcd.Close() + } + + return cleanup, validatorsPKs, port +} + +// startTM creates and starts an in-process Tendermint node with memDB and +// in-process ABCI application. It returns the new node or any error that +// occurred. +// +// TODO: Clean up the WAL dir or enable it to be not persistent! +func startTM( + tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, + privVal tmtypes.PrivValidator, app abci.Application, +) (*nm.Node, error) { + genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil } + dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil } + node, err := nm.NewNode( + tmcfg, + privVal, + proxy.NewLocalClientCreator(app), + genDocProvider, + dbProvider, + nm.DefaultMetricsProvider(tmcfg.Instrumentation), + logger.With("module", "node"), + ) + if err != nil { + return nil, err + } + + err = node.Start() + if err != nil { + return nil, err + } + + tests.WaitForRPC(tmcfg.RPC.ListenAddress) + logger.Info("Tendermint running!") + + return node, err +} + +// startLCD starts the LCD. +// +// NOTE: This causes the thread to block. +func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) { + return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) +} + +// Request makes a test LCD test request. It returns a response object and a +// stringified response body. +func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) { + var ( + err error + res *http.Response + ) + url := fmt.Sprintf("http://localhost:%v%v", port, path) + fmt.Println("REQUEST " + method + " " + url) + + req, err := http.NewRequest(method, url, bytes.NewBuffer(payload)) + require.Nil(t, err) + + res, err = http.DefaultClient.Do(req) + require.Nil(t, err) + + output, err := ioutil.ReadAll(res.Body) + res.Body.Close() + require.Nil(t, err) + + return res, string(output) +} diff --git a/internal/lcd/version.go b/internal/lcd/version.go new file mode 100644 index 00000000..d385730d --- /dev/null +++ b/internal/lcd/version.go @@ -0,0 +1,32 @@ +// Copyright 2016 All in Bits, inc +// Modifications copyright 2018 Kava Labs + +package lcd + +import ( + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/version" +) + +// cli version REST handler endpoint +func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { + v := version.GetVersion() + w.Write([]byte(v)) +} + +// connected node version REST handler endpoint +func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + version, err := cliCtx.Query("/app/version") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error()))) + return + } + + w.Write(version) + } +} diff --git a/internal/lcd/wire.go b/internal/lcd/wire.go index c5d4c7cf..46295b47 100644 --- a/internal/lcd/wire.go +++ b/internal/lcd/wire.go @@ -1,11 +1,12 @@ +// Copyright 2016 All in Bits, inc +// Modifications copyright 2018 Kava Labs + package lcd -/* import ( amino "github.com/tendermint/go-amino" ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -*/ var cdc = amino.NewCodec() diff --git a/internal/x/paychan/client/cmd/cmd.go b/internal/x/paychan/client/cli/cmd.go similarity index 79% rename from internal/x/paychan/client/cmd/cmd.go rename to internal/x/paychan/client/cli/cmd.go index c7b8457f..839d8fea 100644 --- a/internal/x/paychan/client/cmd/cmd.go +++ b/internal/x/paychan/client/cli/cmd.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "io/ioutil" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -10,9 +11,11 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/kava-labs/kava/internal/x/paychan" ) @@ -31,13 +34,15 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - // Create a "client context" stuct populated with info from common flags - ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc)) - // TODO is this needed for channelID - // ctx.PrintResponse = true + // Create a tx and cli "contexts": structs populated with info from common flags. + txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithLogger(os.Stdout). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) - // Get sender adress - sender, err := ctx.GetFromAddress() + // Get sender address + sender, err := cliCtx.GetFromAddress() if err != nil { return err } @@ -67,11 +72,7 @@ func CreateChannelCmd(cdc *wire.Codec) *cobra.Command { } // Build and sign the transaction, then broadcast to the blockchain - err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) - if err != nil { - return err - } - return nil + return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg}) }, } cmd.Flags().String(flagTo, "", "Recipient address of the payment channel.") @@ -92,8 +93,11 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - // Create a "client context" stuct populated with info from common flags - ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc)) + // Create a cli "context": struct populated with info from common flags. + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithLogger(os.Stdout). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) // Get the paychan id id := paychan.ChannelID(viper.GetInt64(flagId)) // TODO make this default to pulling id from chain @@ -121,8 +125,8 @@ func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command { if err != nil { return err } - name := ctx.FromAddressName - passphrase, err := ctx.GetPassphraseFromStdin(name) + name := cliCtx.FromAddressName + passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName) if err != nil { return err } @@ -169,8 +173,11 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - // Create a "client context" stuct populated with info from common flags - ctx := context.NewCoreContextFromViper() + // Create a cli "context": struct populated with info from common flags. + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithLogger(os.Stdout). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) // read in update bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile)) @@ -183,7 +190,7 @@ func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command { cdc.UnmarshalJSON(bz, &update) // get the channel from the node - res, err := ctx.QueryStore(paychan.GetChannelKey(update.ChannelID), paychanStoreName) + res, err := cliCtx.QueryStore(paychan.GetChannelKey(update.ChannelID), paychanStoreName) if len(res) == 0 || err != nil { return errors.Errorf("channel with ID '%d' does not exist", update.ChannelID) } @@ -218,12 +225,15 @@ func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - // Create a "client context" stuct populated with info from common flags - ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc)) - // ctx.PrintResponse = true TODO is this needed for channelID + // Create a tx and cli "contexts": structs populated with info from common flags. + txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithLogger(os.Stdout). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) - // Get sender adress - submitter, err := ctx.GetFromAddress() + // Get sender address + submitter, err := cliCtx.GetFromAddress() if err != nil { return err } @@ -248,11 +258,7 @@ func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command { } // Build and sign the transaction, then broadcast to the blockchain - err = ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, []sdk.Msg{msg}, cdc) - if err != nil { - return err - } - return nil + return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg}) }, } cmd.Flags().String(flagPaymentFile, "payment.json", "File to read the payment from.") @@ -268,14 +274,17 @@ func GetChannelCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - // Create a "client context" stuct populated with info from common flags - ctx := context.NewCoreContextFromViper() + // Create a cli "context": struct populated with info from common flags. + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithLogger(os.Stdout). + WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) // Get channel ID id := paychan.ChannelID(viper.GetInt64(flagId)) // Get the channel from the node - res, err := ctx.QueryStore(paychan.GetChannelKey(id), paychanStoreName) + res, err := cliCtx.QueryStore(paychan.GetChannelKey(id), paychanStoreName) if len(res) == 0 || err != nil { return errors.Errorf("channel with ID '%d' does not exist", id) } @@ -291,11 +300,11 @@ func GetChannelCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command { fmt.Println(string(jsonChannel)) // Get any submitted updates from the node - res, err = ctx.QueryStore(paychan.GetSubmittedUpdateKey(id), paychanStoreName) + res, err = cliCtx.QueryStore(paychan.GetSubmittedUpdateKey(id), paychanStoreName) if err != nil { return err } - // Print out the submited update if it exsits + // Print out the submitted update if it exists if len(res) != 0 { var submittedUpdate paychan.SubmittedUpdate cdc.MustUnmarshalBinary(res, &submittedUpdate) diff --git a/internal/x/paychan/endblocker_test.go b/internal/x/paychan/endblocker_test.go index 62fd276e..ad363d05 100644 --- a/internal/x/paychan/endblocker_test.go +++ b/internal/x/paychan/endblocker_test.go @@ -1,9 +1,10 @@ package paychan import ( + "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" - "testing" ) func TestEndBlocker(t *testing.T) { @@ -14,7 +15,7 @@ func TestEndBlocker(t *testing.T) { ctx, _, channelKeeper, addrs, _, _, _ := createMockApp(accountSeeds) sender := addrs[0] receiver := addrs[1] - coins := sdk.Coins{sdk.NewCoin("KVA", 10)} + coins := sdk.Coins{sdk.NewInt64Coin("KVA", 10)} // create new channel channelID := ChannelID(0) // should be 0 as first channel @@ -26,11 +27,10 @@ func TestEndBlocker(t *testing.T) { channelKeeper.setChannel(ctx, channel) // create closing update and submittedUpdate - payout := Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}} + payout := Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}} update := Update{ ChannelID: channelID, Payout: payout, - //Sigs: [1]crypto.Signature{}, } sUpdate := SubmittedUpdate{ Update: update, diff --git a/internal/x/paychan/keeper_test.go b/internal/x/paychan/keeper_test.go index 145a4f28..48c2c4f3 100644 --- a/internal/x/paychan/keeper_test.go +++ b/internal/x/paychan/keeper_test.go @@ -30,7 +30,7 @@ func TestKeeper(t *testing.T) { "HappyPath", addrs[senderAccountIndex], addrs[receiverAccountIndex], - sdk.Coins{sdk.NewCoin("KVA", 10)}, + sdk.Coins{sdk.NewInt64Coin("KVA", 10)}, true, false, }, @@ -38,7 +38,7 @@ func TestKeeper(t *testing.T) { "NilAddress", sdk.AccAddress{}, sdk.AccAddress{}, - sdk.Coins{sdk.NewCoin("KVA", 10)}, + sdk.Coins{sdk.NewInt64Coin("KVA", 10)}, false, true, }, @@ -54,7 +54,7 @@ func TestKeeper(t *testing.T) { "NegativeCoins", addrs[senderAccountIndex], addrs[receiverAccountIndex], - sdk.Coins{sdk.NewCoin("KVA", -57)}, + sdk.Coins{sdk.NewInt64Coin("KVA", -57)}, false, true, }, @@ -126,7 +126,7 @@ func TestKeeper(t *testing.T) { ) ctx, coinKeeper, channelKeeper, addrs, pubKeys, privKeys, genAccFunding := createMockApp(accountSeeds) - coins := sdk.Coins{sdk.NewCoin("KVA", 10)} + coins := sdk.Coins{sdk.NewInt64Coin("KVA", 10)} // create new channel channelID := ChannelID(0) // should be 0 as first channel @@ -138,7 +138,7 @@ func TestKeeper(t *testing.T) { channelKeeper.setChannel(ctx, channel) // create closing update - payout := Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}} + payout := Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}} update := Update{ ChannelID: channelID, Payout: payout, @@ -200,14 +200,14 @@ func TestKeeper(t *testing.T) { { "HappyPath", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, "sameAsSubmited", false, }, { "NoChannel", false, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, "empty", true, }, @@ -221,35 +221,35 @@ func TestKeeper(t *testing.T) { { "NegativeCoins", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", -5)}, sdk.Coins{sdk.NewCoin("KVA", 15)}}, senderAccountIndex, senderAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", -5)}, sdk.Coins{sdk.NewInt64Coin("KVA", 15)}}, senderAccountIndex, senderAccountIndex}, "empty", true, }, { "TooManyCoins", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 100)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 100)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex}, "empty", true, }, { "WrongSignature", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, senderAccountIndex, otherAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, otherAccountIndex}, "empty", true, }, { "WrongPubKey", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, otherAccountIndex, senderAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, otherAccountIndex, senderAccountIndex}, "empty", true, }, { "ReceiverSigned", true, - testUpdate{chanID, Payout{sdk.Coins{sdk.NewCoin("KVA", 3)}, sdk.Coins{sdk.NewCoin("KVA", 7)}}, receiverAccountIndex, receiverAccountIndex}, + testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, receiverAccountIndex, receiverAccountIndex}, "empty", true, }, @@ -266,7 +266,7 @@ func TestKeeper(t *testing.T) { channel := Channel{ ID: chanID, // should be 0 as first channel Participants: [2]sdk.AccAddress{addrs[senderAccountIndex], addrs[receiverAccountIndex]}, - Coins: sdk.Coins{sdk.NewCoin("KVA", 10)}, + Coins: sdk.Coins{sdk.NewInt64Coin("KVA", 10)}, } channelKeeper.setChannel(ctx, channel) } diff --git a/internal/x/paychan/test_common.go b/internal/x/paychan/test_common.go index d102b27f..98e6a0df 100644 --- a/internal/x/paychan/test_common.go +++ b/internal/x/paychan/test_common.go @@ -27,7 +27,7 @@ func createMockApp(accountSeeds []string) (sdk.Context, bank.Keeper, Keeper, []s mApp.CompleteSetup([]*sdk.KVStoreKey{keyChannel}) // create some accounts - genAccFunding := sdk.Coins{sdk.NewCoin("KVA", 1000)} + genAccFunding := sdk.Coins{sdk.NewInt64Coin("KVA", 1000)} genAccs, addrs, pubKeys, privKeys := createTestGenAccounts(accountSeeds, genAccFunding) // initialize the app with these accounts diff --git a/internal/x/paychan/types.go b/internal/x/paychan/types.go index 57d44b52..3d6a8e69 100644 --- a/internal/x/paychan/types.go +++ b/internal/x/paychan/types.go @@ -61,7 +61,7 @@ func (p Payout) Sum() sdk.Coins { type UpdateSignature struct { PubKey crypto.PubKey - CryptoSignature crypto.Signature + CryptoSignature []byte } // An update that has been submitted to the blockchain, but not yet acted on. diff --git a/internal/x/paychan/types_test.go b/internal/x/paychan/types_test.go index e4004317..83c97ff6 100644 --- a/internal/x/paychan/types_test.go +++ b/internal/x/paychan/types_test.go @@ -28,18 +28,18 @@ func TestSubmittedUpdatesQueue(t *testing.T) { func TestPayout(t *testing.T) { t.Run("IsNotNegative", func(t *testing.T) { - p := Payout{sdk.Coins{sdk.NewCoin("USD", 4), sdk.NewCoin("GBP", 0)}, sdk.Coins{sdk.NewCoin("USD", 129879234), sdk.NewCoin("GBP", 1)}} + p := Payout{sdk.Coins{sdk.NewInt64Coin("USD", 4), sdk.NewInt64Coin("GBP", 0)}, sdk.Coins{sdk.NewInt64Coin("USD", 129879234), sdk.NewInt64Coin("GBP", 1)}} assert.True(t, p.IsNotNegative()) - p = Payout{sdk.Coins{sdk.NewCoin("USD", -4), sdk.NewCoin("GBP", 0)}, sdk.Coins{sdk.NewCoin("USD", 129879234), sdk.NewCoin("GBP", 1)}} + p = Payout{sdk.Coins{sdk.NewInt64Coin("USD", -4), sdk.NewInt64Coin("GBP", 0)}, sdk.Coins{sdk.NewInt64Coin("USD", 129879234), sdk.NewInt64Coin("GBP", 1)}} assert.False(t, p.IsNotNegative()) }) t.Run("Sum", func(t *testing.T) { p := Payout{ - sdk.Coins{sdk.NewCoin("EUR", 1), sdk.NewCoin("USD", -5)}, - sdk.Coins{sdk.NewCoin("EUR", 1), sdk.NewCoin("USD", 100), sdk.NewCoin("GBP", 1)}, + sdk.Coins{sdk.NewInt64Coin("EUR", 1), sdk.NewInt64Coin("USD", -5)}, + sdk.Coins{sdk.NewInt64Coin("EUR", 1), sdk.NewInt64Coin("USD", 100), sdk.NewInt64Coin("GBP", 1)}, } - expected := sdk.Coins{sdk.NewCoin("EUR", 2), sdk.NewCoin("GBP", 1), sdk.NewCoin("USD", 95)} + expected := sdk.Coins{sdk.NewInt64Coin("EUR", 2), sdk.NewInt64Coin("GBP", 1), sdk.NewInt64Coin("USD", 95)} assert.Equal(t, expected, p.Sum()) }) } diff --git a/testnets/kava-test-3/config.toml b/testnets/kava-test-3/config.toml new file mode 100644 index 00000000..1437f4de --- /dev/null +++ b/testnets/kava-test-3/config.toml @@ -0,0 +1,207 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:17128" + +# A custom human readable name for this node +moniker = "" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb +db_backend = "leveldb" + +# Database directory +db_path = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_file = "config/priv_validator.json" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "localhost:6060" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:17127" + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:17126" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "6b1f509f5152ca08606c299c01ae284389671d8d@kava-test-3.node.connector.kava.io:17126" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +addr_book_strict = true + +# Time to wait before flushing messages out on the connection, in ms +flush_throttle_timeout = 100 + +# Maximum number of peers to connect to +max_num_peers = 50 + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +##### mempool configuration options ##### +[mempool] + +recheck = true +recheck_empty = true +broadcast = true +wal_dir = "data/mempool.wal" + +# size of the mempool +size = 100000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 100000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +# All timeouts are in milliseconds +timeout_propose = 3000 +timeout_propose_delta = 500 +timeout_prevote = 1000 +timeout_prevote_delta = 500 +timeout_precommit = 1000 +timeout_precommit_delta = 500 +timeout_commit = 5000 + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks in seconds +create_empty_blocks = true +create_empty_blocks_interval = 0 + +# Reactor sleep duration parameters are in milliseconds +peer_gossip_sleep_duration = 100 +peer_query_maj23_sleep_duration = 2000 + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" (default) +# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is tx hash) +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags. Note this may be not +# desirable (see the comment above). IndexTags has a precedence over +# IndexAllTags (i.e. when given both, IndexTags will be indexed). +index_all_tags = true + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 diff --git a/testnets/kava-test-3/genesis.json b/testnets/kava-test-3/genesis.json new file mode 100644 index 00000000..dfd371b4 --- /dev/null +++ b/testnets/kava-test-3/genesis.json @@ -0,0 +1,99 @@ +{ + "genesis_time": "2018-10-03T18:25:12.233785395Z", + "chain_id": "kava-test-3", + "consensus_params": { + "block_size_params": { + "max_bytes": "22020096", + "max_txs": "10000", + "max_gas": "-1" + }, + "tx_size_params": { + "max_bytes": "10240", + "max_gas": "-1" + }, + "block_gossip_params": { + "block_part_size_bytes": "65536" + }, + "evidence_params": { + "max_age": "100000" + } + }, + "validators": [ + { + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "n+XBgHtHEJOQa9ufM1sj7Sacxo/W8lB3dJFmzlmmSrU=" + }, + "power": "1000", + "name": "" + } + ], + "app_hash": "", + "app_state": { + "accounts": [ + { + "address": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j", + "coins": [ + { + "denom": "KVA", + "amount": "99000" + } + ] + } + ], + "stake": { + "pool": { + "loose_tokens": "100000", + "bonded_tokens": "0", + "inflation_last_time": "1970-01-01T00:00:00Z", + "inflation": "7/100", + "date_last_commission_reset": "0", + "prev_bonded_shares": "0" + }, + "params": { + "inflation_rate_change": "13/100", + "inflation_max": "1/5", + "inflation_min": "7/100", + "goal_bonded": "67/100", + "unbonding_time": "300000000000", + "max_validators": 100, + "bond_denom": "KVA" + }, + "validators": [ + { + "owner": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "n+XBgHtHEJOQa9ufM1sj7Sacxo/W8lB3dJFmzlmmSrU=" + }, + "revoked": false, + "status": 0, + "tokens": "1000", + "delegator_shares": "1000", + "description": { + "moniker": "kava-validator", + "identity": "", + "website": "", + "details": "" + }, + "bond_height": "0", + "bond_intra_tx_counter": 0, + "proposer_reward_pool": [], + "commission": "0", + "commission_max": "0", + "commission_change_rate": "0", + "commission_change_today": "0", + "prev_bonded_tokens": "0" + } + ], + "bonds": [ + { + "delegator_addr": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j", + "validator_addr": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j", + "shares": "1000", + "height": "0" + } + ] + } + } +} \ No newline at end of file