Merge branch 'develop' into release/v0.19.0
This commit is contained in:
commit
5a273a1cdd
|
@ -1,8 +1,12 @@
|
|||
<!-- Thanks for filing a PR! Before hitting the button, please check the following items.-->
|
||||
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
|
||||
v ✰ Thanks for creating a PR! ✰
|
||||
v Before smashing the submit button please review the checkboxes.
|
||||
v If a checkbox is n/a - please still include it but + a little note why
|
||||
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
|
||||
|
||||
* [ ] Updated all relevant documentation in docs
|
||||
* [ ] Updated all code comments where relevant
|
||||
* [ ] Wrote tests
|
||||
* [ ] Updated CHANGELOG.md
|
||||
* [ ] Updated Basecoin / other examples
|
||||
* [ ] Squashed related commits and prefixed with PR number per [coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)
|
||||
* [ ] Updated Gaia/Examples
|
||||
* [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr))
|
||||
|
|
|
@ -14,6 +14,7 @@ docs/_build
|
|||
# Data - ideally these don't exist
|
||||
examples/basecoin/app/data
|
||||
baseapp/data/*
|
||||
client/lcd/keys/*
|
||||
|
||||
# Testing
|
||||
coverage.txt
|
||||
|
|
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -2,16 +2,25 @@
|
|||
|
||||
BREAKING CHANGES
|
||||
* msg.GetSignBytes() now returns bech32-encoded addresses in all cases
|
||||
* [lcd] REST end-points now include gas
|
||||
|
||||
FEATURES
|
||||
* [x/auth] Added AccountNumbers to BaseAccount and StdTxs to allow for replay protection with account pruning
|
||||
|
||||
IMPROVEMENTS
|
||||
* export command now writes current validator set for Tendermint
|
||||
* [tests] Application module tests now use a mock application
|
||||
* [gaiacli] Fix error message when account isn't found when running gaiacli account
|
||||
* [lcd] refactored to eliminate use of global variables, and interdependent tests
|
||||
* [x/stake] More stake tests added to test ByPower index
|
||||
|
||||
FIXES
|
||||
* [x/stake] bonded inflation removed, non-bonded inflation partially implemented
|
||||
* [lcd] Switch to bech32 for addresses on all human readable inputs and outputs
|
||||
* [lcd] fixed tx indexing/querying
|
||||
* [cli] Added `--gas` flag to specify transaction gas limit
|
||||
* [gaia] Registered slashing message handler
|
||||
* [x/slashing] Set signInfo.StartHeight correctly for newly bonded validators
|
||||
|
||||
## 0.18.0
|
||||
|
||||
|
@ -81,6 +90,25 @@ BUG FIXES
|
|||
* [gaiacli] Fix error message when account isn't found when running gaiacli account
|
||||
|
||||
|
||||
## 0.17.5
|
||||
|
||||
*June 5, 2018*
|
||||
|
||||
Update to Tendermint v0.19.9 (Fix evidence reactor, mempool deadlock, WAL panic,
|
||||
memory leak)
|
||||
|
||||
## 0.17.4
|
||||
|
||||
*May 31, 2018*
|
||||
|
||||
Update to Tendermint v0.19.7 (WAL fixes and more)
|
||||
|
||||
## 0.17.3
|
||||
|
||||
*May 29, 2018*
|
||||
|
||||
Update to Tendermint v0.19.6 (fix fast-sync halt)
|
||||
|
||||
## 0.17.5
|
||||
|
||||
*June 5, 2018*
|
||||
|
|
|
@ -197,8 +197,8 @@
|
|||
".",
|
||||
"mem"
|
||||
]
|
||||
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||
version = "v1.1.0"
|
||||
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cast"
|
||||
|
@ -236,8 +236,8 @@
|
|||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
version = "v1.2.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -366,7 +366,7 @@
|
|||
"merkle",
|
||||
"merkle/tmhash"
|
||||
]
|
||||
revision = "640af0205d98d1f45fb2f912f9c35c8bf816adc9"
|
||||
revision = "0c98d10b4ffbd87978d79c160e835b3d3df241ec"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -396,13 +396,13 @@
|
|||
"internal/timeseries",
|
||||
"trace"
|
||||
]
|
||||
revision = "1e491301e022f8f977054da4c2d852decd59571f"
|
||||
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
|
||||
revision = "bff228c7b664c5fce602223a05fb708fd8654986"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
|
8
Makefile
8
Makefile
|
@ -46,6 +46,9 @@ install_examples:
|
|||
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
|
||||
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democli
|
||||
|
||||
install_debug:
|
||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug
|
||||
|
||||
dist:
|
||||
@bash publish/dist.sh
|
||||
@bash publish/publish.sh
|
||||
|
@ -92,6 +95,9 @@ test_cli:
|
|||
test_unit:
|
||||
@go test $(PACKAGES_NOCLITEST)
|
||||
|
||||
test_race:
|
||||
@go test -race $(PACKAGES_NOCLITEST)
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
|
@ -154,4 +160,4 @@ remotenet-status:
|
|||
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||
# unless there is a reason not to.
|
||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update remotenet-start remotenet-stop remotenet-status
|
||||
.PHONY: build build_examples install install_examples install_debug dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update remotenet-start remotenet-stop remotenet-status
|
||||
|
|
|
@ -109,9 +109,12 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
if chainID == "" {
|
||||
return nil, errors.Errorf("Chain ID required but not specified")
|
||||
}
|
||||
accnum := ctx.AccountNumber
|
||||
sequence := ctx.Sequence
|
||||
|
||||
signMsg := auth.StdSignMsg{
|
||||
ChainID: chainID,
|
||||
AccountNumbers: []int64{accnum},
|
||||
Sequences: []int64{sequence},
|
||||
Msg: msg,
|
||||
Fee: auth.NewStdFee(ctx.Gas, sdk.Coin{}), // TODO run simulate to estimate gas?
|
||||
|
@ -132,6 +135,7 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
sigs := []auth.StdSignature{{
|
||||
PubKey: pubkey,
|
||||
Signature: sig,
|
||||
AccountNumber: accnum,
|
||||
Sequence: sequence,
|
||||
}}
|
||||
|
||||
|
@ -144,6 +148,10 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
// sign and build the transaction from the msg
|
||||
func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (res *ctypes.ResultBroadcastTxCommit, err error) {
|
||||
|
||||
ctx, err = EnsureAccountNumber(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
|
@ -163,13 +171,37 @@ func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msg sdk.Msg, cdc *w
|
|||
return ctx.BroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
// get the next sequence for the account address
|
||||
func (ctx CoreContext) GetAccountNumber(address []byte) (int64, error) {
|
||||
if ctx.Decoder == nil {
|
||||
return 0, errors.New("AccountDecoder required but not provided")
|
||||
}
|
||||
|
||||
res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
fmt.Printf("No account found. Returning 0.\n")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
account, err := ctx.Decoder(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return account.GetAccountNumber(), nil
|
||||
}
|
||||
|
||||
// get the next sequence for the account address
|
||||
func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
|
||||
if ctx.Decoder == nil {
|
||||
return 0, errors.New("AccountDecoder required but not provided")
|
||||
}
|
||||
|
||||
res, err := ctx.Query(address, ctx.AccountStore)
|
||||
res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ type CoreContext struct {
|
|||
TrustNode bool
|
||||
NodeURI string
|
||||
FromAddressName string
|
||||
AccountNumber int64
|
||||
Sequence int64
|
||||
Client rpcclient.Client
|
||||
Decoder auth.AccountDecoder
|
||||
|
@ -57,6 +58,12 @@ func (c CoreContext) WithFromAddressName(fromAddressName string) CoreContext {
|
|||
return c
|
||||
}
|
||||
|
||||
// WithSequence - return a copy of the context with an account number
|
||||
func (c CoreContext) WithAccountNumber(accnum int64) CoreContext {
|
||||
c.AccountNumber = accnum
|
||||
return c
|
||||
}
|
||||
|
||||
// WithSequence - return a copy of the context with an updated sequence number
|
||||
func (c CoreContext) WithSequence(sequence int64) CoreContext {
|
||||
c.Sequence = sequence
|
||||
|
|
|
@ -34,6 +34,7 @@ func NewCoreContextFromViper() CoreContext {
|
|||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
FromAddressName: viper.GetString(client.FlagName),
|
||||
NodeURI: nodeURI,
|
||||
AccountNumber: viper.GetInt64(client.FlagAccountNumber),
|
||||
Sequence: viper.GetInt64(client.FlagSequence),
|
||||
Client: rpc,
|
||||
Decoder: nil,
|
||||
|
@ -54,6 +55,25 @@ func defaultChainID() (string, error) {
|
|||
return doc.ChainID, nil
|
||||
}
|
||||
|
||||
// EnsureSequence - automatically set sequence number if none provided
|
||||
func EnsureAccountNumber(ctx CoreContext) (CoreContext, error) {
|
||||
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
|
||||
if viper.GetInt64(client.FlagAccountNumber) != 0 {
|
||||
return ctx, nil
|
||||
}
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
accnum, err := ctx.GetAccountNumber(from)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
fmt.Printf("Defaulting to account number: %d\n", accnum)
|
||||
ctx = ctx.WithAccountNumber(accnum)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// EnsureSequence - automatically set sequence number if none provided
|
||||
func EnsureSequence(ctx CoreContext) (CoreContext, error) {
|
||||
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
|
||||
|
|
|
@ -10,6 +10,7 @@ const (
|
|||
FlagGas = "gas"
|
||||
FlagTrustNode = "trust-node"
|
||||
FlagName = "name"
|
||||
FlagAccountNumber = "account-number"
|
||||
FlagSequence = "sequence"
|
||||
FlagFee = "fee"
|
||||
)
|
||||
|
@ -26,7 +27,6 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
|||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block")
|
||||
c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction")
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
|||
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||
for _, c := range cmds {
|
||||
c.Flags().String(FlagName, "", "Name of private key with which to sign")
|
||||
c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx")
|
||||
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
|
||||
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
|
||||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||
|
|
|
@ -102,12 +102,6 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// addOutput lets us json format the data
|
||||
type addOutput struct {
|
||||
Key keys.Info `json:"key"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
func printCreate(info keys.Info, seed string) {
|
||||
output := viper.Get(cli.OutputFlag)
|
||||
switch output {
|
||||
|
@ -121,7 +115,10 @@ func printCreate(info keys.Info, seed string) {
|
|||
fmt.Println(seed)
|
||||
}
|
||||
case "json":
|
||||
out := addOutput{Key: info}
|
||||
out, err := Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !viper.GetBool(flagNoBackup) {
|
||||
out.Seed = seed
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -54,9 +53,11 @@ func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write([]byte("[]"))
|
||||
return
|
||||
}
|
||||
keysOutput := make([]KeyOutput, len(infos))
|
||||
for i, info := range infos {
|
||||
keysOutput[i] = KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address().Bytes())}
|
||||
keysOutput, err := Bech32KeysOutput(infos)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
output, err := json.MarshalIndent(keysOutput, "", " ")
|
||||
if err != nil {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/gorilla/mux"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
|
@ -51,7 +50,12 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
keyOutput := KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address())}
|
||||
keyOutput, err := Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
output, err := json.MarshalIndent(keyOutput, "", " ")
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
@ -22,6 +21,8 @@ const KeyDBName = "keys"
|
|||
// keybase is used to make GetKeyBase a singleton
|
||||
var keybase keys.Keybase
|
||||
|
||||
// TODO make keybase take a database not load from the directory
|
||||
|
||||
// initialize a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
@ -48,28 +49,46 @@ func SetKeyBase(kb keys.Keybase) {
|
|||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Address sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pub_key"`
|
||||
Seed string `json:"seed,omitempty"`
|
||||
}
|
||||
|
||||
func NewKeyOutput(info keys.Info) KeyOutput {
|
||||
return KeyOutput{
|
||||
Name: info.Name,
|
||||
Address: sdk.Address(info.PubKey.Address().Bytes()),
|
||||
PubKey: info.PubKey,
|
||||
}
|
||||
}
|
||||
|
||||
func NewKeyOutputs(infos []keys.Info) []KeyOutput {
|
||||
// create a list of KeyOutput in bech32 format
|
||||
func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
|
||||
kos := make([]KeyOutput, len(infos))
|
||||
for i, info := range infos {
|
||||
kos[i] = NewKeyOutput(info)
|
||||
ko, err := Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kos
|
||||
kos[i] = ko
|
||||
}
|
||||
return kos, nil
|
||||
}
|
||||
|
||||
// create a KeyOutput in bech32 format
|
||||
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
|
||||
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes()))
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey)
|
||||
if err != nil {
|
||||
return KeyOutput{}, err
|
||||
}
|
||||
return KeyOutput{
|
||||
Name: info.Name,
|
||||
Address: bechAccount,
|
||||
PubKey: bechPubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func printInfo(info keys.Info) {
|
||||
ko := NewKeyOutput(info)
|
||||
ko, err := Bech32KeyOutput(info)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
|
@ -84,7 +103,10 @@ func printInfo(info keys.Info) {
|
|||
}
|
||||
|
||||
func printInfos(infos []keys.Info) {
|
||||
kos := NewKeyOutputs(infos)
|
||||
kos, err := Bech32KeysOutput(infos)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
|
||||
|
@ -101,13 +123,5 @@ func printInfos(infos []keys.Info) {
|
|||
}
|
||||
|
||||
func printKeyOutput(ko KeyOutput) {
|
||||
bechAccount, err := sdk.Bech32ifyAcc(ko.Address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bechPubKey, err := sdk.Bech32ifyAccPub(ko.PubKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, bechAccount, bechPubKey)
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package lcd
|
||||
|
||||
// NOTE: COPIED VERBATIM FROM tendermint/tendermint/rpc/test/helpers.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
)
|
||||
|
||||
var globalConfig *cfg.Config
|
||||
|
||||
// f**ing long, but unique for each test
|
||||
func makePathname() string {
|
||||
// get path
|
||||
p, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// fmt.Println(p)
|
||||
sep := string(filepath.Separator)
|
||||
return strings.Replace(p, sep, "_", -1)
|
||||
}
|
||||
|
||||
func randPort() int {
|
||||
return int(cmn.RandUint16()/2 + 10000)
|
||||
}
|
||||
|
||||
func makeAddrs() (string, string, string) {
|
||||
start := randPort()
|
||||
return fmt.Sprintf("tcp://0.0.0.0:%d", start),
|
||||
fmt.Sprintf("tcp://0.0.0.0:%d", start+1),
|
||||
fmt.Sprintf("tcp://0.0.0.0:%d", start+2)
|
||||
}
|
||||
|
||||
// GetConfig returns a config for the test cases as a singleton
|
||||
func GetConfig() *cfg.Config {
|
||||
if globalConfig == nil {
|
||||
pathname := makePathname()
|
||||
globalConfig = cfg.ResetTestRoot(pathname)
|
||||
|
||||
// and we use random ports to run in parallel
|
||||
tm, rpc, _ := makeAddrs()
|
||||
globalConfig.P2P.ListenAddress = tm
|
||||
globalConfig.RPC.ListenAddress = rpc
|
||||
globalConfig.TxIndex.IndexTags = "app.creator" // see kvstore application
|
||||
}
|
||||
return globalConfig
|
||||
}
|
|
@ -1,142 +1,115 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
cryptoKeys "github.com/tendermint/go-crypto/keys"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
p2p "github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
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/stake"
|
||||
)
|
||||
|
||||
var (
|
||||
coinDenom = "steak"
|
||||
coinAmount = int64(10000000)
|
||||
|
||||
validatorAddr1 = ""
|
||||
validatorAddr2 = ""
|
||||
|
||||
// XXX bad globals
|
||||
name = "test"
|
||||
password = "0123456789"
|
||||
port string
|
||||
seed string
|
||||
sendAddr string
|
||||
stakerest "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
)
|
||||
|
||||
func TestKeys(t *testing.T) {
|
||||
|
||||
// empty keys
|
||||
// XXX: the test comes with a key setup
|
||||
/*
|
||||
res, body := request(t, port, "GET", "/keys", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
assert.Equal(t, "[]", body, "Expected an empty array")
|
||||
*/
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// get seed
|
||||
res, body := request(t, port, "GET", "/keys/seed", nil)
|
||||
res, body := Request(t, port, "GET", "/keys/seed", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
newSeed := body
|
||||
reg, err := regexp.Compile(`([a-z]+ ){12}`)
|
||||
require.Nil(t, err)
|
||||
match := reg.MatchString(seed)
|
||||
assert.True(t, match, "Returned seed has wrong foramt", seed)
|
||||
assert.True(t, match, "Returned seed has wrong format", seed)
|
||||
|
||||
newName := "test_newname"
|
||||
newPassword := "0987654321"
|
||||
|
||||
// add key
|
||||
var jsonStr = []byte(fmt.Sprintf(`{"name":"test_fail", "password":"%s"}`, password))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode, "Account creation should require a seed")
|
||||
|
||||
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
addr := body
|
||||
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
|
||||
addr2 := body
|
||||
assert.Len(t, addr2, 40, "Returned address has wrong format", addr2)
|
||||
|
||||
// existing keys
|
||||
res, body = request(t, port, "GET", "/keys", nil)
|
||||
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)
|
||||
|
||||
sendAddrAcc, _ := sdk.GetAccAddressHex(sendAddr)
|
||||
addrAcc, _ := sdk.GetAccAddressHex(addr)
|
||||
addr2Acc, err := sdk.GetAccAddressHex(addr2)
|
||||
require.Nil(t, err)
|
||||
addr2Bech32 := sdk.MustBech32ifyAcc(addr2Acc)
|
||||
addrBech32 := sdk.MustBech32ifyAcc(addr)
|
||||
|
||||
assert.Equal(t, m[0].Name, name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, m[0].Address, sendAddrAcc, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, m[1].Name, newName, "Did not serve keys name correctly")
|
||||
assert.Equal(t, m[1].Address, addrAcc, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, name, m[0].Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, addrBech32, m[0].Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, newName, m[1].Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, addr2Bech32, m[1].Address, "Did not serve keys Address correctly")
|
||||
|
||||
// select key
|
||||
keyEndpoint := fmt.Sprintf("/keys/%s", newName)
|
||||
res, body = request(t, port, "GET", keyEndpoint, nil)
|
||||
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)
|
||||
|
||||
assert.Equal(t, newName, m2.Name, "Did not serve keys name correctly")
|
||||
assert.Equal(t, addrAcc, m2.Address, "Did not serve keys Address correctly")
|
||||
assert.Equal(t, addr2Bech32, m2.Address, "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)
|
||||
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)
|
||||
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)
|
||||
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.Address{})
|
||||
defer cleanup()
|
||||
|
||||
// node info
|
||||
res, body := request(t, port, "GET", "/version", nil)
|
||||
res, body := Request(t, port, "GET", "/version", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
|
||||
|
@ -146,9 +119,11 @@ func TestVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeStatus(t *testing.T) {
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
// node info
|
||||
res, body := request(t, port, "GET", "/node_info", nil)
|
||||
res, body := Request(t, port, "GET", "/node_info", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var nodeInfo p2p.NodeInfo
|
||||
|
@ -158,21 +133,20 @@ func TestNodeStatus(t *testing.T) {
|
|||
assert.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res)
|
||||
|
||||
// syncing
|
||||
res, body = request(t, port, "GET", "/syncing", nil)
|
||||
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"
|
||||
// we c
|
||||
assert.Equal(t, "false", body)
|
||||
}
|
||||
|
||||
func TestBlock(t *testing.T) {
|
||||
|
||||
tests.WaitForHeight(2, port)
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.Address{})
|
||||
defer cleanup()
|
||||
|
||||
var resultBlock ctypes.ResultBlock
|
||||
|
||||
res, body := request(t, port, "GET", "/blocks/latest", nil)
|
||||
res, body := Request(t, port, "GET", "/blocks/latest", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
|
@ -182,7 +156,7 @@ func TestBlock(t *testing.T) {
|
|||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/blocks/1", nil)
|
||||
res, body = Request(t, port, "GET", "/blocks/1", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = json.Unmarshal([]byte(body), &resultBlock)
|
||||
|
@ -192,50 +166,62 @@ func TestBlock(t *testing.T) {
|
|||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/blocks/1000000000", nil)
|
||||
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.Address{})
|
||||
defer cleanup()
|
||||
|
||||
var resultVals ctypes.ResultValidators
|
||||
var resultVals rpc.ResultValidatorsOutput
|
||||
|
||||
res, body := request(t, port, "GET", "/validatorsets/latest", nil)
|
||||
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")
|
||||
|
||||
assert.NotEqual(t, ctypes.ResultValidators{}, resultVals)
|
||||
assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
|
||||
|
||||
assert.Contains(t, resultVals.Validators[0].Address, "cosmosvaladdr")
|
||||
assert.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalpub")
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/validatorsets/1", nil)
|
||||
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")
|
||||
|
||||
assert.NotEqual(t, ctypes.ResultValidators{}, resultVals)
|
||||
assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
|
||||
|
||||
// --
|
||||
|
||||
res, body = request(t, port, "GET", "/validatorsets/1000000000", nil)
|
||||
res, body = Request(t, port, "GET", "/validatorsets/1000000000", nil)
|
||||
require.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
}
|
||||
|
||||
func TestCoinSend(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
|
||||
require.NoError(t, err)
|
||||
someFakeAddr := sdk.MustBech32ifyAcc(bz)
|
||||
|
||||
// query empty
|
||||
//res, body := request(t, port, "GET", "/accounts/8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6", nil)
|
||||
res, body := request(t, port, "GET", "/accounts/8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6", nil)
|
||||
res, body := Request(t, port, "GET", "/accounts/"+someFakeAddr, nil)
|
||||
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
initialBalance := acc.GetCoins()
|
||||
|
||||
// create TX
|
||||
receiveAddr, resultTx := doSend(t, port, seed)
|
||||
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
|
@ -243,27 +229,31 @@ func TestCoinSend(t *testing.T) {
|
|||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc = getAccount(t, sendAddr)
|
||||
acc = getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
mycoins := coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, initialBalance[0].Amount-1, mycoins.Amount)
|
||||
|
||||
// query receiver
|
||||
acc = getAccount(t, receiveAddr)
|
||||
acc = getAccount(t, port, receiveAddr)
|
||||
coins = acc.GetCoins()
|
||||
mycoins = coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, int64(1), mycoins.Amount)
|
||||
}
|
||||
|
||||
func TestIBCTransfer(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
initialBalance := acc.GetCoins()
|
||||
|
||||
// create TX
|
||||
resultTx := doIBCTransfer(t, port, seed)
|
||||
resultTx := doIBCTransfer(t, port, seed, name, password, addr)
|
||||
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
|
@ -272,72 +262,108 @@ func TestIBCTransfer(t *testing.T) {
|
|||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc = getAccount(t, sendAddr)
|
||||
acc = getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
mycoins := coins[0]
|
||||
assert.Equal(t, coinDenom, mycoins.Denom)
|
||||
assert.Equal(t, "steak", mycoins.Denom)
|
||||
assert.Equal(t, initialBalance[0].Amount-1, mycoins.Amount)
|
||||
|
||||
// TODO: query ibc egress packet state
|
||||
}
|
||||
|
||||
func TestTxs(t *testing.T) {
|
||||
|
||||
// TODO: re-enable once we can get txs by tag
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
// query wrong
|
||||
// res, body := request(t, port, "GET", "/txs", nil)
|
||||
// require.Equal(t, http.StatusBadRequest, res.StatusCode, body)
|
||||
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=coin.sender='%s'", "8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6"), nil)
|
||||
// require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
// assert.Equal(t, "[]", body)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "cosmosaccaddr1jawd35d9aq4u76sr3fjalmcqc8hqygs9gtnmv3"), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
assert.Equal(t, "[]", body)
|
||||
|
||||
// create TX
|
||||
_, resultTx := doSend(t, port, seed)
|
||||
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)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
// // query sender
|
||||
// res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=coin.sender='%s'", addr), nil)
|
||||
// require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
type txInfo struct {
|
||||
Height int64 `json:"height"`
|
||||
Tx sdk.Tx `json:"tx"`
|
||||
Result abci.ResponseDeliverTx `json:"result"`
|
||||
}
|
||||
var indexedTxs []txInfo
|
||||
|
||||
// assert.NotEqual(t, "[]", body)
|
||||
// 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)
|
||||
assert.NotEqual(t, "[]", body)
|
||||
|
||||
// // query receiver
|
||||
// res, body = request(t, port, "GET", fmt.Sprintf("/txs?tag=coin.receiver='%s'", receiveAddr), nil)
|
||||
// require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
err := cdc.UnmarshalJSON([]byte(body), &indexedTxs)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(indexedTxs))
|
||||
|
||||
// assert.NotEqual(t, "[]", body)
|
||||
// query sender
|
||||
addrBech := sdk.MustBech32ifyAcc(addr)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", addrBech), 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
|
||||
assert.Equal(t, resultTx.Height, indexedTxs[0].Height)
|
||||
|
||||
// query recipient
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddrBech), 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))
|
||||
assert.Equal(t, resultTx.Height, indexedTxs[0].Height)
|
||||
}
|
||||
|
||||
func TestValidatorsQuery(t *testing.T) {
|
||||
validators := getValidators(t)
|
||||
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{})
|
||||
require.Equal(t, 2, len(pks))
|
||||
defer cleanup()
|
||||
|
||||
validators := getValidators(t, port)
|
||||
assert.Equal(t, len(validators), 2)
|
||||
|
||||
// make sure all the validators were found (order unknown because sorted by owner addr)
|
||||
foundVal1, foundVal2 := false, false
|
||||
res1, res2 := hex.EncodeToString(validators[0].Owner), hex.EncodeToString(validators[1].Owner)
|
||||
if res1 == validatorAddr1 || res2 == validatorAddr1 {
|
||||
pk1Bech := sdk.MustBech32ifyValPub(pks[0])
|
||||
pk2Bech := sdk.MustBech32ifyValPub(pks[1])
|
||||
if validators[0].PubKey == pk1Bech || validators[1].PubKey == pk1Bech {
|
||||
foundVal1 = true
|
||||
}
|
||||
if res1 == validatorAddr2 || res2 == validatorAddr2 {
|
||||
if validators[0].PubKey == pk2Bech || validators[1].PubKey == pk2Bech {
|
||||
foundVal2 = true
|
||||
}
|
||||
assert.True(t, foundVal1, "validatorAddr1 %v, res1 %v, res2 %v", validatorAddr1, res1, res2)
|
||||
assert.True(t, foundVal2, "validatorAddr2 %v, res1 %v, res2 %v", validatorAddr2, res1, res2)
|
||||
assert.True(t, foundVal1, "pk1Bech %v, owner1 %v, owner2 %v", pk1Bech, validators[0].Owner, validators[1].Owner)
|
||||
assert.True(t, foundVal2, "pk2Bech %v, owner1 %v, owner2 %v", pk2Bech, validators[0].Owner, validators[1].Owner)
|
||||
}
|
||||
|
||||
func TestBond(t *testing.T) {
|
||||
func TestBonding(t *testing.T) {
|
||||
name, password, denom := "test", "1234567890", "steak"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKB(t))
|
||||
cleanup, pks, port := InitializeTestLCD(t, 2, []sdk.Address{addr})
|
||||
defer cleanup()
|
||||
|
||||
validator1Owner := pks[0].Address()
|
||||
|
||||
// create bond TX
|
||||
resultTx := doBond(t, port, seed)
|
||||
resultTx := doBond(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
|
@ -345,197 +371,42 @@ func TestBond(t *testing.T) {
|
|||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// query sender
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
coins := acc.GetCoins()
|
||||
assert.Equal(t, int64(87), coins.AmountOf(coinDenom))
|
||||
assert.Equal(t, int64(40), coins.AmountOf(denom))
|
||||
|
||||
// query candidate
|
||||
bond := getDelegation(t, sendAddr, validatorAddr1)
|
||||
assert.Equal(t, "10/1", bond.Shares.String())
|
||||
}
|
||||
// query validator
|
||||
bond := getDelegation(t, port, addr, validator1Owner)
|
||||
assert.Equal(t, "60/1", bond.Shares.String())
|
||||
|
||||
func TestUnbond(t *testing.T) {
|
||||
//////////////////////
|
||||
// testing unbonding
|
||||
|
||||
// create unbond TX
|
||||
resultTx := doUnbond(t, port, seed)
|
||||
resultTx = doUnbond(t, port, seed, name, password, addr, validator1Owner)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// query validator
|
||||
bond = getDelegation(t, port, addr, validator1Owner)
|
||||
assert.Equal(t, "30/1", bond.Shares.String())
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
|
||||
// TODO fix shares fn in staking
|
||||
// query sender
|
||||
acc := getAccount(t, sendAddr)
|
||||
coins := acc.GetCoins()
|
||||
assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
|
||||
//acc := getAccount(t, sendAddr)
|
||||
//coins := acc.GetCoins()
|
||||
//assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
|
||||
|
||||
// query candidate
|
||||
bond := getDelegation(t, sendAddr, validatorAddr1)
|
||||
assert.Equal(t, "9/1", bond.Shares.String())
|
||||
}
|
||||
|
||||
//__________________________________________________________
|
||||
// helpers
|
||||
|
||||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
||||
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
viper.Set(client.FlagGas, 200000)
|
||||
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
config := GetConfig()
|
||||
config.Consensus.TimeoutCommit = 1000
|
||||
config.Consensus.SkipTimeoutCommit = false
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
logger = log.NewFilter(logger, log.AllowError())
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
genDoc.Validators = append(genDoc.Validators,
|
||||
tmtypes.GenesisValidator{
|
||||
PubKey: crypto.GenPrivKeyEd25519().PubKey(),
|
||||
Power: 1,
|
||||
Name: "val",
|
||||
},
|
||||
)
|
||||
|
||||
pk1 := genDoc.Validators[0].PubKey
|
||||
pk2 := genDoc.Validators[1].PubKey
|
||||
validatorAddr1 = hex.EncodeToString(pk1.Address())
|
||||
validatorAddr2 = hex.EncodeToString(pk2.Address())
|
||||
|
||||
// NOTE it's bad practice to reuse pk address for the owner address but doing in the
|
||||
// test for simplicity
|
||||
var appGenTxs [2]json.RawMessage
|
||||
appGenTxs[0], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk1, pk1.Address(), "test_val1", true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
appGenTxs[1], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk2, pk2.Address(), "test_val2", true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// add the sendAddr to genesis
|
||||
var info cryptoKeys.Info
|
||||
info, seed, err = kb.Create(name, password, cryptoKeys.AlgoEd25519) // XXX global seed
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sendAddr = info.PubKey.Address().String() // XXX global
|
||||
accAuth := auth.NewBaseAccountWithAddress(info.PubKey.Address())
|
||||
accAuth.Coins = sdk.Coins{{"steak", 100}}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
genDoc.AppStateJSON = appState
|
||||
|
||||
// LCD listen address
|
||||
var listenAddr string
|
||||
listenAddr, port, err = server.FreeTCPAddr()
|
||||
if err != nil {
|
||||
return nil, nil, 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)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
lcd, err := startLCD(logger, listenAddr, cdc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tests.WaitForStart(port)
|
||||
|
||||
return node, lcd, nil
|
||||
}
|
||||
|
||||
// Create & start in-process tendermint node with memdb
|
||||
// and in-process abci application.
|
||||
// TODO: need to clean up the WAL dir or enable it to be not persistent
|
||||
func startTM(cfg *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 }
|
||||
n, err := nm.NewNode(cfg,
|
||||
privVal,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
dbProvider,
|
||||
logger.With("module", "node"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wait for rpc
|
||||
tests.WaitForRPC(GetConfig().RPC.ListenAddress)
|
||||
|
||||
logger.Info("Tendermint running!")
|
||||
return n, err
|
||||
}
|
||||
|
||||
// start the LCD. note this blocks!
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
|
||||
handler := createHandler(cdc)
|
||||
return tmrpc.StartHTTPServer(listenAddr, handler, logger)
|
||||
}
|
||||
|
||||
func request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, path)
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
|
||||
output, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
return res, string(output)
|
||||
}
|
||||
|
||||
func getAccount(t *testing.T, sendAddr string) auth.Account {
|
||||
//_____________________________________________________________________________
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
|
||||
func getAccount(t *testing.T, port string, addr sdk.Address) auth.Account {
|
||||
addrBech32 := sdk.MustBech32ifyAcc(addr)
|
||||
res, body := Request(t, port, "GET", "/accounts/"+addrBech32, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var acc auth.Account
|
||||
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
||||
|
@ -543,20 +414,34 @@ func getAccount(t *testing.T, sendAddr string) auth.Account {
|
|||
return acc
|
||||
}
|
||||
|
||||
func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doSend(t *testing.T, port, seed, name, password string, addr sdk.Address) (receiveAddr sdk.Address, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
|
||||
require.Nil(t, err)
|
||||
receiveAddr = receiveInfo.PubKey.Address().String()
|
||||
receiveAddr = receiveInfo.PubKey.Address()
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
|
||||
res, body := request(t, port, "POST", "/accounts/"+receiveAddr+"/send", jsonStr)
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name":"%s",
|
||||
"password":"%s",
|
||||
"account_number":%d,
|
||||
"sequence":%d,
|
||||
"gas": 10000,
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}`, name, password, accnum, sequence, "steak"))
|
||||
res, body := Request(t, port, "POST", "/accounts/"+receiveAddrBech+"/send", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
|
@ -565,21 +450,34 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
|
|||
return receiveAddr, resultTx
|
||||
}
|
||||
|
||||
func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
|
||||
func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
|
||||
require.Nil(t, err)
|
||||
receiveAddr := receiveInfo.PubKey.Address().String()
|
||||
receiveAddr := receiveInfo.PubKey.Address()
|
||||
receiveAddrBech := sdk.MustBech32ifyAcc(receiveAddr)
|
||||
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, addr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
|
||||
res, body := request(t, port, "POST", "/ibc/testchain/"+receiveAddr+"/send", jsonStr)
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name":"%s",
|
||||
"password": "%s",
|
||||
"account_number":%d,
|
||||
"sequence": %d,
|
||||
"gas": 100000,
|
||||
"amount":[
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}`, name, password, accnum, sequence, "steak"))
|
||||
res, body := Request(t, port, "POST", "/ibc/testchain/"+receiveAddrBech+"/send", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
|
@ -588,9 +486,13 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
|||
return resultTx
|
||||
}
|
||||
|
||||
func getDelegation(t *testing.T, delegatorAddr, candidateAddr string) stake.Delegation {
|
||||
func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.Address) stake.Delegation {
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/stake/"+delegatorAddr+"/bonding_status/"+candidateAddr, nil)
|
||||
res, body := Request(t, port, "GET", "/stake/"+delegatorAddrBech+"/bonding_status/"+validatorAddrBech, nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var bond stake.Delegation
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bond)
|
||||
|
@ -598,26 +500,32 @@ func getDelegation(t *testing.T, delegatorAddr, candidateAddr string) stake.Dele
|
|||
return bond
|
||||
}
|
||||
|
||||
func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doBond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": %d,
|
||||
"sequence": %d,
|
||||
"gas": 10000,
|
||||
"delegate": [
|
||||
{
|
||||
"delegator_addr": "%x",
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"bond": { "denom": "%s", "amount": 10 }
|
||||
"bond": { "denom": "%s", "amount": 60 }
|
||||
}
|
||||
],
|
||||
"unbond": []
|
||||
}`, name, password, sequence, acc.GetAddress(), validatorAddr1, coinDenom))
|
||||
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech, "steak"))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results []ctypes.ResultBroadcastTxCommit
|
||||
|
@ -627,26 +535,32 @@ func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxC
|
|||
return results[0]
|
||||
}
|
||||
|
||||
func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
func doUnbond(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.Address) (resultTx ctypes.ResultBroadcastTxCommit) {
|
||||
// get the account to get the sequence
|
||||
acc := getAccount(t, sendAddr)
|
||||
acc := getAccount(t, port, delegatorAddr)
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
delegatorAddrBech := sdk.MustBech32ifyAcc(delegatorAddr)
|
||||
validatorAddrBech := sdk.MustBech32ifyVal(validatorAddr)
|
||||
|
||||
// send
|
||||
jsonStr := []byte(fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"password": "%s",
|
||||
"account_number": %d,
|
||||
"sequence": %d,
|
||||
"bond": [],
|
||||
"gas": 10000,
|
||||
"delegate": [],
|
||||
"unbond": [
|
||||
{
|
||||
"delegator_addr": "%x",
|
||||
"delegator_addr": "%s",
|
||||
"validator_addr": "%s",
|
||||
"shares": "1"
|
||||
"shares": "30"
|
||||
}
|
||||
]
|
||||
}`, name, password, sequence, acc.GetAddress(), validatorAddr1))
|
||||
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
}`, name, password, accnum, sequence, delegatorAddrBech, validatorAddrBech))
|
||||
res, body := Request(t, port, "POST", "/stake/delegations", jsonStr)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var results []ctypes.ResultBroadcastTxCommit
|
||||
|
@ -656,11 +570,11 @@ func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastT
|
|||
return results[0]
|
||||
}
|
||||
|
||||
func getValidators(t *testing.T) []stake.Validator {
|
||||
func getValidators(t *testing.T, port string) []stakerest.StakeValidatorOutput {
|
||||
// get the account to get the sequence
|
||||
res, body := request(t, port, "GET", "/stake/validators", nil)
|
||||
res, body := Request(t, port, "GET", "/stake/validators", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validators stake.Validators
|
||||
var validators []stakerest.StakeValidatorOutput
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validators)
|
||||
require.Nil(t, err)
|
||||
return validators
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
)
|
||||
|
||||
var node *nm.Node
|
||||
|
||||
// See https://golang.org/pkg/testing/#hdr-Main
|
||||
// for more details
|
||||
func TestMain(m *testing.M) {
|
||||
// start a basecoind node and LCD server in the background to test against
|
||||
|
||||
// run all the tests against a single server instance
|
||||
node, lcd, err := startTMAndLCD()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
// tear down
|
||||
// TODO: cleanup
|
||||
// TODO: it would be great if TM could run without
|
||||
// persiting anything in the first place
|
||||
node.Stop()
|
||||
node.Wait()
|
||||
|
||||
// just a listener ...
|
||||
lcd.Close()
|
||||
|
||||
os.Exit(code)
|
||||
}
|
|
@ -25,29 +25,17 @@ import (
|
|||
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
flagListenAddr = "laddr"
|
||||
flagCORS = "cors"
|
||||
)
|
||||
|
||||
// ServeCommand will generate a long-running rest server
|
||||
// (aka Light Client Daemon) that exposes functionality similar
|
||||
// to the cli, but over rest
|
||||
func ServeCommand(cdc *wire.Codec) *cobra.Command {
|
||||
flagListenAddr := "laddr"
|
||||
flagCORS := "cors"
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rest-server",
|
||||
Short: "Start LCD (light-client daemon), a local REST server",
|
||||
RunE: startRESTServerFn(cdc),
|
||||
}
|
||||
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")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listenAddr := viper.GetString(flagListenAddr)
|
||||
handler := createHandler(cdc)
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||
|
@ -64,7 +52,13 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
|
|||
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")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func createHandler(cdc *wire.Codec) http.Handler {
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
package lcd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
crkeys "github.com/tendermint/go-crypto/keys"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
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"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"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"
|
||||
)
|
||||
|
||||
// f**ing long, but unique for each test
|
||||
func makePathname() string {
|
||||
// get path
|
||||
p, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sep := string(filepath.Separator)
|
||||
return strings.Replace(p, sep, "_", -1)
|
||||
}
|
||||
|
||||
// GetConfig returns a config for the test cases as a singleton
|
||||
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)
|
||||
}
|
||||
|
||||
config.P2P.ListenAddress = tmAddr
|
||||
config.RPC.ListenAddress = rcpAddr
|
||||
return config
|
||||
}
|
||||
|
||||
// get the lcd test keybase
|
||||
// note can't use a memdb because the request is expecting to interact with the default location
|
||||
func GetKB(t *testing.T) crkeys.Keybase {
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
require.NoError(t, err)
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
keybase, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
require.NoError(t, err)
|
||||
return keybase
|
||||
}
|
||||
|
||||
// add an address to the store return name and password
|
||||
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (addr sdk.Address, seed string) {
|
||||
var info crkeys.Info
|
||||
var err error
|
||||
info, seed, err = kb.Create(name, password, crkeys.AlgoEd25519)
|
||||
require.NoError(t, err)
|
||||
addr = info.PubKey.Address()
|
||||
return
|
||||
}
|
||||
|
||||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
// nValidators = number of validators
|
||||
// initAddrs = accounts to initialize with some steaks
|
||||
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) (cleanup func(), validatorsPKs []crypto.PubKey, port string) {
|
||||
|
||||
config := GetConfig()
|
||||
config.Consensus.TimeoutCommit = 1000
|
||||
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 := gapp.NewGaiaApp(logger, db)
|
||||
cdc = gapp.MakeCodec() // XXX
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add more validators
|
||||
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: crypto.GenPrivKeyEd25519().PubKey(),
|
||||
Power: 1,
|
||||
Name: "val",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// NOTE it's bad practice to reuse pk 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) // append keys for output
|
||||
appGenTx, _, _, err := gapp.GaiaAppGenTxNF(cdc, pk, pk.Address(), "test_val1", true)
|
||||
require.NoError(t, err)
|
||||
appGenTxs = append(appGenTxs, appGenTx)
|
||||
}
|
||||
|
||||
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
// add some tokens to init accounts
|
||||
for _, addr := range initAddrs {
|
||||
accAuth := auth.NewBaseAccountWithAddress(addr)
|
||||
accAuth.Coins = sdk.Coins{{"steak", 100}}
|
||||
acc := gapp.NewGenesisAccount(&accAuth)
|
||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||
}
|
||||
|
||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||
require.NoError(t, err)
|
||||
genDoc.AppStateJSON = appState
|
||||
|
||||
// LCD listen address
|
||||
var listenAddr string
|
||||
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)
|
||||
|
||||
//time.Sleep(time.Second)
|
||||
//tests.WaitForHeight(2, port)
|
||||
tests.WaitForStart(port)
|
||||
tests.WaitForHeight(1, port)
|
||||
|
||||
// for use in defer
|
||||
cleanup = func() {
|
||||
node.Stop()
|
||||
node.Wait()
|
||||
lcd.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Create & start in-process tendermint node with memdb
|
||||
// and in-process abci application.
|
||||
// TODO: need to 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 }
|
||||
n, err := nm.NewNode(tmcfg,
|
||||
privVal,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
dbProvider,
|
||||
logger.With("module", "node"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wait for rpc
|
||||
tests.WaitForRPC(tmcfg.RPC.ListenAddress)
|
||||
|
||||
logger.Info("Tendermint running!")
|
||||
return n, err
|
||||
}
|
||||
|
||||
// start the LCD. note this blocks!
|
||||
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
|
||||
handler := createHandler(cdc)
|
||||
return tmrpc.StartHTTPServer(listenAddr, handler, logger)
|
||||
}
|
||||
|
||||
// make a test lcd test request
|
||||
func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, path)
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
// res, err = http.Post(url, "application/json", bytes.NewBuffer(payload))
|
||||
require.Nil(t, err)
|
||||
|
||||
output, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
return res, string(output)
|
||||
}
|
|
@ -10,6 +10,8 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// TODO these next two functions feel kinda hacky based on their placement
|
||||
|
@ -28,6 +30,38 @@ func ValidatorCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// Validator output in bech32 format
|
||||
type ValidatorOutput struct {
|
||||
Address string `json:"address"` // in bech32
|
||||
PubKey string `json:"pub_key"` // in bech32
|
||||
Accum int64 `json:"accum"`
|
||||
VotingPower int64 `json:"voting_power"`
|
||||
}
|
||||
|
||||
// Validators at a certain height output in bech32 format
|
||||
type ResultValidatorsOutput struct {
|
||||
BlockHeight int64 `json:"block_height"`
|
||||
Validators []ValidatorOutput `json:"validators"`
|
||||
}
|
||||
|
||||
func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) {
|
||||
bechAddress, err := sdk.Bech32ifyVal(validator.Address)
|
||||
if err != nil {
|
||||
return ValidatorOutput{}, err
|
||||
}
|
||||
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
|
||||
if err != nil {
|
||||
return ValidatorOutput{}, err
|
||||
}
|
||||
|
||||
return ValidatorOutput{
|
||||
Address: bechAddress,
|
||||
PubKey: bechValPubkey,
|
||||
Accum: validator.Accum,
|
||||
VotingPower: validator.VotingPower,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
node, err := ctx.GetNode()
|
||||
|
@ -35,12 +69,23 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
res, err := node.Validators(height)
|
||||
validatorsRes, err := node.Validators(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(res)
|
||||
outputValidatorsRes := ResultValidatorsOutput{
|
||||
BlockHeight: validatorsRes.BlockHeight,
|
||||
Validators: make([]ValidatorOutput, len(validatorsRes.Validators)),
|
||||
}
|
||||
for i := 0; i < len(validatorsRes.Validators); i++ {
|
||||
outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(outputValidatorsRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -96,6 +141,7 @@ func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
|||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
|
|||
// register REST routes
|
||||
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, ctx)).Methods("GET")
|
||||
// r.HandleFunc("/txs", SearchTxRequestHandler(cdc)).Methods("GET")
|
||||
r.HandleFunc("/txs", SearchTxRequestHandlerFn(ctx, cdc)).Methods("GET")
|
||||
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
|
||||
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
|
@ -29,7 +30,11 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
tags := viper.GetStringSlice(flagTags)
|
||||
|
||||
output, err := searchTx(context.NewCoreContextFromViper(), cdc, tags)
|
||||
txs, err := searchTxs(context.NewCoreContextFromViper(), cdc, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := cdc.MarshalJSON(txs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -47,13 +52,12 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte, error) {
|
||||
func searchTxs(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]txInfo, error) {
|
||||
if len(tags) == 0 {
|
||||
return nil, errors.New("Must declare at least one tag to search")
|
||||
}
|
||||
// XXX: implement ANY
|
||||
query := strings.Join(tags, " AND ")
|
||||
|
||||
// get the node
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
|
@ -74,11 +78,7 @@ func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return output, nil
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error) {
|
||||
|
@ -102,17 +102,44 @@ func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.Han
|
|||
tag := r.FormValue("tag")
|
||||
if tag == "" {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("You need to provide a tag to search for."))
|
||||
w.Write([]byte("You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys"))
|
||||
return
|
||||
}
|
||||
keyValue := strings.Split(tag, "=")
|
||||
key := keyValue[0]
|
||||
value := keyValue[1]
|
||||
if strings.HasSuffix(key, "_bech32") {
|
||||
bech32address := strings.Trim(value, "'")
|
||||
prefix := strings.Split(bech32address, "1")[0]
|
||||
bz, err := sdk.GetFromBech32(bech32address, prefix)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
tags := []string{tag}
|
||||
output, err := searchTx(ctx, cdc, tags)
|
||||
tag = strings.TrimRight(key, "_bech32") + "='" + sdk.Address(bz).String() + "'"
|
||||
}
|
||||
|
||||
txs, err := searchTxs(ctx, cdc, []string{tag})
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if len(txs) == 0 {
|
||||
w.Write([]byte("[]"))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(txs)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -81,7 +82,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
|||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
|
||||
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetInitChainer(app.initChainer)
|
||||
|
@ -143,6 +145,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
|
|||
// load the accounts
|
||||
for _, gacc := range genesisState.Accounts {
|
||||
acc := gacc.ToAccount()
|
||||
acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx)
|
||||
app.accountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
|
@ -153,7 +156,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
|
|||
}
|
||||
|
||||
// export the state of gaia for a genesis file
|
||||
func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
||||
func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// iterate to get the accounts
|
||||
|
@ -169,5 +172,10 @@ func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
|||
Accounts: accounts,
|
||||
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
|
||||
}
|
||||
return wire.MarshalJSONIndent(app.cdc, genState)
|
||||
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
validators = stake.WriteValidators(ctx, app.stakeKeeper)
|
||||
return appState, validators, nil
|
||||
}
|
||||
|
|
|
@ -1,101 +1,13 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
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/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
// Construct some global addrs and txs for tests.
|
||||
var (
|
||||
chainID = "" // TODO
|
||||
|
||||
accName = "foobart"
|
||||
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
halfCoins = sdk.Coins{{"foocoin", 5}}
|
||||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
100000,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
sendMsg2 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, halfCoins),
|
||||
bank.NewOutput(addr3, halfCoins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg3 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, coins),
|
||||
bank.NewInput(addr4, coins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, coins),
|
||||
bank.NewOutput(addr3, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg4 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr2, coins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr1, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg5 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, manyCoins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, manyCoins),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func loggerAndDB() (log.Logger, dbm.DB) {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
return logger, db
|
||||
}
|
||||
|
||||
func newGaiaApp() *GaiaApp {
|
||||
logger, db := loggerAndDB()
|
||||
return NewGaiaApp(logger, db)
|
||||
}
|
||||
|
||||
func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
||||
genaccs := make([]GenesisAccount, len(accs))
|
||||
for i, acc := range accs {
|
||||
|
@ -119,382 +31,3 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func TestMsgs(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
require.Nil(t, setGenesis(gapp))
|
||||
|
||||
msgs := []struct {
|
||||
msg sdk.Msg
|
||||
}{
|
||||
{sendMsg1},
|
||||
}
|
||||
|
||||
for i, m := range msgs {
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, gapp, m.msg, []int64{int64(i)}, false, priv1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesis(t *testing.T) {
|
||||
logger, dbs := loggerAndDB()
|
||||
gapp := NewGaiaApp(logger, dbs)
|
||||
|
||||
// Construct some genesis bytes to reflect GaiaAccount
|
||||
pk := crypto.GenPrivKeyEd25519().PubKey()
|
||||
addr := pk.Address()
|
||||
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := &auth.BaseAccount{
|
||||
Address: addr,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
err = setGenesis(gapp, baseAcc)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context
|
||||
ctx := gapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := gapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, baseAcc, res1)
|
||||
|
||||
// reload app and ensure the account is still there
|
||||
gapp = NewGaiaApp(logger, dbs)
|
||||
ctx = gapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 = gapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, baseAcc, res1)
|
||||
}
|
||||
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
// Construct some genesis bytes to reflect GaiaAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins, err := sdk.ParseCoins("77foocoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesis(gapp, baseAcc)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, baseAcc, res1.(*auth.BaseAccount))
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, gapp, addr1, "67foocoin")
|
||||
CheckBalance(t, gapp, addr2, "10foocoin")
|
||||
|
||||
// Delivering again should cause replay error
|
||||
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, false, priv1)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
tx := genTx(sendMsg1, []int64{0}, priv1)
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res := gapp.Deliver(tx)
|
||||
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
SignCheckDeliver(t, gapp, sendMsg1, []int64{1}, true, priv1)
|
||||
}
|
||||
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
err = setGenesis(gapp, acc1, acc2)
|
||||
require.Nil(t, err)
|
||||
|
||||
// Simulate a Block
|
||||
SignCheckDeliver(t, gapp, sendMsg2, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, gapp, addr1, "32foocoin")
|
||||
CheckBalance(t, gapp, addr2, "47foocoin")
|
||||
CheckBalance(t, gapp, addr3, "5foocoin")
|
||||
}
|
||||
|
||||
func TestSengMsgMultipleInOut(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: genCoins,
|
||||
}
|
||||
acc4 := &auth.BaseAccount{
|
||||
Address: addr4,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
err = setGenesis(gapp, acc1, acc2, acc4)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// CheckDeliver
|
||||
SignCheckDeliver(t, gapp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, gapp, addr1, "32foocoin")
|
||||
CheckBalance(t, gapp, addr4, "32foocoin")
|
||||
CheckBalance(t, gapp, addr2, "52foocoin")
|
||||
CheckBalance(t, gapp, addr3, "10foocoin")
|
||||
}
|
||||
|
||||
func TestMsgSendDependent(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
err = setGenesis(gapp, acc1)
|
||||
require.Nil(t, err)
|
||||
|
||||
// CheckDeliver
|
||||
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, gapp, addr1, "32foocoin")
|
||||
CheckBalance(t, gapp, addr2, "10foocoin")
|
||||
|
||||
// Simulate a Block
|
||||
SignCheckDeliver(t, gapp, sendMsg4, []int64{0}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, gapp, addr1, "42foocoin")
|
||||
}
|
||||
|
||||
func TestIBCMsgs(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
sourceChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
baseAcc := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
err := setGenesis(gapp, baseAcc)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, baseAcc, res1)
|
||||
|
||||
packet := ibc.IBCPacket{
|
||||
SrcAddr: addr1,
|
||||
DestAddr: addr1,
|
||||
Coins: coins,
|
||||
SrcChain: sourceChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
|
||||
transferMsg := ibc.IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
receiveMsg := ibc.IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: addr1,
|
||||
Sequence: 0,
|
||||
}
|
||||
|
||||
SignCheckDeliver(t, gapp, transferMsg, []int64{0}, true, priv1)
|
||||
CheckBalance(t, gapp, addr1, "")
|
||||
SignCheckDeliver(t, gapp, transferMsg, []int64{1}, false, priv1)
|
||||
SignCheckDeliver(t, gapp, receiveMsg, []int64{2}, true, priv1)
|
||||
CheckBalance(t, gapp, addr1, "10foocoin")
|
||||
SignCheckDeliver(t, gapp, receiveMsg, []int64{3}, false, priv1)
|
||||
}
|
||||
|
||||
func TestStakeMsgs(t *testing.T) {
|
||||
gapp := newGaiaApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42steak")
|
||||
require.Nil(t, err)
|
||||
bondCoin, err := sdk.ParseCoin("10steak")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
err = setGenesis(gapp, acc1, acc2)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
res2 := gapp.accountMapper.GetAccount(ctxCheck, addr2)
|
||||
require.Equal(t, acc1, res1)
|
||||
require.Equal(t, acc2, res2)
|
||||
|
||||
// Create Validator
|
||||
|
||||
description := stake.NewDescription("foo_moniker", "", "", "")
|
||||
createValidatorMsg := stake.NewMsgCreateValidator(
|
||||
addr1, priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
SignCheckDeliver(t, gapp, createValidatorMsg, []int64{0}, true, priv1)
|
||||
|
||||
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res1.GetCoins())
|
||||
validator, found := gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, addr1, validator.Owner)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
|
||||
// check the bond that should have been created as well
|
||||
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1)
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
|
||||
|
||||
// Edit Validator
|
||||
|
||||
description = stake.NewDescription("bar_moniker", "", "", "")
|
||||
editValidatorMsg := stake.NewMsgEditValidator(
|
||||
addr1, description,
|
||||
)
|
||||
SignDeliver(t, gapp, editValidatorMsg, []int64{1}, true, priv1)
|
||||
|
||||
validator, found = gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, description, validator.Description)
|
||||
|
||||
// Delegate
|
||||
|
||||
delegateMsg := stake.NewMsgDelegate(
|
||||
addr2, addr1, bondCoin,
|
||||
)
|
||||
SignDeliver(t, gapp, delegateMsg, []int64{0}, true, priv2)
|
||||
|
||||
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins())
|
||||
bond, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, addr2, bond.DelegatorAddr)
|
||||
require.Equal(t, addr1, bond.ValidatorAddr)
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
|
||||
|
||||
// Unbond
|
||||
|
||||
unbondMsg := stake.NewMsgUnbond(
|
||||
addr2, addr1, "MAX",
|
||||
)
|
||||
SignDeliver(t, gapp, unbondMsg, []int64{1}, true, priv2)
|
||||
|
||||
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||
require.Equal(t, genCoins, res2.GetCoins())
|
||||
_, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
//____________________________________________________________________________________
|
||||
|
||||
func CheckBalance(t *testing.T, gapp *GaiaApp, addr sdk.Address, balExpected string) {
|
||||
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 := gapp.accountMapper.GetAccount(ctxDeliver, addr)
|
||||
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
|
||||
}
|
||||
|
||||
func genTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
for i, p := range priv {
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: p.Sign(auth.StdSignBytes(chainID, seq, fee, msg)),
|
||||
Sequence: seq[i],
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msg, fee, sigs)
|
||||
|
||||
}
|
||||
|
||||
func SignCheckDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
|
||||
|
||||
// Sign the tx
|
||||
tx := genTx(msg, seq, priv...)
|
||||
|
||||
// Run a Check
|
||||
res := gapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
gapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = gapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
gapp.EndBlock(abci.RequestEndBlock{})
|
||||
|
||||
// XXX fix code or add explaination as to why using commit breaks a bunch of these tests
|
||||
//gapp.Commit()
|
||||
}
|
||||
|
||||
// XXX the only reason we are using Sign Deliver here is because the tests
|
||||
// break on check tx the second time you use SignCheckDeliver in a test because
|
||||
// the checktx state has not been updated likely because commit is not being
|
||||
// called!
|
||||
func SignDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
|
||||
|
||||
// Sign the tx
|
||||
tx := genTx(msg, seq, priv...)
|
||||
|
||||
// Simulate a Block
|
||||
gapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res := gapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
gapp.EndBlock(abci.RequestEndBlock{})
|
||||
}
|
||||
|
|
|
@ -108,7 +108,8 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
|
|||
return
|
||||
}
|
||||
cliPrint = json.RawMessage(bz)
|
||||
return GaiaAppGenTxNF(cdc, pk, addr, name, overwrite)
|
||||
appGenTx,_,validator,err = GaiaAppGenTxNF(cdc, pk, addr, name, overwrite)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a gaia genesis transaction without flags
|
||||
|
|
|
@ -170,7 +170,13 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.Address, crypto.PubKey)
|
|||
var ko keys.KeyOutput
|
||||
keys.UnmarshalJSON([]byte(out), &ko)
|
||||
|
||||
return ko.Address, ko.PubKey
|
||||
address, err := sdk.GetAccAddressBech32(ko.Address)
|
||||
require.NoError(t, err)
|
||||
|
||||
pk, err := sdk.GetAccPubKeyBech32(ko.PubKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
return address, pk
|
||||
}
|
||||
|
||||
func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -26,7 +27,7 @@ func main() {
|
|||
|
||||
server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(),
|
||||
server.ConstructAppCreator(newApp, "gaia"),
|
||||
server.ConstructAppExporter(exportAppState, "gaia"))
|
||||
server.ConstructAppExporter(exportAppStateAndTMValidators, "gaia"))
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome)
|
||||
|
@ -37,7 +38,7 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
|||
return app.NewGaiaApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
gapp := app.NewGaiaApp(logger, db)
|
||||
return gapp.ExportAppStateJSON()
|
||||
return gapp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
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/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
||||
gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
)
|
||||
|
||||
func runHackCmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Expected 1 arg")
|
||||
}
|
||||
|
||||
// ".gaiad"
|
||||
dataDir := args[0]
|
||||
dataDir = path.Join(dataDir, "data")
|
||||
|
||||
// load the app
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
db, err := dbm.NewGoLevelDB("gaia", dataDir)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
app := NewGaiaApp(logger, db)
|
||||
|
||||
// print some info
|
||||
id := app.LastCommitID()
|
||||
lastBlockHeight := app.LastBlockHeight()
|
||||
fmt.Println("ID", id)
|
||||
fmt.Println("LastBlockHeight", lastBlockHeight)
|
||||
|
||||
//----------------------------------------------------
|
||||
// XXX: start hacking!
|
||||
//----------------------------------------------------
|
||||
// eg. gaia-6001 testnet bug
|
||||
// We paniced when iterating through the "bypower" keys.
|
||||
// The following powerKey was there, but the corresponding "trouble" validator did not exist.
|
||||
// So here we do a binary search on the past states to find when the powerKey first showed up ...
|
||||
|
||||
// owner of the validator the bonds, gets revoked, later unbonds, and then later is still found in the bypower store
|
||||
trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||
// this is his "bypower" key
|
||||
powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||
|
||||
topHeight := lastBlockHeight
|
||||
bottomHeight := int64(0)
|
||||
checkHeight := topHeight
|
||||
for {
|
||||
// load the given version of the state
|
||||
err = app.LoadVersion(checkHeight, app.keyMain)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// check for the powerkey and the validator from the store
|
||||
store := ctx.KVStore(app.keyStake)
|
||||
res := store.Get(powerKey)
|
||||
val, _ := app.stakeKeeper.GetValidator(ctx, trouble)
|
||||
fmt.Println("checking height", checkHeight, res, val)
|
||||
if res == nil {
|
||||
bottomHeight = checkHeight
|
||||
} else {
|
||||
topHeight = checkHeight
|
||||
}
|
||||
checkHeight = (topHeight + bottomHeight) / 2
|
||||
}
|
||||
}
|
||||
|
||||
func base64ToPub(b64 string) crypto.PubKeyEd25519 {
|
||||
data, _ := base64.StdEncoding.DecodeString(b64)
|
||||
var pubKey crypto.PubKeyEd25519
|
||||
copy(pubKey[:], data)
|
||||
return pubKey
|
||||
|
||||
}
|
||||
|
||||
func hexToBytes(h string) []byte {
|
||||
trouble, _ := hex.DecodeString(h)
|
||||
return trouble
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// NOTE: This is all copied from gaia/app/app.go
|
||||
// so we can access internal fields!
|
||||
|
||||
const (
|
||||
appName = "GaiaApp"
|
||||
)
|
||||
|
||||
// default home directories for expected binaries
|
||||
var (
|
||||
DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli")
|
||||
DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad")
|
||||
)
|
||||
|
||||
// Extended ABCI application
|
||||
type GaiaApp struct {
|
||||
*bam.BaseApp
|
||||
cdc *wire.Codec
|
||||
|
||||
// keys to access the substores
|
||||
keyMain *sdk.KVStoreKey
|
||||
keyAccount *sdk.KVStoreKey
|
||||
keyIBC *sdk.KVStoreKey
|
||||
keyStake *sdk.KVStoreKey
|
||||
keySlashing *sdk.KVStoreKey
|
||||
|
||||
// Manage getting and setting accounts
|
||||
accountMapper auth.AccountMapper
|
||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||
coinKeeper bank.Keeper
|
||||
ibcMapper ibc.Mapper
|
||||
stakeKeeper stake.Keeper
|
||||
slashingKeeper slashing.Keeper
|
||||
}
|
||||
|
||||
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
||||
cdc := MakeCodec()
|
||||
|
||||
// create your application object
|
||||
var app = &GaiaApp{
|
||||
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
||||
cdc: cdc,
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||
keyStake: sdk.NewKVStoreKey("stake"),
|
||||
keySlashing: sdk.NewKVStoreKey("slashing"),
|
||||
}
|
||||
|
||||
// define the accountMapper
|
||||
app.accountMapper = auth.NewAccountMapper(
|
||||
app.cdc,
|
||||
app.keyAccount, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
|
||||
// add handlers
|
||||
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
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))
|
||||
|
||||
// register message routes
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetInitChainer(app.initChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetEndBlocker(app.EndBlocker)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
|
||||
err := app.LoadLatestVersion(app.keyMain)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// custom tx codec
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
ibc.RegisterWire(cdc)
|
||||
bank.RegisterWire(cdc)
|
||||
stake.RegisterWire(cdc)
|
||||
slashing.RegisterWire(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
return cdc
|
||||
}
|
||||
|
||||
// application updates every end block
|
||||
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
|
||||
|
||||
return abci.ResponseBeginBlock{
|
||||
Tags: tags.ToKVPairs(),
|
||||
}
|
||||
}
|
||||
|
||||
// application updates every end block
|
||||
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
|
||||
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: validatorUpdates,
|
||||
}
|
||||
}
|
||||
|
||||
// custom logic for gaia initialization
|
||||
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
// TODO is this now the whole genesis file?
|
||||
|
||||
var genesisState gaia.GenesisState
|
||||
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
|
||||
// load the accounts
|
||||
for _, gacc := range genesisState.Accounts {
|
||||
acc := gacc.ToAccount()
|
||||
app.accountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
// load the initial stake information
|
||||
stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
|
||||
return abci.ResponseInitChain{}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/spf13/cobra"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(txCmd)
|
||||
rootCmd.AddCommand(pubkeyCmd)
|
||||
rootCmd.AddCommand(hackCmd)
|
||||
rootCmd.AddCommand(rawBytesCmd)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "gaiadebug",
|
||||
Short: "Gaia debug tool",
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var txCmd = &cobra.Command{
|
||||
Use: "tx",
|
||||
Short: "Decode a gaia tx from hex or base64",
|
||||
RunE: runTxCmd,
|
||||
}
|
||||
|
||||
var pubkeyCmd = &cobra.Command{
|
||||
Use: "pubkey",
|
||||
Short: "Decode a pubkey from hex or base64",
|
||||
RunE: runPubKeyCmd,
|
||||
}
|
||||
|
||||
var hackCmd = &cobra.Command{
|
||||
Use: "hack",
|
||||
Short: "Boilerplate to Hack on an existing state by scripting some Go...",
|
||||
RunE: runHackCmd,
|
||||
}
|
||||
|
||||
var rawBytesCmd = &cobra.Command{
|
||||
Use: "raw-bytes",
|
||||
Short: "Convert raw bytes output (eg. [10 21 13 255]) to hex",
|
||||
RunE: runRawBytesCmd,
|
||||
}
|
||||
|
||||
func runRawBytesCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Expected single arg")
|
||||
}
|
||||
stringBytes := args[0]
|
||||
stringBytes = strings.Trim(stringBytes, "[")
|
||||
stringBytes = strings.Trim(stringBytes, "]")
|
||||
spl := strings.Split(stringBytes, " ")
|
||||
|
||||
byteArray := []byte{}
|
||||
for _, s := range spl {
|
||||
b, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
byteArray = append(byteArray, byte(b))
|
||||
}
|
||||
fmt.Printf("%X\n", byteArray)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPubKeyCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Expected single arg")
|
||||
}
|
||||
|
||||
pubkeyString := args[0]
|
||||
|
||||
// try hex, then base64
|
||||
pubkeyBytes, err := hex.DecodeString(pubkeyString)
|
||||
if err != nil {
|
||||
var err2 error
|
||||
pubkeyBytes, err2 = base64.StdEncoding.DecodeString(pubkeyString)
|
||||
if err2 != nil {
|
||||
return fmt.Errorf(`Expected hex or base64. Got errors:
|
||||
hex: %v,
|
||||
base64: %v
|
||||
`, err, err2)
|
||||
}
|
||||
}
|
||||
|
||||
cdc := gaia.MakeCodec()
|
||||
var pubKey crypto.PubKeyEd25519
|
||||
copy(pubKey[:], pubkeyBytes)
|
||||
pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Address:", pubKey.Address())
|
||||
fmt.Printf("Hex: %X\n", pubkeyBytes)
|
||||
fmt.Println("JSON (base64):", string(pubKeyJSONBytes))
|
||||
return nil
|
||||
}
|
||||
|
||||
func runTxCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Expected single arg")
|
||||
}
|
||||
|
||||
txString := args[0]
|
||||
|
||||
// try hex, then base64
|
||||
txBytes, err := hex.DecodeString(txString)
|
||||
if err != nil {
|
||||
var err2 error
|
||||
txBytes, err2 = base64.StdEncoding.DecodeString(txString)
|
||||
if err2 != nil {
|
||||
return fmt.Errorf(`Expected hex or base64. Got errors:
|
||||
hex: %v,
|
||||
base64: %v
|
||||
`, err, err2)
|
||||
}
|
||||
}
|
||||
|
||||
var tx = auth.StdTx{}
|
||||
cdc := gaia.MakeCodec()
|
||||
|
||||
err = cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bz, err := cdc.MarshalJSON(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err = json.Indent(buf, bz, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(buf.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
|
@ -102,7 +102,7 @@ paths:
|
|||
- application/json
|
||||
responses:
|
||||
200:
|
||||
description: 12 word Seed
|
||||
description: 16 word Seed
|
||||
schema:
|
||||
type: string
|
||||
/keys/{name}:
|
||||
|
@ -204,7 +204,7 @@ paths:
|
|||
parameters:
|
||||
- in: path
|
||||
name: address
|
||||
description: Account address
|
||||
description: Account address in bech32 format
|
||||
required: true
|
||||
type: string
|
||||
get:
|
||||
|
@ -222,7 +222,7 @@ paths:
|
|||
parameters:
|
||||
- in: path
|
||||
name: address
|
||||
description: Account address
|
||||
description: Account address in bech32 format
|
||||
required: true
|
||||
type: string
|
||||
post:
|
||||
|
@ -255,18 +255,6 @@ paths:
|
|||
description: Tx was send and will probably be added to the next block
|
||||
400:
|
||||
description: The Tx was malformated
|
||||
/accounts/{address}/nonce:
|
||||
parameters:
|
||||
- in: path
|
||||
name: address
|
||||
description: Account address
|
||||
required: true
|
||||
type: string
|
||||
get:
|
||||
summary: Get the nonce for a certain account
|
||||
responses:
|
||||
200:
|
||||
description: Plaintext nonce i.e. "4" defaults to "0"
|
||||
/blocks/latest:
|
||||
get:
|
||||
summary: Get the latest block
|
||||
|
@ -304,9 +292,14 @@ paths:
|
|||
200:
|
||||
description: The validator set at the latest block height
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
block_height:
|
||||
type: number
|
||||
validators:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/Delegate"
|
||||
$ref: "#/definitions/Validator"
|
||||
/validatorsets/{height}:
|
||||
parameters:
|
||||
- in: path
|
||||
|
@ -322,9 +315,14 @@ paths:
|
|||
200:
|
||||
description: The validator set at a specific block height
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
block_height:
|
||||
type: number
|
||||
validators:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/Delegate"
|
||||
$ref: "#/definitions/Validator"
|
||||
404:
|
||||
description: Block at height not available
|
||||
# /txs:
|
||||
|
@ -549,7 +547,20 @@ paths:
|
|||
definitions:
|
||||
Address:
|
||||
type: string
|
||||
example: DF096FDE8D380FA5B2AD20DB2962C82DDEA1ED9B
|
||||
description: bech32 encoded addres
|
||||
example: cosmosaccaddr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||
ValidatorAddress:
|
||||
type: string
|
||||
description: bech32 encoded addres
|
||||
example: cosmosvaladdr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||
PubKey:
|
||||
type: string
|
||||
description: bech32 encoded public key
|
||||
example: cosmosaccpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||
ValidatorPubKey:
|
||||
type: string
|
||||
description: bech32 encoded public key
|
||||
example: cosmosvalpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
|
||||
Coins:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -652,16 +663,6 @@ definitions:
|
|||
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
|
||||
Pubkey:
|
||||
$ref: "#/definitions/PubKey"
|
||||
PubKey:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- ed25519
|
||||
data:
|
||||
type: string
|
||||
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
|
||||
Account:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -753,17 +754,19 @@ definitions:
|
|||
type: array
|
||||
items:
|
||||
type: object
|
||||
Delegate:
|
||||
Validator:
|
||||
type: object
|
||||
properties:
|
||||
address:
|
||||
$ref: '#/definitions/ValidatorAddress'
|
||||
pub_key:
|
||||
$ref: "#/definitions/PubKey"
|
||||
$ref: "#/definitions/ValidatorPubKey"
|
||||
power:
|
||||
type: number
|
||||
example: 1000
|
||||
name:
|
||||
type: string
|
||||
example: "159.89.3.34"
|
||||
accum:
|
||||
type: number
|
||||
example: 1000
|
||||
# Added by API Auto Mocking Plugin
|
||||
host: virtserver.swaggerhub.com
|
||||
basePath: /faboweb1/Cosmos-LCD-2/1.0.0
|
||||
|
|
|
@ -232,12 +232,14 @@ a standard form:
|
|||
type StdSignature struct {
|
||||
crypto.PubKey // optional
|
||||
crypto.Signature
|
||||
AccountNumber int64
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
It contains the signature itself, as well as the corresponding account's
|
||||
sequence number. The sequence number is expected to increment every time a
|
||||
message is signed by a given account. This prevents "replay attacks", where
|
||||
It contains the signature itself, as well as the corresponding account's account and
|
||||
sequence numbers. The sequence number is expected to increment every time a
|
||||
message is signed by a given account. The account number stays the same and is assigned
|
||||
when the account is first generated. These prevent "replay attacks", where
|
||||
the same message could be executed over and over again.
|
||||
|
||||
The ``StdSignature`` can also optionally include the public key for verifying the
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -145,6 +146,7 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
|
|||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx)
|
||||
app.accountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
|
@ -155,7 +157,7 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
|
|||
}
|
||||
|
||||
// Custom logic for state export
|
||||
func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
||||
func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// iterate to get the accounts
|
||||
|
@ -173,5 +175,10 @@ func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
|
|||
genState := types.GenesisState{
|
||||
Accounts: accounts,
|
||||
}
|
||||
return wire.MarshalJSONIndent(app.cdc, genState)
|
||||
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
validators = stake.WriteValidators(ctx, app.stakeKeeper)
|
||||
return appState, validators, err
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
@ -13,8 +11,6 @@ import (
|
|||
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/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
@ -23,74 +19,10 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
// Construct some global addrs and txs for tests.
|
||||
var (
|
||||
chainID = "" // TODO
|
||||
|
||||
accName = "foobart"
|
||||
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
halfCoins = sdk.Coins{{"foocoin", 5}}
|
||||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
100000,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
sendMsg2 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, halfCoins),
|
||||
bank.NewOutput(addr3, halfCoins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg3 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, coins),
|
||||
bank.NewInput(addr4, coins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, coins),
|
||||
bank.NewOutput(addr3, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg4 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr2, coins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr1, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg5 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, manyCoins),
|
||||
},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, manyCoins),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func setGenesis(bapp *BasecoinApp, accs ...auth.BaseAccount) error {
|
||||
genaccs := make([]*types.GenesisAccount, len(accs))
|
||||
for i, acc := range accs {
|
||||
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, accName})
|
||||
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, "foobart"})
|
||||
}
|
||||
|
||||
genesisState := types.GenesisState{
|
||||
|
@ -111,79 +43,11 @@ func setGenesis(bapp *BasecoinApp, accs ...auth.BaseAccount) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func loggerAndDB() (log.Logger, dbm.DB) {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
return logger, db
|
||||
}
|
||||
|
||||
func newBasecoinApp() *BasecoinApp {
|
||||
logger, db := loggerAndDB()
|
||||
return NewBasecoinApp(logger, db)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func TestMsgs(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
require.Nil(t, setGenesis(bapp))
|
||||
|
||||
msgs := []struct {
|
||||
msg sdk.Msg
|
||||
}{
|
||||
{sendMsg1},
|
||||
}
|
||||
|
||||
for i, m := range msgs {
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, bapp, m.msg, []int64{int64(i)}, false, priv1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortGenesis(t *testing.T) {
|
||||
logger, db := loggerAndDB()
|
||||
bapp := NewBasecoinApp(logger, db)
|
||||
|
||||
// Note the order: the coins are unsorted!
|
||||
coinDenom1, coinDenom2 := "foocoin", "barcoin"
|
||||
|
||||
genState := fmt.Sprintf(`{
|
||||
"accounts": [{
|
||||
"address": "%s",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 10
|
||||
},
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 20
|
||||
}
|
||||
]
|
||||
}]
|
||||
}`, addr1.String(), coinDenom1, coinDenom2)
|
||||
|
||||
// Initialize the chain
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: []byte(genState)})
|
||||
bapp.Commit()
|
||||
|
||||
// Unsorted coins means invalid
|
||||
err := sendMsg5.ValidateBasic()
|
||||
require.Equal(t, sdk.CodeInvalidCoins, err.Code(), err.ABCILog())
|
||||
|
||||
// Sort coins, should be valid
|
||||
sendMsg5.Inputs[0].Coins.Sort()
|
||||
sendMsg5.Outputs[0].Coins.Sort()
|
||||
err = sendMsg5.ValidateBasic()
|
||||
require.Nil(t, err)
|
||||
|
||||
// Ensure we can send
|
||||
SignCheckDeliver(t, bapp, sendMsg5, []int64{0}, true, priv1)
|
||||
}
|
||||
|
||||
func TestGenesis(t *testing.T) {
|
||||
logger, db := loggerAndDB()
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
bapp := NewBasecoinApp(logger, db)
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
|
@ -211,318 +75,3 @@ func TestGenesis(t *testing.T) {
|
|||
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestMsgChangePubKey(t *testing.T) {
|
||||
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins, err := sdk.ParseCoins("77foocoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesis(bapp, baseAcc)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "67foocoin")
|
||||
CheckBalance(t, bapp, addr2, "10foocoin")
|
||||
|
||||
changePubKeyMsg := auth.MsgChangeKey{
|
||||
Address: addr1,
|
||||
NewPubKey: priv2.PubKey(),
|
||||
}
|
||||
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
acc := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
// send a MsgChangePubKey
|
||||
SignCheckDeliver(t, bapp, changePubKeyMsg, []int64{1}, true, priv1)
|
||||
acc = bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
assert.True(t, priv2.PubKey().Equals(acc.GetPubKey()))
|
||||
|
||||
// signing a SendMsg with the old privKey should be an auth error
|
||||
tx := genTx(sendMsg1, []int64{2}, priv1)
|
||||
res := bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the new correct priv key should work
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{2}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "57foocoin")
|
||||
CheckBalance(t, bapp, addr2, "20foocoin")
|
||||
}
|
||||
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins, err := sdk.ParseCoins("77foocoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesis(bapp, baseAcc)
|
||||
require.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "67foocoin")
|
||||
CheckBalance(t, bapp, addr2, "10foocoin")
|
||||
|
||||
// Delivering again should cause replay error
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, false, priv1)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
tx := genTx(sendMsg1, []int64{0}, priv1)
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res := bapp.Deliver(tx)
|
||||
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
|
||||
}
|
||||
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
acc2 := auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesis(bapp, acc1, acc2)
|
||||
require.Nil(t, err)
|
||||
|
||||
// Simulate a Block
|
||||
SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "32foocoin")
|
||||
CheckBalance(t, bapp, addr2, "47foocoin")
|
||||
CheckBalance(t, bapp, addr3, "5foocoin")
|
||||
}
|
||||
|
||||
func TestSengMsgMultipleInOut(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
acc2 := auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
acc4 := auth.BaseAccount{
|
||||
Address: addr4,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
err = setGenesis(bapp, acc1, acc2, acc4)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// CheckDeliver
|
||||
SignCheckDeliver(t, bapp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "32foocoin")
|
||||
CheckBalance(t, bapp, addr4, "32foocoin")
|
||||
CheckBalance(t, bapp, addr2, "52foocoin")
|
||||
CheckBalance(t, bapp, addr3, "10foocoin")
|
||||
}
|
||||
|
||||
func TestMsgSendDependent(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
require.Nil(t, err)
|
||||
|
||||
acc1 := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: genCoins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesis(bapp, acc1)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = setGenesis(bapp, acc1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// CheckDeliver
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "32foocoin")
|
||||
CheckBalance(t, bapp, addr2, "10foocoin")
|
||||
|
||||
// Simulate a Block
|
||||
SignCheckDeliver(t, bapp, sendMsg4, []int64{0}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "42foocoin")
|
||||
}
|
||||
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: nil,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
// Construct genesis state
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
|
||||
// Initialize the chain (nil)
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
}
|
||||
|
||||
func TestIBCMsgs(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
sourceChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
err := setGenesis(bapp, baseAcc)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
packet := ibc.IBCPacket{
|
||||
SrcAddr: addr1,
|
||||
DestAddr: addr1,
|
||||
Coins: coins,
|
||||
SrcChain: sourceChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
|
||||
transferMsg := ibc.IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
receiveMsg := ibc.IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: addr1,
|
||||
Sequence: 0,
|
||||
}
|
||||
|
||||
SignCheckDeliver(t, bapp, transferMsg, []int64{0}, true, priv1)
|
||||
CheckBalance(t, bapp, addr1, "")
|
||||
SignCheckDeliver(t, bapp, transferMsg, []int64{1}, false, priv1)
|
||||
SignCheckDeliver(t, bapp, receiveMsg, []int64{2}, true, priv1)
|
||||
CheckBalance(t, bapp, addr1, "10foocoin")
|
||||
SignCheckDeliver(t, bapp, receiveMsg, []int64{3}, false, priv1)
|
||||
}
|
||||
|
||||
func genTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
for i, p := range priv {
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: p.Sign(auth.StdSignBytes(chainID, seq, fee, msg)),
|
||||
Sequence: seq[i],
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msg, fee, sigs)
|
||||
|
||||
}
|
||||
|
||||
func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
|
||||
|
||||
// Sign the tx
|
||||
tx := genTx(msg, seq, priv...)
|
||||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
}
|
||||
|
||||
func CheckBalance(t *testing.T, bapp *BasecoinApp, addr sdk.Address, balExpected string) {
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr)
|
||||
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -27,7 +28,7 @@ func main() {
|
|||
|
||||
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
|
||||
server.ConstructAppCreator(newApp, "basecoin"),
|
||||
server.ConstructAppExporter(exportAppState, "basecoin"))
|
||||
server.ConstructAppExporter(exportAppStateAndTMValidators, "basecoin"))
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
||||
|
@ -39,7 +40,7 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
|||
return app.NewBasecoinApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
return bapp.ExportAppStateJSON()
|
||||
return bapp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -154,7 +155,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
|
|||
}
|
||||
|
||||
// Custom logic for state export
|
||||
func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
||||
func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// iterate to get the accounts
|
||||
|
@ -174,5 +175,9 @@ func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
|
|||
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper),
|
||||
CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper),
|
||||
}
|
||||
return wire.MarshalJSONIndent(app.cdc, genState)
|
||||
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return appState, validators, nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
@ -10,12 +9,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
@ -23,101 +18,9 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
// Construct some global addrs and txs for tests.
|
||||
var (
|
||||
chainID = "" // TODO
|
||||
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
1000000,
|
||||
}
|
||||
|
||||
sendMsg = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
quizMsg1 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "icecold",
|
||||
}
|
||||
|
||||
quizMsg2 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg1 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "icecold",
|
||||
}
|
||||
|
||||
setTrendMsg2 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg3 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "warmandkind",
|
||||
}
|
||||
)
|
||||
|
||||
func loggerAndDB() (log.Logger, dbm.DB) {
|
||||
func TestGenesis(t *testing.T) {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
return logger, db
|
||||
}
|
||||
|
||||
func newDemocoinApp() *DemocoinApp {
|
||||
logger, db := loggerAndDB()
|
||||
return NewDemocoinApp(logger, db)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func TestMsgs(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
msgs := []struct {
|
||||
msg sdk.Msg
|
||||
}{
|
||||
{sendMsg},
|
||||
{quizMsg1},
|
||||
{setTrendMsg1},
|
||||
}
|
||||
|
||||
sequences := []int64{0}
|
||||
for i, m := range msgs {
|
||||
sig := priv1.Sign(auth.StdSignBytes(chainID, sequences, fee, m.msg))
|
||||
tx := auth.NewStdTx(m.msg, fee, []auth.StdSignature{{
|
||||
PubKey: priv1.PubKey(),
|
||||
Signature: sig,
|
||||
}})
|
||||
|
||||
// just marshal/unmarshal!
|
||||
txBytes, err := bapp.cdc.MarshalBinary(tx)
|
||||
require.NoError(t, err, "i: %v", i)
|
||||
|
||||
// Run a Check
|
||||
cres := bapp.CheckTx(txBytes)
|
||||
assert.Equal(t, sdk.CodeUnknownAddress,
|
||||
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
dres := bapp.DeliverTx(txBytes)
|
||||
assert.Equal(t, sdk.CodeUnknownAddress,
|
||||
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesis(t *testing.T) {
|
||||
logger, db := loggerAndDB()
|
||||
bapp := NewDemocoinApp(logger, db)
|
||||
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
|
@ -156,272 +59,3 @@ func TestGenesis(t *testing.T) {
|
|||
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins, err := sdk.ParseCoins("77foocoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
// Construct genesis state
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
"cool": map[string]string{
|
||||
"trend": "ice-cold",
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
|
||||
// Initialize the chain
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Sign the tx
|
||||
sequences := []int64{0}
|
||||
sig := priv1.Sign(auth.StdSignBytes(chainID, sequences, fee, sendMsg))
|
||||
tx := auth.NewStdTx(sendMsg, fee, []auth.StdSignature{{
|
||||
PubKey: priv1.PubKey(),
|
||||
Signature: sig,
|
||||
}})
|
||||
|
||||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Check balances
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
|
||||
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
|
||||
|
||||
// Delivering again should cause replay error
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
sequences = []int64{1}
|
||||
sig = priv1.Sign(auth.StdSignBytes(chainID, sequences, fee, tx.Msg))
|
||||
tx.Signatures[0].Signature = sig
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
func TestMsgMine(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: nil,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
// Construct genesis state
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
"cool": map[string]string{
|
||||
"trend": "ice-cold",
|
||||
},
|
||||
"pow": map[string]uint64{
|
||||
"difficulty": 1,
|
||||
"count": 0,
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
|
||||
// Initialize the chain (nil)
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := pow.GenerateMsgMine(addr1, 1, 2)
|
||||
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
|
||||
CheckBalance(t, bapp, "1pow")
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := pow.GenerateMsgMine(addr1, 2, 3)
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
// Mine again - should be invalid
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, false)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
|
||||
}
|
||||
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: nil,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
// Construct genesis state
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
"cool": map[string]string{
|
||||
"trend": "ice-cold",
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
|
||||
// Initialize the chain (nil)
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Set the trend, submit a really cool quiz and check for reward
|
||||
SignCheckDeliver(t, bapp, setTrendMsg1, 0, true)
|
||||
SignCheckDeliver(t, bapp, quizMsg1, 1, true)
|
||||
CheckBalance(t, bapp, "69icecold")
|
||||
SignCheckDeliver(t, bapp, quizMsg2, 2, false) // result without reward
|
||||
CheckBalance(t, bapp, "69icecold")
|
||||
SignCheckDeliver(t, bapp, quizMsg1, 3, true)
|
||||
CheckBalance(t, bapp, "138icecold")
|
||||
SignCheckDeliver(t, bapp, setTrendMsg2, 4, true) // reset the trend
|
||||
SignCheckDeliver(t, bapp, quizMsg1, 5, false) // the same answer will nolonger do!
|
||||
CheckBalance(t, bapp, "138icecold")
|
||||
SignCheckDeliver(t, bapp, quizMsg2, 6, true) // earlier answer now relavent again
|
||||
CheckBalance(t, bapp, "69badvibesonly,138icecold")
|
||||
SignCheckDeliver(t, bapp, setTrendMsg3, 7, false) // expect to fail to set the trend to something which is not cool
|
||||
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
sourceChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
vals := []abci.Validator{}
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
"cool": map[string]string{
|
||||
"trend": "ice-cold",
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
packet := ibc.IBCPacket{
|
||||
SrcAddr: addr1,
|
||||
DestAddr: addr1,
|
||||
Coins: coins,
|
||||
SrcChain: sourceChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
|
||||
transferMsg := ibc.IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
receiveMsg := ibc.IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: addr1,
|
||||
Sequence: 0,
|
||||
}
|
||||
|
||||
SignCheckDeliver(t, bapp, transferMsg, 0, true)
|
||||
CheckBalance(t, bapp, "")
|
||||
SignCheckDeliver(t, bapp, transferMsg, 1, false)
|
||||
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
|
||||
CheckBalance(t, bapp, "10foocoin")
|
||||
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
|
||||
}
|
||||
|
||||
// TODO describe the use of this function
|
||||
func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, expPass bool) {
|
||||
|
||||
// Sign the tx
|
||||
tx := auth.NewStdTx(msg, fee, []auth.StdSignature{{
|
||||
PubKey: priv1.PubKey(),
|
||||
Signature: priv1.Sign(auth.StdSignBytes(chainID, []int64{seq}, fee, msg)),
|
||||
Sequence: seq,
|
||||
}})
|
||||
|
||||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
}
|
||||
|
||||
func CheckBalance(t *testing.T, bapp *DemocoinApp, balExpected string) {
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
@ -46,9 +47,9 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
|||
return app.NewDemocoinApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
dapp := app.NewDemocoinApp(logger, db)
|
||||
return dapp.ExportAppStateJSON()
|
||||
return dapp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -63,7 +64,7 @@ func main() {
|
|||
|
||||
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,
|
||||
server.ConstructAppCreator(newApp, "democoin"),
|
||||
server.ConstructAppExporter(exportAppState, "democoin"))
|
||||
server.ConstructAppExporter(exportAppStateAndTMValidators, "democoin"))
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.democoind")
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package cool
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
|
||||
quizMsg1 = MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "icecold",
|
||||
}
|
||||
|
||||
quizMsg2 = MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg1 = MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "icecold",
|
||||
}
|
||||
|
||||
setTrendMsg2 = MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg3 = MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "warmandkind",
|
||||
}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *mock.App {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyCool := sdk.NewKVStoreKey("cool")
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
keeper := NewKeeper(keyCool, coinKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
||||
mapp.Router().AddRoute("cool", NewHandler(keeper))
|
||||
|
||||
mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold"))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{keyCool})
|
||||
return mapp
|
||||
}
|
||||
|
||||
// overwrite the mock init chainer
|
||||
func getInitChainer(mapp *mock.App, keeper Keeper, newTrend string) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
keeper.setTrend(ctx, newTrend)
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
// Construct genesis state
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: nil,
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
|
||||
// Initialize the chain (nil)
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Set the trend, submit a really cool quiz and check for reward
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, setTrendMsg1, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, quizMsg1, []int64{0}, []int64{1}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", 69}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, quizMsg2, []int64{0}, []int64{2}, false, priv1) // result without reward
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", 69}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, quizMsg1, []int64{0}, []int64{3}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", 138}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, setTrendMsg2, []int64{0}, []int64{4}, true, priv1) // reset the trend
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, quizMsg1, []int64{0}, []int64{5}, false, priv1) // the same answer will nolonger do!
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", 138}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, quizMsg2, []int64{0}, []int64{6}, true, priv1) // earlier answer now relavent again
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", 69}, {"icecold", 138}})
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, setTrendMsg3, []int64{0}, []int64{7}, false, priv1) // expect to fail to set the trend to something which is not cool
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package pow
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *mock.App {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyPOW := sdk.NewKVStoreKey("pow")
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
config := Config{"pow", 1}
|
||||
keeper := NewKeeper(keyPOW, config, coinKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
||||
mapp.Router().AddRoute("pow", keeper.Handler)
|
||||
|
||||
mapp.SetInitChainer(getInitChainer(mapp, keeper))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{keyPOW})
|
||||
return mapp
|
||||
}
|
||||
|
||||
// overwrite the mock init chainer
|
||||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
|
||||
genesis := Genesis{
|
||||
Difficulty: 1,
|
||||
Count: 0,
|
||||
}
|
||||
InitGenesis(ctx, keeper, genesis)
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMine(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
// Construct genesis state
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: nil,
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
|
||||
// Initialize the chain (nil)
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := GenerateMsgMine(addr1, 1, 2)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, mineMsg1, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", 1}})
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := GenerateMsgMine(addr1, 2, 3)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, mineMsg2, []int64{0}, []int64{1}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", 2}})
|
||||
// Mine again - should be invalid
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, mineMsg2, []int64{0}, []int64{1}, false, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", 2}})
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
@ -13,8 +14,8 @@ import (
|
|||
// and other flags (?) to start
|
||||
type AppCreator func(string, log.Logger) (abci.Application, error)
|
||||
|
||||
// AppExporter dumps all app state to JSON-serializable structure
|
||||
type AppExporter func(home string, log log.Logger) (json.RawMessage, error)
|
||||
// AppExporter dumps all app state to JSON-serializable structure and returns the current validator set
|
||||
type AppExporter func(home string, log log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
|
||||
// ConstructAppCreator returns an application generation function
|
||||
func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name string) AppCreator {
|
||||
|
@ -30,12 +31,12 @@ func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name s
|
|||
}
|
||||
|
||||
// ConstructAppExporter returns an application export function
|
||||
func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, error), name string) AppExporter {
|
||||
return func(rootDir string, logger log.Logger) (json.RawMessage, error) {
|
||||
func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error), name string) AppExporter {
|
||||
return func(rootDir string, logger log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
db, err := dbm.NewGoLevelDB(name, dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return appFn(logger, db)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Co
|
|||
Short: "Export state to JSON",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
home := viper.GetString("home")
|
||||
appState, err := appExporter(home, ctx.Logger)
|
||||
appState, validators, err := appExporter(home, ctx.Logger)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error exporting state: %v\n", err)
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Co
|
|||
return err
|
||||
}
|
||||
doc.AppStateJSON = appState
|
||||
doc.Validators = validators
|
||||
encoded, err := wire.MarshalJSONIndent(cdc, doc)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/mock"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/mock"
|
||||
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
|
|
|
@ -26,21 +26,57 @@ func Bech32ifyAcc(addr Address) (string, error) {
|
|||
return bech32.ConvertAndEncode(Bech32PrefixAccAddr, addr.Bytes())
|
||||
}
|
||||
|
||||
// MustBech32ifyAcc panics on bech32-encoding failure
|
||||
func MustBech32ifyAcc(addr Address) string {
|
||||
enc, err := Bech32ifyAcc(addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string
|
||||
func Bech32ifyAccPub(pub crypto.PubKey) (string, error) {
|
||||
return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes())
|
||||
}
|
||||
|
||||
// MustBech32ifyAccPub panics on bech32-encoding failure
|
||||
func MustBech32ifyAccPub(pub crypto.PubKey) string {
|
||||
enc, err := Bech32ifyAccPub(pub)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// Bech32ifyVal returns the bech32 encoded string for a validator address
|
||||
func bech32ifyVal(addr Address) (string, error) {
|
||||
func Bech32ifyVal(addr Address) (string, error) {
|
||||
return bech32.ConvertAndEncode(Bech32PrefixValAddr, addr.Bytes())
|
||||
}
|
||||
|
||||
// MustBech32ifyVal panics on bech32-encoding failure
|
||||
func MustBech32ifyVal(addr Address) string {
|
||||
enc, err := Bech32ifyVal(addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// Bech32ifyValPub returns the bech32 encoded string for a validator pubkey
|
||||
func Bech32ifyValPub(pub crypto.PubKey) (string, error) {
|
||||
return bech32.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes())
|
||||
}
|
||||
|
||||
// MustBech32ifyValPub pancis on bech32-encoding failure
|
||||
func MustBech32ifyValPub(pub crypto.PubKey) string {
|
||||
enc, err := Bech32ifyValPub(pub)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// create an Address from a string
|
||||
func GetAccAddressHex(address string) (addr Address, err error) {
|
||||
if len(address) == 0 {
|
||||
|
@ -55,13 +91,28 @@ func GetAccAddressHex(address string) (addr Address, err error) {
|
|||
|
||||
// create an Address from a string
|
||||
func GetAccAddressBech32(address string) (addr Address, err error) {
|
||||
bz, err := getFromBech32(address, Bech32PrefixAccAddr)
|
||||
bz, err := GetFromBech32(address, Bech32PrefixAccAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Address(bz), nil
|
||||
}
|
||||
|
||||
// create a Pubkey from a string
|
||||
func GetAccPubKeyBech32(address string) (pk crypto.PubKey, err error) {
|
||||
bz, err := GetFromBech32(address, Bech32PrefixAccPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk, err = crypto.PubKeyFromBytes(bz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
// create an Address from a hex string
|
||||
func GetValAddressHex(address string) (addr Address, err error) {
|
||||
if len(address) == 0 {
|
||||
|
@ -76,16 +127,16 @@ func GetValAddressHex(address string) (addr Address, err error) {
|
|||
|
||||
// create an Address from a bech32 string
|
||||
func GetValAddressBech32(address string) (addr Address, err error) {
|
||||
bz, err := getFromBech32(address, Bech32PrefixValAddr)
|
||||
bz, err := GetFromBech32(address, Bech32PrefixValAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Address(bz), nil
|
||||
}
|
||||
|
||||
//Decode a validator publickey into a public key
|
||||
// decode a validator public key into a PubKey
|
||||
func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
|
||||
bz, err := getFromBech32(pubkey, Bech32PrefixValPub)
|
||||
bz, err := GetFromBech32(pubkey, Bech32PrefixValPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -98,7 +149,8 @@ func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
|
|||
return pk, nil
|
||||
}
|
||||
|
||||
func getFromBech32(bech32str, prefix string) ([]byte, error) {
|
||||
// decode a bytestring from a bech32-encoded string
|
||||
func GetFromBech32(bech32str, prefix string) ([]byte, error) {
|
||||
if len(bech32str) == 0 {
|
||||
return nil, errors.New("must provide non-empty string")
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ func BondStatusToString(b BondStatus) string {
|
|||
|
||||
// validator for a delegated proof of stake system
|
||||
type Validator interface {
|
||||
GetMoniker() string // moniker of the validator
|
||||
GetStatus() BondStatus // status of the validator
|
||||
GetOwner() Address // owner address to receive/return validators coins
|
||||
GetPubKey() crypto.PubKey // validation pubkey
|
||||
|
|
|
@ -5,4 +5,5 @@ import wire "github.com/cosmos/cosmos-sdk/wire"
|
|||
// Register the sdk message type
|
||||
func RegisterWire(cdc *wire.Codec) {
|
||||
cdc.RegisterInterface((*Msg)(nil), nil)
|
||||
cdc.RegisterInterface((*Tx)(nil), nil)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ type Account interface {
|
|||
GetPubKey() crypto.PubKey // can return nil.
|
||||
SetPubKey(crypto.PubKey) error
|
||||
|
||||
GetAccountNumber() int64
|
||||
SetAccountNumber(int64) error
|
||||
|
||||
GetSequence() int64
|
||||
SetSequence(int64) error
|
||||
|
||||
|
@ -39,6 +42,7 @@ type BaseAccount struct {
|
|||
Address sdk.Address `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
PubKey crypto.PubKey `json:"public_key"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
}
|
||||
|
||||
|
@ -84,6 +88,17 @@ func (acc *BaseAccount) SetCoins(coins sdk.Coins) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Implements Account
|
||||
func (acc *BaseAccount) GetAccountNumber() int64 {
|
||||
return acc.AccountNumber
|
||||
}
|
||||
|
||||
// Implements Account
|
||||
func (acc *BaseAccount) SetAccountNumber(accNumber int64) error {
|
||||
acc.AccountNumber = accNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements sdk.Account.
|
||||
func (acc *BaseAccount) GetSequence() int64 {
|
||||
return acc.Sequence
|
||||
|
|
|
@ -14,7 +14,7 @@ const (
|
|||
)
|
||||
|
||||
// NewAnteHandler returns an AnteHandler that checks
|
||||
// and increments sequence numbers, checks signatures,
|
||||
// and increments sequence numbers, checks signatures & account numbers,
|
||||
// and deducts fees from the first signer.
|
||||
func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
||||
|
||||
|
@ -46,11 +46,15 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
|||
true
|
||||
}
|
||||
|
||||
// Get the sign bytes (requires all sequence numbers and the fee)
|
||||
// Get the sign bytes (requires all account & sequence numbers and the fee)
|
||||
sequences := make([]int64, len(signerAddrs))
|
||||
for i := 0; i < len(signerAddrs); i++ {
|
||||
sequences[i] = sigs[i].Sequence
|
||||
}
|
||||
accNums := make([]int64, len(signerAddrs))
|
||||
for i := 0; i < len(signerAddrs); i++ {
|
||||
accNums[i] = sigs[i].AccountNumber
|
||||
}
|
||||
fee := stdTx.Fee
|
||||
chainID := ctx.ChainID()
|
||||
// XXX: major hack; need to get ChainID
|
||||
|
@ -58,7 +62,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
|||
if chainID == "" {
|
||||
chainID = viper.GetString("chain-id")
|
||||
}
|
||||
signBytes := StdSignBytes(ctx.ChainID(), sequences, fee, msg)
|
||||
signBytes := StdSignBytes(ctx.ChainID(), accNums, sequences, fee, msg)
|
||||
|
||||
// Check sig and nonce and collect signer accounts.
|
||||
var signerAccs = make([]Account, len(signerAddrs))
|
||||
|
@ -117,6 +121,13 @@ func processSig(
|
|||
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
|
||||
}
|
||||
|
||||
// Check account number.
|
||||
accnum := acc.GetAccountNumber()
|
||||
if accnum != sig.AccountNumber {
|
||||
return nil, sdk.ErrInvalidSequence(
|
||||
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
|
||||
}
|
||||
|
||||
// Check and increment sequence number.
|
||||
seq := acc.GetSequence()
|
||||
if seq != sig.Sequence {
|
||||
|
|
|
@ -52,15 +52,15 @@ func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context,
|
|||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code)
|
||||
}
|
||||
|
||||
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee StdFee) sdk.Tx {
|
||||
signBytes := StdSignBytes(ctx.ChainID(), seqs, fee, msg)
|
||||
return newTestTxWithSignBytes(msg, privs, seqs, fee, signBytes)
|
||||
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee) sdk.Tx {
|
||||
signBytes := StdSignBytes(ctx.ChainID(), accNums, seqs, fee, msg)
|
||||
return newTestTxWithSignBytes(msg, privs, accNums, seqs, fee, signBytes)
|
||||
}
|
||||
|
||||
func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee StdFee, signBytes []byte) sdk.Tx {
|
||||
func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee, signBytes []byte) sdk.Tx {
|
||||
sigs := make([]StdSignature, len(privs))
|
||||
for i, priv := range privs {
|
||||
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), Sequence: seqs[i]}
|
||||
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), AccountNumber: accNums[i], Sequence: seqs[i]}
|
||||
}
|
||||
tx := NewStdTx(msg, fee, sigs)
|
||||
return tx
|
||||
|
@ -87,18 +87,18 @@ func TestAnteHandlerSigErrors(t *testing.T) {
|
|||
fee := newStdFee()
|
||||
|
||||
// test no signatures
|
||||
privs, seqs := []crypto.PrivKey{}, []int64{}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accNums, seqs := []crypto.PrivKey{}, []int64{}, []int64{}
|
||||
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
|
||||
|
||||
// test num sigs dont match GetSigners
|
||||
privs, seqs = []crypto.PrivKey{priv1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
|
||||
|
||||
// test an unrecognized account
|
||||
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0}
|
||||
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress)
|
||||
|
||||
// save the first account, but second is still unrecognized
|
||||
|
@ -108,6 +108,61 @@ func TestAnteHandlerSigErrors(t *testing.T) {
|
|||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress)
|
||||
}
|
||||
|
||||
// Test logic around account number checking with one signer and many signers.
|
||||
func TestAnteHandlerAccountNumbers(t *testing.T) {
|
||||
// setup
|
||||
ms, capKey, capKey2 := setupMultiStore()
|
||||
cdc := wire.NewCodec()
|
||||
RegisterBaseAccount(cdc)
|
||||
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
|
||||
feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
|
||||
anteHandler := NewAnteHandler(mapper, feeCollector)
|
||||
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
|
||||
|
||||
// keys and addresses
|
||||
priv1, addr1 := privAndAddr()
|
||||
priv2, addr2 := privAndAddr()
|
||||
|
||||
// set the accounts
|
||||
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
|
||||
acc1.SetCoins(newCoins())
|
||||
mapper.SetAccount(ctx, acc1)
|
||||
acc2 := mapper.NewAccountWithAddress(ctx, addr2)
|
||||
acc2.SetCoins(newCoins())
|
||||
mapper.SetAccount(ctx, acc2)
|
||||
|
||||
// msg and signatures
|
||||
var tx sdk.Tx
|
||||
msg := newTestMsg(addr1)
|
||||
fee := newStdFee()
|
||||
|
||||
// test good tx from one signer
|
||||
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// new tx from wrong account number
|
||||
seqs = []int64{1}
|
||||
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
|
||||
|
||||
// from correct account number
|
||||
seqs = []int64{1}
|
||||
tx = newTestTx(ctx, msg, privs, []int64{0}, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// new tx with another signer and incorrect account numbers
|
||||
msg = newTestMsg(addr1, addr2)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
|
||||
|
||||
// correct account numbers
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
}
|
||||
|
||||
// Test logic around sequence checking with one signer and many signers.
|
||||
func TestAnteHandlerSequences(t *testing.T) {
|
||||
// setup
|
||||
|
@ -137,8 +192,8 @@ func TestAnteHandlerSequences(t *testing.T) {
|
|||
fee := newStdFee()
|
||||
|
||||
// test good tx from one signer
|
||||
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// test sending it again fails (replay protection)
|
||||
|
@ -146,13 +201,13 @@ func TestAnteHandlerSequences(t *testing.T) {
|
|||
|
||||
// fix sequence, should pass
|
||||
seqs = []int64{1}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// new tx with another signer and correct sequences
|
||||
msg = newTestMsg(addr1, addr2)
|
||||
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{2, 0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// replay fails
|
||||
|
@ -160,18 +215,18 @@ func TestAnteHandlerSequences(t *testing.T) {
|
|||
|
||||
// tx from just second signer with incorrect sequence fails
|
||||
msg = newTestMsg(addr2)
|
||||
privs, seqs = []crypto.PrivKey{priv2}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
|
||||
|
||||
// fix the sequence and it passes
|
||||
tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{1}, fee)
|
||||
tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{1}, []int64{1}, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
// another tx from both of them that passes
|
||||
msg = newTestMsg(addr1, addr2)
|
||||
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{3, 2}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{3, 2}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
}
|
||||
|
||||
|
@ -196,13 +251,13 @@ func TestAnteHandlerFees(t *testing.T) {
|
|||
// msg and signatures
|
||||
var tx sdk.Tx
|
||||
msg := newTestMsg(addr1)
|
||||
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
||||
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
fee := NewStdFee(100,
|
||||
sdk.Coin{"atom", 150},
|
||||
)
|
||||
|
||||
// signer does not have enough funds to pay the fee
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds)
|
||||
|
||||
acc1.SetCoins(sdk.Coins{{"atom", 149}})
|
||||
|
@ -249,8 +304,8 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
|
|||
fee3.Amount[0].Amount += 100
|
||||
|
||||
// test good tx and signBytes
|
||||
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
chainID := ctx.ChainID()
|
||||
|
@ -259,37 +314,39 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
|
|||
|
||||
cases := []struct {
|
||||
chainID string
|
||||
accnums []int64
|
||||
seqs []int64
|
||||
fee StdFee
|
||||
msg sdk.Msg
|
||||
code sdk.CodeType
|
||||
}{
|
||||
{chainID2, []int64{1}, fee, msg, codeUnauth}, // test wrong chain_id
|
||||
{chainID, []int64{2}, fee, msg, codeUnauth}, // test wrong seqs
|
||||
{chainID, []int64{1, 2}, fee, msg, codeUnauth}, // test wrong seqs
|
||||
{chainID, []int64{1}, fee, newTestMsg(addr2), codeUnauth}, // test wrong msg
|
||||
{chainID, []int64{1}, fee2, newTestMsg(addr2), codeUnauth}, // test wrong fee
|
||||
{chainID, []int64{1}, fee3, newTestMsg(addr2), codeUnauth}, // test wrong fee
|
||||
{chainID2, []int64{0}, []int64{1}, fee, msg, codeUnauth}, // test wrong chain_id
|
||||
{chainID, []int64{0}, []int64{2}, fee, msg, codeUnauth}, // test wrong seqs
|
||||
{chainID, []int64{0}, []int64{1, 2}, fee, msg, codeUnauth}, // test wrong seqs
|
||||
{chainID, []int64{1}, []int64{1}, fee, msg, codeUnauth}, // test wrong accnum
|
||||
{chainID, []int64{0}, []int64{1}, fee, newTestMsg(addr2), codeUnauth}, // test wrong msg
|
||||
{chainID, []int64{0}, []int64{1}, fee2, msg, codeUnauth}, // test wrong fee
|
||||
{chainID, []int64{0}, []int64{1}, fee3, msg, codeUnauth}, // test wrong fee
|
||||
}
|
||||
|
||||
privs, seqs = []crypto.PrivKey{priv1}, []int64{1}
|
||||
for _, cs := range cases {
|
||||
tx := newTestTxWithSignBytes(
|
||||
msg, privs, seqs, fee,
|
||||
StdSignBytes(cs.chainID, cs.seqs, cs.fee, cs.msg),
|
||||
msg, privs, accnums, seqs, fee,
|
||||
StdSignBytes(cs.chainID, cs.accnums, cs.seqs, cs.fee, cs.msg),
|
||||
)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, cs.code)
|
||||
}
|
||||
|
||||
// test wrong signer if public key exist
|
||||
privs, seqs = []crypto.PrivKey{priv2}, []int64{1}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{0}, []int64{1}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
|
||||
|
||||
// test wrong signer if public doesn't exist
|
||||
msg = newTestMsg(addr2)
|
||||
privs, seqs = []crypto.PrivKey{priv1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
privs, accnums, seqs = []crypto.PrivKey{priv1}, []int64{1}, []int64{0}
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
||||
|
||||
}
|
||||
|
@ -320,9 +377,9 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
|
|||
|
||||
// test good tx and set public key
|
||||
msg := newTestMsg(addr1)
|
||||
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
||||
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
|
||||
fee := newStdFee()
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
|
||||
checkValidTx(t, anteHandler, ctx, tx)
|
||||
|
||||
acc1 = mapper.GetAccount(ctx, addr1)
|
||||
|
@ -330,7 +387,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
|
|||
|
||||
// test public key not found
|
||||
msg = newTestMsg(addr2)
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
|
||||
sigs := tx.(StdTx).GetSignatures()
|
||||
sigs[0].PubKey = nil
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
||||
|
@ -339,7 +396,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
|
|||
assert.Nil(t, acc2.GetPubKey())
|
||||
|
||||
// test invalid signature and public key
|
||||
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
||||
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
|
||||
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
||||
|
||||
acc2 = mapper.GetAccount(ctx, addr2)
|
||||
|
|
|
@ -47,7 +47,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
|
|||
|
||||
// perform query
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.Query(key, storeName)
|
||||
res, err := ctx.Query(auth.AddressStoreKey(key), storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
|
@ -26,17 +25,16 @@ func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, sto
|
|||
func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder, ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
addr := vars["address"]
|
||||
bech32addr := vars["address"]
|
||||
|
||||
bz, err := hex.DecodeString(addr)
|
||||
addr, err := sdk.GetAccAddressBech32(bech32addr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
key := sdk.Address(bz)
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
res, err := ctx.Query(auth.AddressStoreKey(addr), storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var globalAccountNumberKey = []byte("globalAccountNumber")
|
||||
|
||||
// This AccountMapper encodes/decodes accounts using the
|
||||
// go-amino (binary) encoding/decoding library.
|
||||
type AccountMapper struct {
|
||||
|
@ -38,13 +40,25 @@ func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto Account) AccountM
|
|||
func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) Account {
|
||||
acc := am.clonePrototype()
|
||||
acc.SetAddress(addr)
|
||||
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
|
||||
return acc
|
||||
}
|
||||
|
||||
// New Account
|
||||
func (am AccountMapper) NewAccount(ctx sdk.Context, acc Account) Account {
|
||||
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
|
||||
return acc
|
||||
}
|
||||
|
||||
// Turn an address to key used to get it from the account store
|
||||
func AddressStoreKey(addr sdk.Address) []byte {
|
||||
return append([]byte("account:"), addr.Bytes()...)
|
||||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) Account {
|
||||
store := ctx.KVStore(am.key)
|
||||
bz := store.Get(addr)
|
||||
bz := store.Get(AddressStoreKey(addr))
|
||||
if bz == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -57,13 +71,13 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) {
|
|||
addr := acc.GetAddress()
|
||||
store := ctx.KVStore(am.key)
|
||||
bz := am.encodeAccount(acc)
|
||||
store.Set(addr, bz)
|
||||
store.Set(AddressStoreKey(addr), bz)
|
||||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
|
||||
store := ctx.KVStore(am.key)
|
||||
iter := store.Iterator(nil, nil)
|
||||
iter := sdk.KVStorePrefixIterator(store, []byte("account:"))
|
||||
for {
|
||||
if !iter.Valid() {
|
||||
return
|
||||
|
@ -116,6 +130,26 @@ func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequen
|
|||
return nil
|
||||
}
|
||||
|
||||
// Returns and increments the global account number counter
|
||||
func (am AccountMapper) GetNextAccountNumber(ctx sdk.Context) int64 {
|
||||
var accNumber int64
|
||||
store := ctx.KVStore(am.key)
|
||||
bz := store.Get(globalAccountNumberKey)
|
||||
if bz == nil {
|
||||
accNumber = 0
|
||||
} else {
|
||||
err := am.cdc.UnmarshalBinary(bz, &accNumber)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
bz = am.cdc.MustMarshalBinary(accNumber + 1)
|
||||
store.Set(globalAccountNumberKey, bz)
|
||||
|
||||
return accNumber
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// misc.
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
)
|
||||
|
||||
// Extended ABCI application
|
||||
type App struct {
|
||||
*bam.BaseApp
|
||||
Cdc *wire.Codec // public since the codec is passed into the module anyways.
|
||||
KeyMain *sdk.KVStoreKey
|
||||
KeyAccount *sdk.KVStoreKey
|
||||
|
||||
// TODO: Abstract this out from not needing to be auth specifically
|
||||
AccountMapper auth.AccountMapper
|
||||
FeeCollectionKeeper auth.FeeCollectionKeeper
|
||||
|
||||
GenesisAccounts []auth.Account
|
||||
}
|
||||
|
||||
// partially construct a new app on the memstore for module and genesis testing
|
||||
func NewApp() *App {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
// create the cdc with some standard codecs
|
||||
cdc := wire.NewCodec()
|
||||
sdk.RegisterWire(cdc)
|
||||
wire.RegisterCrypto(cdc)
|
||||
auth.RegisterWire(cdc)
|
||||
|
||||
// create your application object
|
||||
app := &App{
|
||||
BaseApp: bam.NewBaseApp("mock", cdc, logger, db),
|
||||
Cdc: cdc,
|
||||
KeyMain: sdk.NewKVStoreKey("main"),
|
||||
KeyAccount: sdk.NewKVStoreKey("acc"),
|
||||
}
|
||||
|
||||
// define the accountMapper
|
||||
app.AccountMapper = auth.NewAccountMapper(
|
||||
app.Cdc,
|
||||
app.KeyAccount, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
|
||||
// initialize the app, the chainers and blockers can be overwritten before calling complete setup
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.AccountMapper, app.FeeCollectionKeeper))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// complete the application setup after the routes have been registered
|
||||
func (app *App) CompleteSetup(t *testing.T, newKeys []*sdk.KVStoreKey) {
|
||||
|
||||
newKeys = append(newKeys, app.KeyMain)
|
||||
newKeys = append(newKeys, app.KeyAccount)
|
||||
app.MountStoresIAVL(newKeys...)
|
||||
err := app.LoadLatestVersion(app.KeyMain)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// custom logic for initialization
|
||||
func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain {
|
||||
|
||||
// load the accounts
|
||||
for _, genacc := range app.GenesisAccounts {
|
||||
acc := app.AccountMapper.NewAccountWithAddress(ctx, genacc.GetAddress())
|
||||
acc.SetCoins(genacc.GetCoins())
|
||||
app.AccountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// test auth module messages
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
sendMsg1 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *App {
|
||||
mapp := NewApp()
|
||||
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
mapp.Router().AddRoute("bank", bank.NewHandler(coinKeeper))
|
||||
mapp.Router().AddRoute("auth", auth.NewHandler(mapp.AccountMapper))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{})
|
||||
return mapp
|
||||
}
|
||||
|
||||
func TestMsgChangePubKey(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins := sdk.Coins{{"foocoin", 77}}
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
|
||||
// Construct genesis state
|
||||
SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1.(*auth.BaseAccount))
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 67}})
|
||||
CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 10}})
|
||||
|
||||
changePubKeyMsg := auth.MsgChangeKey{
|
||||
Address: addr1,
|
||||
NewPubKey: priv2.PubKey(),
|
||||
}
|
||||
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
ctxDeliver := mapp.BaseApp.NewContext(false, abci.Header{})
|
||||
acc2 := mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
// send a MsgChangePubKey
|
||||
SignCheckDeliver(t, mapp.BaseApp, changePubKeyMsg, []int64{0}, []int64{1}, true, priv1)
|
||||
acc2 = mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
assert.True(t, priv2.PubKey().Equals(acc2.GetPubKey()))
|
||||
|
||||
// signing a SendMsg with the old privKey should be an auth error
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
tx := GenTx(sendMsg1, []int64{0}, []int64{2}, priv1)
|
||||
res := mapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the new correct priv key should work
|
||||
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{2}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 57}})
|
||||
CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 20}})
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
var chainID = "" // TODO
|
||||
|
||||
// set the mock app genesis
|
||||
func SetGenesis(app *App, accs []auth.Account) {
|
||||
|
||||
// pass the accounts in via the application (lazy) instead of through RequestInitChain
|
||||
app.GenesisAccounts = accs
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
// check an account balance
|
||||
func CheckBalance(t *testing.T, app *App, addr sdk.Address, exp sdk.Coins) {
|
||||
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
|
||||
res := app.AccountMapper.GetAccount(ctxCheck, addr)
|
||||
assert.Equal(t, exp, res.GetCoins())
|
||||
}
|
||||
|
||||
// generate a signed transaction
|
||||
func GenTx(msg sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
|
||||
|
||||
// make the transaction free
|
||||
fee := auth.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
100000,
|
||||
}
|
||||
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
for i, p := range priv {
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: p.Sign(auth.StdSignBytes(chainID, accnums, seq, fee, msg)),
|
||||
AccountNumber: accnums[i],
|
||||
Sequence: seq[i],
|
||||
}
|
||||
}
|
||||
return auth.NewStdTx(msg, fee, sigs)
|
||||
}
|
||||
|
||||
// check a transaction result
|
||||
func SignCheck(t *testing.T, app *baseapp.BaseApp, msg sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.Result {
|
||||
tx := GenTx(msg, accnums, seq, priv...)
|
||||
res := app.Check(tx)
|
||||
return res
|
||||
}
|
||||
|
||||
// simulate a block
|
||||
func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msg sdk.Msg, accnums []int64, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
|
||||
|
||||
// Sign the tx
|
||||
tx := GenTx(msg, accnums, seq, priv...)
|
||||
|
||||
// Run a Check
|
||||
res := app.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = app.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
|
||||
app.Commit()
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
@ -84,6 +86,7 @@ func (fee StdFee) Bytes() []byte {
|
|||
// inchain replay and enforce tx ordering per account).
|
||||
type StdSignDoc struct {
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumbers []int64 `json:"account_numbers"`
|
||||
Sequences []int64 `json:"sequences"`
|
||||
FeeBytes []byte `json:"fee_bytes"`
|
||||
MsgBytes []byte `json:"msg_bytes"`
|
||||
|
@ -92,9 +95,10 @@ type StdSignDoc struct {
|
|||
|
||||
// StdSignBytes returns the bytes to sign for a transaction.
|
||||
// TODO: change the API to just take a chainID and StdTx ?
|
||||
func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []byte {
|
||||
bz, err := msgCdc.MarshalJSON(StdSignDoc{
|
||||
func StdSignBytes(chainID string, accnums []int64, sequences []int64, fee StdFee, msg sdk.Msg) []byte {
|
||||
bz, err := json.Marshal(StdSignDoc{
|
||||
ChainID: chainID,
|
||||
AccountNumbers: accnums,
|
||||
Sequences: sequences,
|
||||
FeeBytes: fee.Bytes(),
|
||||
MsgBytes: msg.GetSignBytes(),
|
||||
|
@ -110,6 +114,7 @@ func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []
|
|||
// it is signed. For use in the CLI.
|
||||
type StdSignMsg struct {
|
||||
ChainID string
|
||||
AccountNumbers []int64
|
||||
Sequences []int64
|
||||
Fee StdFee
|
||||
Msg sdk.Msg
|
||||
|
@ -118,12 +123,13 @@ type StdSignMsg struct {
|
|||
|
||||
// get message bytes
|
||||
func (msg StdSignMsg) Bytes() []byte {
|
||||
return StdSignBytes(msg.ChainID, msg.Sequences, msg.Fee, msg.Msg)
|
||||
return StdSignBytes(msg.ChainID, msg.AccountNumbers, msg.Sequences, msg.Fee, msg.Msg)
|
||||
}
|
||||
|
||||
// Standard Signature
|
||||
type StdSignature struct {
|
||||
crypto.PubKey `json:"pub_key"` // optional
|
||||
crypto.Signature `json:"signature"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ func RegisterWire(cdc *wire.Codec) {
|
|||
cdc.RegisterInterface((*Account)(nil), nil)
|
||||
cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil)
|
||||
cdc.RegisterConcrete(MsgChangeKey{}, "auth/ChangeKey", nil)
|
||||
cdc.RegisterConcrete(StdTx{}, "auth/StdTx", nil)
|
||||
}
|
||||
|
||||
var msgCdc = wire.NewCodec()
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
package bank
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// test bank module in a mock application
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
halfCoins = sdk.Coins{{"foocoin", 5}}
|
||||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
|
||||
freeFee = auth.StdFee{ // no fees for a buncha gas
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
100000,
|
||||
}
|
||||
|
||||
sendMsg1 = MsgSend{
|
||||
Inputs: []Input{NewInput(addr1, coins)},
|
||||
Outputs: []Output{NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
sendMsg2 = MsgSend{
|
||||
Inputs: []Input{NewInput(addr1, coins)},
|
||||
Outputs: []Output{
|
||||
NewOutput(addr2, halfCoins),
|
||||
NewOutput(addr3, halfCoins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg3 = MsgSend{
|
||||
Inputs: []Input{
|
||||
NewInput(addr1, coins),
|
||||
NewInput(addr4, coins),
|
||||
},
|
||||
Outputs: []Output{
|
||||
NewOutput(addr2, coins),
|
||||
NewOutput(addr3, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg4 = MsgSend{
|
||||
Inputs: []Input{
|
||||
NewInput(addr2, coins),
|
||||
},
|
||||
Outputs: []Output{
|
||||
NewOutput(addr1, coins),
|
||||
},
|
||||
}
|
||||
|
||||
sendMsg5 = MsgSend{
|
||||
Inputs: []Input{
|
||||
NewInput(addr1, manyCoins),
|
||||
},
|
||||
Outputs: []Output{
|
||||
NewOutput(addr2, manyCoins),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *mock.App {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
coinKeeper := NewKeeper(mapp.AccountMapper)
|
||||
mapp.Router().AddRoute("bank", NewHandler(coinKeeper))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{})
|
||||
return mapp
|
||||
}
|
||||
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
// Add an account at genesis
|
||||
acc := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{{"foocoin", 67}},
|
||||
}
|
||||
accs := []auth.Account{acc}
|
||||
|
||||
// Construct genesis state
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
require.NotNil(t, res1)
|
||||
assert.Equal(t, acc, res1.(*auth.BaseAccount))
|
||||
|
||||
// Run a CheckDeliver
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 57}})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 10}})
|
||||
|
||||
// Delivering again should cause replay error
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, false, priv1)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
tx := mock.GenTx(sendMsg1, []int64{0}, []int64{0}, priv1)
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res := mapp.Deliver(tx)
|
||||
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{1}, true, priv1)
|
||||
}
|
||||
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
accs := []auth.Account{acc1, acc2}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// Simulate a Block
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg2, []int64{0}, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 47}})
|
||||
mock.CheckBalance(t, mapp, addr3, sdk.Coins{{"foocoin", 5}})
|
||||
}
|
||||
|
||||
func TestSengMsgMultipleInOut(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
acc4 := &auth.BaseAccount{
|
||||
Address: addr4,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
accs := []auth.Account{acc1, acc2, acc4}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// CheckDeliver
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg3, []int64{0, 2}, []int64{0, 0}, true, priv1, priv4)
|
||||
|
||||
// Check balances
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
|
||||
mock.CheckBalance(t, mapp, addr4, sdk.Coins{{"foocoin", 32}})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 52}})
|
||||
mock.CheckBalance(t, mapp, addr3, sdk.Coins{{"foocoin", 10}})
|
||||
}
|
||||
|
||||
func TestMsgSendDependent(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{{"foocoin", 42}},
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// CheckDeliver
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 10}})
|
||||
|
||||
// Simulate a Block
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg4, []int64{1}, []int64{0}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 42}})
|
||||
}
|
|
@ -27,7 +27,9 @@ type sendBody struct {
|
|||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
}
|
||||
|
||||
var msgCdc = wire.NewCodec()
|
||||
|
@ -41,7 +43,14 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// collect data
|
||||
vars := mux.Vars(r)
|
||||
address := vars["address"]
|
||||
bech32addr := vars["address"]
|
||||
|
||||
address, err := sdk.GetAccAddressBech32(bech32addr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
var m sendBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
|
@ -64,7 +73,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
|
|||
return
|
||||
}
|
||||
|
||||
to, err := sdk.GetAccAddressHex(address)
|
||||
to, err := sdk.GetAccAddressHex(address.String())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -79,7 +88,11 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
|
|||
return
|
||||
}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign
|
||||
ctx = ctx.WithAccountNumber(m.AccountNumber)
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
|
||||
if err != nil {
|
||||
|
|
|
@ -151,7 +151,7 @@ func subtractCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt
|
|||
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
||||
}
|
||||
err := setCoins(ctx, am, addr, newCoins)
|
||||
tags := sdk.NewTags("sender", addr.Bytes())
|
||||
tags := sdk.NewTags("sender", []byte(addr.String()))
|
||||
return newCoins, tags, err
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ func addCoins(ctx sdk.Context, am auth.AccountMapper, addr sdk.Address, amt sdk.
|
|||
return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt))
|
||||
}
|
||||
err := setCoins(ctx, am, addr, newCoins)
|
||||
tags := sdk.NewTags("recipient", addr.Bytes())
|
||||
tags := sdk.NewTags("recipient", []byte(addr.String()))
|
||||
return newCoins, tags, err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package bank
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -53,7 +55,20 @@ func (msg MsgSend) ValidateBasic() sdk.Error {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgSend) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
|
||||
var inputs, outputs []json.RawMessage
|
||||
for _, input := range msg.Inputs {
|
||||
inputs = append(inputs, input.GetSignBytes())
|
||||
}
|
||||
for _, output := range msg.Outputs {
|
||||
outputs = append(outputs, output.GetSignBytes())
|
||||
}
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Inputs []json.RawMessage `json:"inputs"`
|
||||
Outputs []json.RawMessage `json:"outputs"`
|
||||
}{
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -78,6 +93,8 @@ type MsgIssue struct {
|
|||
Outputs []Output `json:"outputs"`
|
||||
}
|
||||
|
||||
var _ sdk.Msg = MsgIssue{}
|
||||
|
||||
// NewMsgIssue - construct arbitrary multi-in, multi-out send msg.
|
||||
func NewMsgIssue(banker sdk.Address, out []Output) MsgIssue {
|
||||
return MsgIssue{Banker: banker, Outputs: out}
|
||||
|
@ -102,7 +119,17 @@ func (msg MsgIssue) ValidateBasic() sdk.Error {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgIssue) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
|
||||
var outputs []json.RawMessage
|
||||
for _, output := range msg.Outputs {
|
||||
outputs = append(outputs, output.GetSignBytes())
|
||||
}
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Banker string `json:"banker"`
|
||||
Outputs []json.RawMessage `json:"outputs"`
|
||||
}{
|
||||
Banker: sdk.MustBech32ifyAcc(msg.Banker),
|
||||
Outputs: outputs,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -123,6 +150,21 @@ type Input struct {
|
|||
Coins sdk.Coins `json:"coins"`
|
||||
}
|
||||
|
||||
// Return bytes to sign for Input
|
||||
func (in Input) GetSignBytes() []byte {
|
||||
bin, err := msgCdc.MarshalJSON(struct {
|
||||
Address string `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
}{
|
||||
Address: sdk.MustBech32ifyAcc(in.Address),
|
||||
Coins: in.Coins,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bin
|
||||
}
|
||||
|
||||
// ValidateBasic - validate transaction input
|
||||
func (in Input) ValidateBasic() sdk.Error {
|
||||
if len(in.Address) == 0 {
|
||||
|
@ -155,6 +197,21 @@ type Output struct {
|
|||
Coins sdk.Coins `json:"coins"`
|
||||
}
|
||||
|
||||
// Return bytes to sign for Output
|
||||
func (out Output) GetSignBytes() []byte {
|
||||
bin, err := msgCdc.MarshalJSON(struct {
|
||||
Address string `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
}{
|
||||
Address: sdk.MustBech32ifyAcc(out.Address),
|
||||
Coins: out.Coins,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bin
|
||||
}
|
||||
|
||||
// ValidateBasic - validate transaction output
|
||||
func (out Output) ValidateBasic() sdk.Error {
|
||||
if len(out.Address) == 0 {
|
||||
|
|
|
@ -187,12 +187,7 @@ func TestMsgSendGetSignBytes(t *testing.T) {
|
|||
}
|
||||
res := msg.GetSignBytes()
|
||||
|
||||
unmarshaledMsg := &MsgSend{}
|
||||
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
|
||||
assert.Equal(t, &msg, unmarshaledMsg)
|
||||
|
||||
// TODO bad results
|
||||
expected := `{"type":"EAFDE32A2C87F8","value":{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}]}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}}`
|
||||
expected := `{"inputs":[{"address":"cosmosaccaddr1d9h8qat5e4ehc5","coins":[{"denom":"atom","amount":10}]}],"outputs":[{"address":"cosmosaccaddr1da6hgur4wse3jx32","coins":[{"denom":"atom","amount":10}]}]}`
|
||||
assert.Equal(t, expected, string(res))
|
||||
}
|
||||
|
||||
|
@ -262,12 +257,7 @@ func TestMsgIssueGetSignBytes(t *testing.T) {
|
|||
}
|
||||
res := msg.GetSignBytes()
|
||||
|
||||
unmarshaledMsg := &MsgIssue{}
|
||||
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
|
||||
assert.Equal(t, &msg, unmarshaledMsg)
|
||||
|
||||
// TODO bad results
|
||||
expected := `{"type":"72E617C06ABAD0","value":{"banker":"696E707574","outputs":[{"address":"6C6F616E2D66726F6D2D62616E6B","coins":[{"denom":"atom","amount":10}]}]}}`
|
||||
expected := `{"banker":"cosmosaccaddr1d9h8qat5e4ehc5","outputs":[{"address":"cosmosaccaddr1d3hkzm3dveex7mfdvfsku6cwsauqd","coins":[{"denom":"atom","amount":10}]}]}`
|
||||
assert.Equal(t, expected, string(res))
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) *mock.App {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyIBC := sdk.NewKVStoreKey("ibc")
|
||||
ibcMapper := NewMapper(mapp.Cdc, keyIBC, mapp.RegisterCodespace(DefaultCodespace))
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, coinKeeper))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{keyIBC})
|
||||
return mapp
|
||||
}
|
||||
|
||||
func TestIBCMsgs(t *testing.T) {
|
||||
mapp := getMockApp(t)
|
||||
|
||||
sourceChain := "source-chain"
|
||||
destChain := "dest-chain"
|
||||
|
||||
priv1 := crypto.GenPrivKeyEd25519()
|
||||
addr1 := priv1.PubKey().Address()
|
||||
coins := sdk.Coins{{"foocoin", 10}}
|
||||
var emptyCoins sdk.Coins
|
||||
|
||||
acc := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
accs := []auth.Account{acc}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := mapp.AccountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc, res1)
|
||||
|
||||
packet := IBCPacket{
|
||||
SrcAddr: addr1,
|
||||
DestAddr: addr1,
|
||||
Coins: coins,
|
||||
SrcChain: sourceChain,
|
||||
DestChain: destChain,
|
||||
}
|
||||
|
||||
transferMsg := IBCTransferMsg{
|
||||
IBCPacket: packet,
|
||||
}
|
||||
|
||||
receiveMsg := IBCReceiveMsg{
|
||||
IBCPacket: packet,
|
||||
Relayer: addr1,
|
||||
Sequence: 0,
|
||||
}
|
||||
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{0},[]int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, emptyCoins)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{0}, []int64{1}, false, priv1)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{0}, []int64{2}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, coins)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{0}, []int64{3}, false, priv1)
|
||||
}
|
|
@ -25,7 +25,9 @@ type transferBody struct {
|
|||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
SrcChainID string `json:"src_chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Gas int64 `json:"gas"`
|
||||
}
|
||||
|
||||
// TransferRequestHandler - http request handler to transfer coins to a address
|
||||
|
@ -35,7 +37,14 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
|
|||
// collect data
|
||||
vars := mux.Vars(r)
|
||||
destChainID := vars["destchain"]
|
||||
address := vars["address"]
|
||||
bech32addr := vars["address"]
|
||||
|
||||
address, err := sdk.GetAccAddressBech32(bech32addr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
var m transferBody
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
|
@ -58,7 +67,7 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
|
|||
return
|
||||
}
|
||||
|
||||
bz, err := hex.DecodeString(address)
|
||||
bz, err := hex.DecodeString(address.String())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -70,7 +79,11 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
|
|||
packet := ibc.NewIBCPacket(info.PubKey.Address(), to, m.Amount, m.SrcChainID, destChainID)
|
||||
msg := ibc.IBCTransferMsg{packet}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign
|
||||
ctx = ctx.WithAccountNumber(m.AccountNumber)
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
package ibc
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
msgCdc *wire.Codec
|
||||
)
|
||||
|
||||
func init() {
|
||||
msgCdc = wire.NewCodec()
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// IBCPacket
|
||||
|
||||
|
@ -32,12 +41,33 @@ func NewIBCPacket(srcAddr sdk.Address, destAddr sdk.Address, coins sdk.Coins,
|
|||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (p IBCPacket) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
SrcAddr string
|
||||
DestAddr string
|
||||
Coins sdk.Coins
|
||||
SrcChain string
|
||||
DestChain string
|
||||
}{
|
||||
SrcAddr: sdk.MustBech32ifyAcc(p.SrcAddr),
|
||||
DestAddr: sdk.MustBech32ifyAcc(p.DestAddr),
|
||||
Coins: p.Coins,
|
||||
SrcChain: p.SrcChain,
|
||||
DestChain: p.DestChain,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// validator the ibc packey
|
||||
func (ibcp IBCPacket) ValidateBasic() sdk.Error {
|
||||
if ibcp.SrcChain == ibcp.DestChain {
|
||||
func (p IBCPacket) ValidateBasic() sdk.Error {
|
||||
if p.SrcChain == p.DestChain {
|
||||
return ErrIdenticalChains(DefaultCodespace).Trace("")
|
||||
}
|
||||
if !ibcp.Coins.IsValid() {
|
||||
if !p.Coins.IsValid() {
|
||||
return sdk.ErrInvalidCoins("")
|
||||
}
|
||||
return nil
|
||||
|
@ -60,12 +90,7 @@ func (msg IBCTransferMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.
|
|||
|
||||
// get the sign bytes for ibc transfer message
|
||||
func (msg IBCTransferMsg) GetSignBytes() []byte {
|
||||
cdc := wire.NewCodec()
|
||||
bz, err := cdc.MarshalBinary(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
return msg.IBCPacket.GetSignBytes()
|
||||
}
|
||||
|
||||
// validate ibc transfer message
|
||||
|
@ -94,10 +119,17 @@ func (msg IBCReceiveMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.R
|
|||
|
||||
// get the sign bytes for ibc receive message
|
||||
func (msg IBCReceiveMsg) GetSignBytes() []byte {
|
||||
cdc := wire.NewCodec()
|
||||
bz, err := cdc.MarshalBinary(msg)
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
IBCPacket json.RawMessage
|
||||
Relayer string
|
||||
Sequence int64
|
||||
}{
|
||||
IBCPacket: json.RawMessage(msg.IBCPacket.GetSignBytes()),
|
||||
Relayer: sdk.MustBech32ifyAcc(msg.Relayer),
|
||||
Sequence: msg.Sequence,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
keySlashing := sdk.NewKVStoreKey("slashing")
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(stake.DefaultCodespace))
|
||||
keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
||||
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
|
||||
mapp.Router().AddRoute("slashing", NewHandler(keeper))
|
||||
|
||||
mapp.SetEndBlocker(getEndBlocker(stakeKeeper))
|
||||
mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper))
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{keyStake, keySlashing})
|
||||
|
||||
return mapp, stakeKeeper, keeper
|
||||
}
|
||||
|
||||
// stake endblocker
|
||||
func getEndBlocker(keeper stake.Keeper) sdk.EndBlocker {
|
||||
return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
validatorUpdates := stake.EndBlocker(ctx, keeper)
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: validatorUpdates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite the mock init chainer
|
||||
func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
stake.InitGenesis(ctx, keeper, stake.DefaultGenesisState())
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
func checkValidator(t *testing.T, mapp *mock.App, keeper stake.Keeper,
|
||||
addr sdk.Address, expFound bool) stake.Validator {
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
validator, found := keeper.GetValidator(ctxCheck, addr1)
|
||||
assert.Equal(t, expFound, found)
|
||||
return validator
|
||||
}
|
||||
|
||||
func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper,
|
||||
addr sdk.Address, expFound bool) ValidatorSigningInfo {
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
signingInfo, found := keeper.getValidatorSigningInfo(ctxCheck, addr)
|
||||
assert.Equal(t, expFound, found)
|
||||
return signingInfo
|
||||
}
|
||||
|
||||
func TestSlashingMsgs(t *testing.T) {
|
||||
mapp, stakeKeeper, keeper := getMockApp(t)
|
||||
|
||||
genCoin := sdk.Coin{"steak", 42}
|
||||
bondCoin := sdk.Coin{"steak", 10}
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{genCoin},
|
||||
}
|
||||
accs := []auth.Account{acc1}
|
||||
mock.SetGenesis(mapp, accs)
|
||||
description := stake.NewDescription("foo_moniker", "", "", "")
|
||||
createValidatorMsg := stake.NewMsgCreateValidator(
|
||||
addr1, priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, createValidatorMsg, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
validator := checkValidator(t, mapp, stakeKeeper, addr1, true)
|
||||
require.Equal(t, addr1, validator.Owner)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
unrevokeMsg := MsgUnrevoke{ValidatorAddr: validator.PubKey.Address()}
|
||||
|
||||
// no signing info yet
|
||||
checkValidatorSigningInfo(t, mapp, keeper, addr1, false)
|
||||
|
||||
// unrevoke should fail with unknown validator
|
||||
res := mock.SignCheck(t, mapp.BaseApp, unrevokeMsg, []int64{0}, []int64{1}, priv1)
|
||||
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeInvalidValidator), res.Code)
|
||||
}
|
|
@ -55,8 +55,12 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
|
|||
address := pubkey.Address()
|
||||
|
||||
// Local index, so counts blocks validator *should* have signed
|
||||
// Will use the 0-value default signing info if not present
|
||||
signInfo, _ := k.getValidatorSigningInfo(ctx, address)
|
||||
// Will use the 0-value default signing info if not present, except for start height
|
||||
signInfo, found := k.getValidatorSigningInfo(ctx, address)
|
||||
if !found {
|
||||
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
|
||||
signInfo = NewValidatorSigningInfo(height, 0, 0, 0)
|
||||
}
|
||||
index := signInfo.IndexOffset % SignedBlocksWindow
|
||||
signInfo.IndexOffset++
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// Test that a validator is slashed correctly
|
||||
// when we discover evidence of equivocation
|
||||
func TestHandleDoubleSign(t *testing.T) {
|
||||
|
||||
// initial setup
|
||||
|
@ -32,6 +34,8 @@ func TestHandleDoubleSign(t *testing.T) {
|
|||
require.Equal(t, sdk.NewRat(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
|
||||
}
|
||||
|
||||
// Test a validator through uptime, downtime, revocation,
|
||||
// unrevocation, starting height reset, and revocation again
|
||||
func TestHandleAbsentValidator(t *testing.T) {
|
||||
|
||||
// initial setup
|
||||
|
@ -129,3 +133,39 @@ func TestHandleAbsentValidator(t *testing.T) {
|
|||
validator, _ = sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||
}
|
||||
|
||||
// Test a new validator entering the validator set
|
||||
// Ensure that SigningInfo.StartHeight is set correctly
|
||||
// and that they are not immediately revoked
|
||||
func TestHandleNewValidator(t *testing.T) {
|
||||
// initial setup
|
||||
ctx, ck, sk, keeper := createTestInput(t)
|
||||
addr, val, amt := addrs[0], pks[0], int64(100)
|
||||
sh := stake.NewHandler(sk)
|
||||
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
|
||||
require.True(t, got.IsOK())
|
||||
stake.EndBlocker(ctx, sk)
|
||||
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins - amt}})
|
||||
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
|
||||
|
||||
// 1000 first blocks not a validator
|
||||
ctx = ctx.WithBlockHeight(1001)
|
||||
|
||||
// Now a validator, for two blocks
|
||||
keeper.handleValidatorSignature(ctx, val, true)
|
||||
ctx = ctx.WithBlockHeight(1002)
|
||||
keeper.handleValidatorSignature(ctx, val, false)
|
||||
|
||||
info, found := keeper.getValidatorSigningInfo(ctx, val.Address())
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(1001), info.StartHeight)
|
||||
require.Equal(t, int64(2), info.IndexOffset)
|
||||
require.Equal(t, int64(1), info.SignedBlocksCounter)
|
||||
require.Equal(t, int64(0), info.JailedUntil)
|
||||
|
||||
// validator should be bonded still, should not have been revoked or slashed
|
||||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||
pool := sk.GetPool(ctx)
|
||||
require.Equal(t, int64(100), pool.BondedTokens)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,11 @@ func (msg MsgUnrevoke) GetSigners() []sdk.Address { return []sdk.Address{msg.Val
|
|||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgUnrevoke) GetSignBytes() []byte {
|
||||
b, err := cdc.MarshalJSON(msg)
|
||||
b, err := cdc.MarshalJSON(struct {
|
||||
ValidatorAddr string `json:"address"`
|
||||
}{
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package slashing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestMsgUnrevokeGetSignBytes(t *testing.T) {
|
||||
addr := sdk.Address("abcd")
|
||||
msg := NewMsgUnrevoke(addr)
|
||||
bytes := msg.GetSignBytes()
|
||||
assert.Equal(t, string(bytes), `{"address":"cosmosvaladdr1v93xxeqamr0mv"}`)
|
||||
}
|
|
@ -47,6 +47,16 @@ func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.Address
|
|||
store.Set(GetValidatorSigningBitArrayKey(address, index), bz)
|
||||
}
|
||||
|
||||
// Construct a new `ValidatorSigningInfo` struct
|
||||
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil int64, signedBlocksCounter int64) ValidatorSigningInfo {
|
||||
return ValidatorSigningInfo{
|
||||
StartHeight: startHeight,
|
||||
IndexOffset: indexOffset,
|
||||
JailedUntil: jailedUntil,
|
||||
SignedBlocksCounter: signedBlocksCounter,
|
||||
}
|
||||
}
|
||||
|
||||
// Signing info for a validator
|
||||
type ValidatorSigningInfo struct {
|
||||
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
priv1 = crypto.GenPrivKeyEd25519()
|
||||
addr1 = priv1.PubKey().Address()
|
||||
priv2 = crypto.GenPrivKeyEd25519()
|
||||
addr2 = priv2.PubKey().Address()
|
||||
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
priv4 = crypto.GenPrivKeyEd25519()
|
||||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
fee = auth.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
100000,
|
||||
}
|
||||
)
|
||||
|
||||
// initialize the mock application for this module
|
||||
func getMockApp(t *testing.T) (*mock.App, Keeper) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
RegisterWire(mapp.Cdc)
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||
keeper := NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
||||
mapp.Router().AddRoute("stake", NewHandler(keeper))
|
||||
|
||||
mapp.SetEndBlocker(getEndBlocker(keeper))
|
||||
mapp.SetInitChainer(getInitChainer(mapp, keeper))
|
||||
|
||||
mapp.CompleteSetup(t, []*sdk.KVStoreKey{keyStake})
|
||||
return mapp, keeper
|
||||
}
|
||||
|
||||
// stake endblocker
|
||||
func getEndBlocker(keeper Keeper) sdk.EndBlocker {
|
||||
return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
validatorUpdates := EndBlocker(ctx, keeper)
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: validatorUpdates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite the mock init chainer
|
||||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
mapp.InitChainer(ctx, req)
|
||||
InitGenesis(ctx, keeper, DefaultGenesisState())
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
//__________________________________________________________________________________________
|
||||
|
||||
func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper,
|
||||
addr sdk.Address, expFound bool) Validator {
|
||||
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
validator, found := keeper.GetValidator(ctxCheck, addr1)
|
||||
assert.Equal(t, expFound, found)
|
||||
return validator
|
||||
}
|
||||
|
||||
func checkDelegation(t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr,
|
||||
validatorAddr sdk.Address, expFound bool, expShares sdk.Rat) {
|
||||
|
||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
||||
delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr)
|
||||
if expFound {
|
||||
assert.True(t, found)
|
||||
assert.True(sdk.RatEq(t, expShares, delegation.Shares))
|
||||
return
|
||||
}
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
func TestStakeMsgs(t *testing.T) {
|
||||
mapp, keeper := getMockApp(t)
|
||||
|
||||
genCoin := sdk.Coin{"steak", 42}
|
||||
bondCoin := sdk.Coin{"steak", 10}
|
||||
|
||||
acc1 := &auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: sdk.Coins{genCoin},
|
||||
}
|
||||
acc2 := &auth.BaseAccount{
|
||||
Address: addr2,
|
||||
Coins: sdk.Coins{genCoin},
|
||||
}
|
||||
accs := []auth.Account{acc1, acc2}
|
||||
|
||||
mock.SetGenesis(mapp, accs)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin})
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
|
||||
////////////////////
|
||||
// Create Validator
|
||||
|
||||
description := NewDescription("foo_moniker", "", "", "")
|
||||
createValidatorMsg := NewMsgCreateValidator(
|
||||
addr1, priv1.PubKey(), bondCoin, description,
|
||||
)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, createValidatorMsg, []int64{0}, []int64{0}, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
mapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
validator := checkValidator(t, mapp, keeper, addr1, true)
|
||||
require.Equal(t, addr1, validator.Owner)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
|
||||
// check the bond that should have been created as well
|
||||
checkDelegation(t, mapp, keeper, addr1, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Edit Validator
|
||||
|
||||
description = NewDescription("bar_moniker", "", "", "")
|
||||
editValidatorMsg := NewMsgEditValidator(addr1, description)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, editValidatorMsg, []int64{0}, []int64{1}, true, priv1)
|
||||
validator = checkValidator(t, mapp, keeper, addr1, true)
|
||||
require.Equal(t, description, validator.Description)
|
||||
|
||||
////////////////////
|
||||
// Delegate
|
||||
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
delegateMsg := NewMsgDelegate(addr2, addr1, bondCoin)
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, delegateMsg, []int64{1}, []int64{0}, true, priv2)
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
|
||||
|
||||
////////////////////
|
||||
// Unbond
|
||||
|
||||
unbondMsg := NewMsgUnbond(addr2, addr1, "MAX")
|
||||
mock.SignCheckDeliver(t, mapp.BaseApp, unbondMsg, []int64{1}, []int64{1}, true, priv2)
|
||||
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
|
||||
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
|
@ -30,24 +29,22 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
|||
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
delegator := vars["delegator"]
|
||||
validator := vars["validator"]
|
||||
bech32delegator := vars["delegator"]
|
||||
bech32validator := vars["validator"]
|
||||
|
||||
bz, err := hex.DecodeString(delegator)
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
delegatorAddr := sdk.Address(bz)
|
||||
|
||||
bz, err = hex.DecodeString(validator)
|
||||
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
validatorAddr := sdk.Address(bz)
|
||||
|
||||
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
||||
|
||||
|
@ -83,6 +80,62 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire
|
|||
}
|
||||
}
|
||||
|
||||
// TODO move exist next to validator struct for maintainability
|
||||
type StakeValidatorOutput struct {
|
||||
Owner string `json:"owner"` // in bech32
|
||||
PubKey string `json:"pub_key"` // in bech32
|
||||
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
||||
|
||||
PoolShares stake.PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
|
||||
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
|
||||
Description stake.Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
||||
}
|
||||
|
||||
func bech32StakeValidatorOutput(validator stake.Validator) (StakeValidatorOutput, error) {
|
||||
bechOwner, err := sdk.Bech32ifyVal(validator.Owner)
|
||||
if err != nil {
|
||||
return StakeValidatorOutput{}, err
|
||||
}
|
||||
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
|
||||
if err != nil {
|
||||
return StakeValidatorOutput{}, err
|
||||
}
|
||||
|
||||
return StakeValidatorOutput{
|
||||
Owner: bechOwner,
|
||||
PubKey: bechValPubkey,
|
||||
Revoked: validator.Revoked,
|
||||
|
||||
PoolShares: validator.PoolShares,
|
||||
DelegatorShares: validator.DelegatorShares,
|
||||
|
||||
Description: validator.Description,
|
||||
BondHeight: validator.BondHeight,
|
||||
BondIntraTxCounter: validator.BondIntraTxCounter,
|
||||
ProposerRewardPool: validator.ProposerRewardPool,
|
||||
|
||||
Commission: validator.Commission,
|
||||
CommissionMax: validator.CommissionMax,
|
||||
CommissionChangeRate: validator.CommissionChangeRate,
|
||||
CommissionChangeToday: validator.CommissionChangeToday,
|
||||
|
||||
PrevBondedShares: validator.PrevBondedShares,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO bech32
|
||||
// http request handler to query list of validators
|
||||
func validatorsHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -100,16 +153,20 @@ func validatorsHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Co
|
|||
}
|
||||
|
||||
// parse out the validators
|
||||
validators := make([]stake.Validator, len(kvs))
|
||||
validators := make([]StakeValidatorOutput, len(kvs))
|
||||
for i, kv := range kvs {
|
||||
var validator stake.Validator
|
||||
var bech32Validator StakeValidatorOutput
|
||||
err = cdc.UnmarshalBinary(kv.Value, &validator)
|
||||
if err == nil {
|
||||
bech32Validator, err = bech32StakeValidatorOutput(validator)
|
||||
}
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validators[i] = validator
|
||||
validators[i] = bech32Validator
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(validators)
|
||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
|
@ -23,13 +24,26 @@ func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, k
|
|||
).Methods("POST")
|
||||
}
|
||||
|
||||
type msgDelegateInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
type msgUnbondInput struct {
|
||||
DelegatorAddr string `json:"delegator_addr"` // in bech32
|
||||
ValidatorAddr string `json:"validator_addr"` // in bech32
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
type editDelegationsBody struct {
|
||||
LocalAccountName string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
ChainID string `json:"chain_id"`
|
||||
AccountNumber int64 `json:"account_number"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Delegate []stake.MsgDelegate `json:"delegate"`
|
||||
Unbond []stake.MsgUnbond `json:"unbond"`
|
||||
Gas int64 `json:"gas"`
|
||||
Delegate []msgDelegateInput `json:"delegate"`
|
||||
Unbond []msgUnbondInput `json:"unbond"`
|
||||
}
|
||||
|
||||
func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
|
||||
|
@ -59,28 +73,64 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
|
|||
messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond))
|
||||
i := 0
|
||||
for _, msg := range m.Delegate {
|
||||
if !bytes.Equal(info.Address(), msg.DelegatorAddr) {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = msg
|
||||
messages[i] = stake.MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: msg.Bond,
|
||||
}
|
||||
i++
|
||||
}
|
||||
for _, msg := range m.Unbond {
|
||||
if !bytes.Equal(info.Address(), msg.DelegatorAddr) {
|
||||
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(info.Address(), delegatorAddr) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Must use own delegator address"))
|
||||
return
|
||||
}
|
||||
messages[i] = msg
|
||||
messages[i] = stake.MsgUnbond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: msg.Shares,
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// add gas to context
|
||||
ctx = ctx.WithGas(m.Gas)
|
||||
|
||||
// sign messages
|
||||
signedTxs := make([][]byte, len(messages[:]))
|
||||
for i, msg := range messages {
|
||||
// increment sequence for each message
|
||||
ctx = ctx.WithAccountNumber(m.AccountNumber)
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
m.Sequence++
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package stake
|
||||
|
||||
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
import (
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
|
@ -63,3 +67,16 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
|||
bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteValidators - output current validator set
|
||||
func WriteValidators(ctx sdk.Context, k Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
vals = append(vals, tmtypes.GenesisValidator{
|
||||
PubKey: validator.GetPubKey(),
|
||||
Power: validator.GetPower().Evaluate(),
|
||||
Name: validator.GetMoniker(),
|
||||
})
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -33,6 +33,78 @@ func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) Msg
|
|||
|
||||
//______________________________________________________________________
|
||||
|
||||
func TestValidatorByPowerIndex(t *testing.T) {
|
||||
validatorAddr, validatorAddr3 := addrs[0], addrs[1]
|
||||
|
||||
initBond := int64(1000000)
|
||||
initBondStr := "1000"
|
||||
ctx, _, keeper := createTestInput(t, false, initBond)
|
||||
|
||||
// create validator
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// verify the self-delegation exists
|
||||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
require.Equal(t, initBond, gotBond,
|
||||
"initBond: %v\ngotBond: %v\nbond: %v\n",
|
||||
initBond, gotBond, bond)
|
||||
|
||||
// verify that the by power index exists
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool := keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// create a second validator keep it bonded
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, pks[2], int64(1000000))
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// slash and revoke the first validator
|
||||
keeper.Slash(ctx, pks[0], 0, sdk.NewRat(1, 2))
|
||||
keeper.Revoke(ctx, pks[0])
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded
|
||||
require.Equal(t, int64(500000), validator.PoolShares.Amount.Evaluate()) // ensure is unbonded
|
||||
|
||||
// the old power record should have been deleted as the power changed
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// but the new power record should have been created
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
pool = keeper.GetPool(ctx)
|
||||
power2 := GetValidatorsByPowerKey(validator, pool)
|
||||
require.True(t, keeper.validatorByPowerIndexExists(ctx, power2))
|
||||
|
||||
// inflate a bunch
|
||||
for i := 0; i < 20000; i++ {
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
}
|
||||
|
||||
// now the new record power index should be the same as the original record
|
||||
power3 := GetValidatorsByPowerKey(validator, pool)
|
||||
assert.Equal(t, power2, power3)
|
||||
|
||||
// unbond self-delegation
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "MAX")
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\ninitBondStr: %v\n", got, msgUnbond, initBondStr)
|
||||
|
||||
// verify that by power key nolonger exists
|
||||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power3))
|
||||
}
|
||||
|
||||
func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
|
||||
|
@ -42,6 +114,7 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
|||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
assert.True(t, got.IsOK(), "%v", got)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
|
||||
require.True(t, found)
|
||||
assert.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.Equal(t, validatorAddr, validator.Owner)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
const (
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 1000000000
|
||||
precision = 100000000000 // increased to this precision for accuracy with tests on tick_test.go
|
||||
)
|
||||
|
||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
|
||||
|
@ -22,7 +22,9 @@ func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
|||
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
|
||||
|
||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate()
|
||||
pool.BondedTokens += provisions
|
||||
|
||||
// TODO add to the fees provisions
|
||||
pool.LooseUnbondedTokens += provisions
|
||||
return pool
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,7 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
|||
params := k.GetParams(ctx)
|
||||
pool := k.GetPool(ctx)
|
||||
// The target annual inflation rate is recalculated for each previsions cycle. The
|
||||
// inflation is also subject to a rate change (positive of negative) depending or
|
||||
// inflation is also subject to a rate change (positive or negative) depending on
|
||||
// the distance from the desired ratio (67%). The maximum rate change possible is
|
||||
// defined to be 13% per year, however the annual inflation is capped as between
|
||||
// 7% and 20%.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -8,6 +10,9 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
|
||||
var r = rand.New(rand.NewSource(6595))
|
||||
|
||||
func TestGetInflation(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
@ -59,82 +64,307 @@ func TestGetInflation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that provisions are correctly added to the pool and validators each hour for 1 year
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
params := DefaultParams()
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var tokenSupply int64 = 550000000
|
||||
var bondedShares int64 = 150000000
|
||||
var unbondedShares int64 = 400000000
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 250000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 2
|
||||
)
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
var validators [5]Validator
|
||||
validators[0] = NewValidator(addrs[0], pks[0], Description{})
|
||||
validators[0], pool, _ = validators[0].addTokensFromDel(pool, 150000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
pool = keeper.GetPool(ctx)
|
||||
require.Equal(t, bondedShares, pool.BondedTokens, "%v", pool)
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
validators[1] = NewValidator(addrs[1], pks[1], Description{})
|
||||
validators[1], pool, _ = validators[1].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = NewValidator(addrs[2], pks[2], Description{})
|
||||
validators[2], pool, _ = validators[2].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
validators[3] = NewValidator(addrs[3], pks[3], Description{})
|
||||
validators[3], pool, _ = validators[3].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
validators[4] = NewValidator(addrs[4], pks[4], Description{})
|
||||
validators[4], pool, _ = validators[4].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[4] = keeper.updateValidator(ctx, validators[4])
|
||||
|
||||
assert.Equal(t, tokenSupply, pool.TokenSupply())
|
||||
assert.Equal(t, bondedShares, pool.BondedTokens)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedTokens)
|
||||
|
||||
// initial bonded ratio ~ 27%
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, tokenSupply)), "%v", pool.bondedRatio())
|
||||
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
|
||||
|
||||
initialSupply := pool.TokenSupply()
|
||||
initialUnbonded := pool.TokenSupply() - pool.BondedTokens
|
||||
|
||||
// process the provisions a year
|
||||
// process the provisions for a year
|
||||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
expInflation := keeper.nextInflation(ctx).Round(1000000000)
|
||||
_, expProvisions, _ := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
}
|
||||
|
||||
//get the pool and do the final value checks from checkFinalPoolValues
|
||||
pool = keeper.GetPool(ctx)
|
||||
checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs)
|
||||
}
|
||||
|
||||
// Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate
|
||||
// Cycles through the whole gambit of inflation possibilities, starting at 7% inflation, up to 20%, back down to 7% (it takes ~11.4 years)
|
||||
func TestHourlyInflationRateOfChange(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 550000000
|
||||
initialBondedTokens int64 = 150000000
|
||||
initialUnbondedTokens int64 = 400000000
|
||||
cumulativeExpProvs int64
|
||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
// ~11.4 years to go from 7%, up to 20%, back down to 7%
|
||||
for hr := 0; hr < 100000; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
previousInflation := pool.Inflation
|
||||
updatedInflation, expProvisions, pool := updateProvisions(t, keeper, pool, ctx, hr)
|
||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
||||
msg := strconv.Itoa(hr)
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
}
|
||||
|
||||
// Final check that the pool equals initial values + cumulative provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs)
|
||||
}
|
||||
|
||||
//Test that a large unbonding will significantly lower the bonded ratio
|
||||
func TestLargeUnbond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 1200000000
|
||||
initialBondedTokens int64 = 900000000
|
||||
initialUnbondedTokens int64 = 300000000
|
||||
val0UnbondedTokens int64
|
||||
bondedShares = sdk.NewRat(900000000, 1)
|
||||
unbondedShares = sdk.NewRat(300000000, 1)
|
||||
bondSharesVal0 = sdk.NewRat(300000000, 1)
|
||||
validatorTokens = []int64{300000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000}
|
||||
bondedValidators uint16 = 7
|
||||
)
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[0])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
|
||||
// validator[0] will be unbonded, bringing us from 75% bonded ratio to ~50% (unbonding 300,000,000)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
|
||||
bondedShares = bondedShares.Sub(bondSharesVal0)
|
||||
val0UnbondedTokens = pool.unbondedShareExRate().Mul(validator.PoolShares.Unbonded()).Evaluate()
|
||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.unbondedShareExRate()))
|
||||
|
||||
// unbonded shares should increase
|
||||
assert.True(t, unbondedShares.GT(sdk.NewRat(300000000, 1)))
|
||||
// Ensure that new bonded ratio is less than old bonded ratio , because before they were increasing (i.e. 50% < 75)
|
||||
assert.True(t, (pool.bondedRatio().LT(initialBondedRatio)))
|
||||
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter)
|
||||
}
|
||||
|
||||
//Test that a large bonding will significantly increase the bonded ratio
|
||||
func TestLargeBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
var (
|
||||
initialTotalTokens int64 = 1600000000
|
||||
initialBondedTokens int64 = 400000000
|
||||
initialUnbondedTokens int64 = 1200000000
|
||||
unbondedShares = sdk.NewRat(1200000000, 1)
|
||||
unbondedSharesVal9 = sdk.NewRat(400000000, 1)
|
||||
validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000}
|
||||
bondedValidators uint16 = 1
|
||||
)
|
||||
|
||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found := keeper.GetValidator(ctx, addrs[9])
|
||||
assert.True(t, found)
|
||||
|
||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
||||
initialBondedRatio := pool.bondedRatio()
|
||||
|
||||
params := DefaultParams()
|
||||
params.MaxValidators = bondedValidators + 1 //must do this to allow for an extra validator to bond
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// validator[9] will be bonded, bringing us from 25% to ~50% (bonding 400,000,000 tokens)
|
||||
pool, validator, _, _ = OpBondOrUnbond(r, pool, validator)
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
||||
unbondedShares = unbondedShares.Sub(unbondedSharesVal9)
|
||||
|
||||
// unbonded shares should decrease
|
||||
assert.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1)))
|
||||
// Ensure that new bonded ratio is greater than old bonded ratio (i.e. 50% > 25%)
|
||||
assert.True(t, (pool.bondedRatio().GT(initialBondedRatio)))
|
||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
||||
pool = keeper.GetPool(ctx)
|
||||
|
||||
checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter)
|
||||
}
|
||||
|
||||
// Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators
|
||||
func TestInflationWithRandomOperations(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
params := DefaultParams()
|
||||
keeper.setParams(ctx, params)
|
||||
numValidators := 20
|
||||
|
||||
// start off by randomly setting up 20 validators
|
||||
pool, validators := randomSetup(r, numValidators)
|
||||
require.Equal(t, numValidators, len(validators))
|
||||
|
||||
for i := 0; i < len(validators); i++ {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
}
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// Used to rotate validators so each random operation is applied to a different validator
|
||||
validatorCounter := 0
|
||||
|
||||
// Loop through 20 random operations, and check the inflation after each operation
|
||||
for i := 0; i < numValidators; i++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// Get inflation before randomOperation, for comparison later
|
||||
previousInflation := pool.Inflation
|
||||
|
||||
// Perform the random operation, and record how validators are modified
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, pool, validators[validatorCounter])
|
||||
validatorsMod := make([]Validator, len(validators))
|
||||
copy(validatorsMod[:], validators[:])
|
||||
require.Equal(t, numValidators, len(validators), "i %v", validatorCounter)
|
||||
require.Equal(t, numValidators, len(validatorsMod), "i %v", validatorCounter)
|
||||
validatorsMod[validatorCounter] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
pool, validators,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
// set pool and validators after the random operation
|
||||
pool = poolMod
|
||||
keeper.setPool(ctx, pool)
|
||||
validators = validatorsMod
|
||||
|
||||
// Must set inflation here manually, as opposed to most other tests in this suite, where we call keeper.processProvisions(), which updates pool.Inflation
|
||||
updatedInflation := keeper.nextInflation(ctx)
|
||||
pool.Inflation = updatedInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// Ensure inflation changes as expected when random operations are applied.
|
||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
||||
validatorCounter++
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________
|
||||
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
|
||||
|
||||
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
|
||||
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs int64) {
|
||||
calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs
|
||||
assert.Equal(t, calculatedTotalTokens, pool.TokenSupply())
|
||||
}
|
||||
|
||||
// Processes provisions are added to the pool correctly every hour
|
||||
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
|
||||
func updateProvisions(t *testing.T, keeper Keeper, pool Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, Pool) {
|
||||
expInflation := keeper.nextInflation(ctx)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate()
|
||||
startBondedTokens := pool.BondedTokens
|
||||
startTotalSupply := pool.TokenSupply()
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
//fmt.Printf("hr %v, startBondedTokens %v, expProvisions %v, pool.BondedTokens %v\n", hr, startBondedTokens, expProvisions, pool.BondedTokens)
|
||||
require.Equal(t, startBondedTokens+expProvisions, pool.BondedTokens, "hr %v", hr)
|
||||
|
||||
//check provisions were added to pool
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
|
||||
|
||||
return expInflation, expProvisions, pool
|
||||
}
|
||||
|
||||
// Deterministic setup of validators and pool
|
||||
// Allows you to decide how many validators to setup
|
||||
// Allows you to pick which validators are bonded by adjusting the MaxValidators of params
|
||||
func setupTestValidators(pool Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64, maxValidators uint16) ([]Validator, Keeper, Pool) {
|
||||
params := DefaultParams()
|
||||
params.MaxValidators = maxValidators
|
||||
keeper.setParams(ctx, params)
|
||||
numValidators := len(validatorTokens)
|
||||
validators := make([]Validator, numValidators)
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i], pool, _ = validators[i].addTokensFromDel(pool, validatorTokens[i])
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[i] = keeper.updateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
||||
pool = keeper.GetPool(ctx)
|
||||
assert.NotEqual(t, initialSupply, pool.TokenSupply())
|
||||
assert.Equal(t, initialUnbonded, pool.UnbondedTokens)
|
||||
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedTokens, pool.TokenSupply()-pool.BondedTokens))
|
||||
|
||||
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(211813022, 611813022)), "%v", pool.bondedRatio())
|
||||
|
||||
// global supply
|
||||
assert.Equal(t, int64(611813022), pool.TokenSupply())
|
||||
assert.Equal(t, int64(211813022), pool.BondedTokens)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedTokens)
|
||||
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", pool.bondedShareExRate())
|
||||
}
|
||||
|
||||
return validators, keeper, pool
|
||||
}
|
||||
|
||||
// Checks that the deterministic validator setup you wanted matches the values in the pool
|
||||
func checkValidatorSetup(t *testing.T, pool Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
||||
assert.Equal(t, initialTotalTokens, pool.TokenSupply())
|
||||
assert.Equal(t, initialBondedTokens, pool.BondedTokens)
|
||||
assert.Equal(t, initialUnbondedTokens, pool.UnbondedTokens)
|
||||
|
||||
// test initial bonded ratio
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.bondedRatio())
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
|
||||
}
|
||||
|
||||
// Checks that The inflation will correctly increase or decrease after an update to the pool
|
||||
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||
inflationChange := updatedInflation.Sub(previousInflation)
|
||||
|
||||
switch {
|
||||
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||
assert.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||
|
||||
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
|
||||
case pool.bondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(20, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
||||
//This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%)
|
||||
} else {
|
||||
assert.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||
}
|
||||
|
||||
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||
assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||
|
||||
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
|
||||
case pool.bondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||
if previousInflation.Equal(sdk.NewRat(7, 100)) {
|
||||
assert.Equal(t, true, inflationChange.IsZero(), msg)
|
||||
|
||||
//This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%)
|
||||
} else {
|
||||
assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,12 @@ func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, p
|
|||
store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner)
|
||||
}
|
||||
|
||||
// used in testing
|
||||
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Get(power) != nil
|
||||
}
|
||||
|
||||
// Get the set of all validators with no limits, used during genesis dump
|
||||
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
@ -167,6 +173,12 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []Validator {
|
|||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||
}
|
||||
|
||||
// Reached to revoked validators, stop iterating
|
||||
if validator.Revoked {
|
||||
iterator.Close()
|
||||
break
|
||||
}
|
||||
if validator.Status() == sdk.Bonded {
|
||||
validators[i] = validator
|
||||
i++
|
||||
|
|
|
@ -51,15 +51,22 @@ func GetValidatorsByPowerKey(validator Validator, pool Pool) []byte {
|
|||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
revokedBytes := make([]byte, 1)
|
||||
if validator.Revoked {
|
||||
revokedBytes[0] = byte(0x01)
|
||||
} else {
|
||||
revokedBytes[0] = byte(0x00)
|
||||
}
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
return append(ValidatorsByPowerKey,
|
||||
append(revokedBytes,
|
||||
append(powerBytes,
|
||||
append(heightBytes,
|
||||
append(counterBytes, validator.Owner.Bytes()...)...)...)...) // TODO don't technically need to store owner
|
||||
append(counterBytes, validator.Owner.Bytes()...)...)...)...)...) // TODO don't technically need to store owner
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
|
|
|
@ -24,6 +24,47 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create a random pool
|
||||
pool.BondedTokens = 1234
|
||||
pool.BondedShares = sdk.NewRat(124)
|
||||
pool.UnbondingTokens = 13934
|
||||
pool.UnbondingShares = sdk.NewRat(145)
|
||||
pool.UnbondedTokens = 154
|
||||
pool.UnbondedShares = sdk.NewRat(1333)
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
// add a validator
|
||||
validator := NewValidator(addrVals[0], pks[0], Description{})
|
||||
validator, pool, delSharesCreated := validator.addTokensFromDel(pool, 100)
|
||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate())
|
||||
keeper.setPool(ctx, pool)
|
||||
keeper.updateValidator(ctx, validator)
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool)
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
power := GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
// burn half the delegator shares
|
||||
validator, pool, burned := validator.removeDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||
assert.Equal(t, int64(50), burned)
|
||||
keeper.setPool(ctx, pool) // update the pool
|
||||
keeper.updateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||
assert.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
|
||||
pool = keeper.GetPool(ctx)
|
||||
validator, found = keeper.GetValidator(ctx, addrVals[0])
|
||||
power = GetValidatorsByPowerKey(validator, pool)
|
||||
assert.True(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||
}
|
||||
|
||||
func TestSetValidator(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
@ -117,8 +158,8 @@ func TestValidatorBasics(t *testing.T) {
|
|||
resVals = keeper.GetValidatorsBonded(ctx)
|
||||
require.Equal(t, 3, len(resVals))
|
||||
assert.True(ValEq(t, validators[0], resVals[2])) // order doesn't matter here
|
||||
assert.True(ValEq(t, validators[1], resVals[0]))
|
||||
assert.True(ValEq(t, validators[2], resVals[1]))
|
||||
assert.True(ValEq(t, validators[1], resVals[1]))
|
||||
assert.True(ValEq(t, validators[2], resVals[0]))
|
||||
|
||||
// remove a record
|
||||
keeper.removeValidator(ctx, validators[1].Owner)
|
||||
|
|
|
@ -45,7 +45,20 @@ func (msg MsgCreateValidator) GetSigners() []sdk.Address {
|
|||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgCreateValidator) GetSignBytes() []byte {
|
||||
return msgCdc.MustMarshalBinary(msg)
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
PubKey: sdk.MustBech32ifyValPub(msg.PubKey),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// quick validity check
|
||||
|
@ -89,7 +102,13 @@ func (msg MsgEditValidator) GetSigners() []sdk.Address {
|
|||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgEditValidator) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Description
|
||||
ValidatorAddr string `json:"address"`
|
||||
}{
|
||||
Description: msg.Description,
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -133,7 +152,15 @@ func (msg MsgDelegate) GetSigners() []sdk.Address {
|
|||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgDelegate) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Bond: msg.Bond,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -180,7 +207,15 @@ func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.Deleg
|
|||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgUnbond) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
DelegatorAddr string `json:"delegator_addr"`
|
||||
ValidatorAddr string `json:"validator_addr"`
|
||||
Shares string `json:"shares"`
|
||||
}{
|
||||
DelegatorAddr: sdk.MustBech32ifyAcc(msg.DelegatorAddr),
|
||||
ValidatorAddr: sdk.MustBech32ifyVal(msg.ValidatorAddr),
|
||||
Shares: msg.Shares,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -118,13 +118,14 @@ func (s PoolShares) ToBonded(p Pool) PoolShares {
|
|||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// TODO better tests
|
||||
// get the equivalent amount of tokens contained by the shares
|
||||
func (s PoolShares) Tokens(p Pool) sdk.Rat {
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
return p.unbondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
return p.bondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
case sdk.Unbonding:
|
||||
return p.unbondedShareExRate().Mul(s.Amount)
|
||||
return p.unbondingShareExRate().Mul(s.Amount)
|
||||
case sdk.Unbonded:
|
||||
return p.unbondedShareExRate().Mul(s.Amount)
|
||||
default:
|
||||
|
|
|
@ -3,6 +3,7 @@ package stake
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -21,33 +22,8 @@ import (
|
|||
|
||||
// dummy addresses used for testing
|
||||
var (
|
||||
addrs = []sdk.Address{
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6160", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctqyxjnwh"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6161", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctpesxxn9"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6162", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctzhrnsa6"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6163", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctr2489qg"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6164", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctytvs4pd"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6165", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ct9k6yqul"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6166", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctxcf3kjq"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6167", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ct89l9r0j"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6168", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctg6jkls2"),
|
||||
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6169", "cosmosaccaddr15ky9du8a2wlstz6fpx3p4mqpjyrm5ctf8yz2dc"),
|
||||
}
|
||||
|
||||
// dummy pubkeys used for testing
|
||||
pks = []crypto.PubKey{
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB53"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB54"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB55"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB56"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB57"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB58"),
|
||||
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB59"),
|
||||
}
|
||||
|
||||
addrs = createTestAddrs(100)
|
||||
pks = createTestPubKeys(100)
|
||||
emptyAddr sdk.Address
|
||||
emptyPubkey crypto.PubKey
|
||||
)
|
||||
|
@ -162,3 +138,36 @@ func testAddr(addr string, bech string) sdk.Address {
|
|||
|
||||
return res
|
||||
}
|
||||
|
||||
func createTestAddrs(numAddrs int) []sdk.Address {
|
||||
var addresses []sdk.Address
|
||||
var buffer bytes.Buffer
|
||||
|
||||
// start at 100 so we can make up to 999 test addresses with valid test addresses
|
||||
for i := 100; i < (numAddrs + 100); i++ {
|
||||
numString := strconv.Itoa(i)
|
||||
buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string
|
||||
|
||||
buffer.WriteString(numString) //adding on final two digits to make addresses unique
|
||||
res, _ := sdk.GetAccAddressHex(buffer.String())
|
||||
bech, _ := sdk.Bech32ifyAcc(res)
|
||||
addresses = append(addresses, testAddr(buffer.String(), bech))
|
||||
buffer.Reset()
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
func createTestPubKeys(numPubKeys int) []crypto.PubKey {
|
||||
var publicKeys []crypto.PubKey
|
||||
var buffer bytes.Buffer
|
||||
|
||||
//start at 10 to avoid changing 1 to 01, 2 to 02, etc
|
||||
for i := 100; i < (numPubKeys + 100); i++ {
|
||||
numString := strconv.Itoa(i)
|
||||
buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string
|
||||
buffer.WriteString(numString) //adding on final two digits to make pubkeys unique
|
||||
publicKeys = append(publicKeys, newPubKey(buffer.String()))
|
||||
buffer.Reset()
|
||||
}
|
||||
return publicKeys
|
||||
}
|
||||
|
|
|
@ -251,6 +251,7 @@ func (v Validator) DelegatorShareExRate(pool Pool) sdk.Rat {
|
|||
var _ sdk.Validator = Validator{}
|
||||
|
||||
// nolint - for sdk.Validator
|
||||
func (v Validator) GetMoniker() string { return v.Description.Moniker }
|
||||
func (v Validator) GetStatus() sdk.BondStatus { return v.Status() }
|
||||
func (v Validator) GetOwner() sdk.Address { return v.Owner }
|
||||
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
|
||||
|
|
|
@ -148,7 +148,7 @@ func TestUpdateStatus(t *testing.T) {
|
|||
// TODO refactor this random setup
|
||||
|
||||
// generate a random validator
|
||||
func randomValidator(r *rand.Rand) Validator {
|
||||
func randomValidator(r *rand.Rand, i int) Validator {
|
||||
|
||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
|
@ -160,8 +160,8 @@ func randomValidator(r *rand.Rand) Validator {
|
|||
pShares = NewUnbondedShares(poolSharesAmt)
|
||||
}
|
||||
return Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Owner: addrs[i],
|
||||
PubKey: pks[i],
|
||||
PoolShares: pShares,
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ func randomSetup(r *rand.Rand, numValidators int) (Pool, Validators) {
|
|||
|
||||
validators := make([]Validator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validator := randomValidator(r)
|
||||
validator := randomValidator(r, i)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
||||
pool.BondedTokens += validator.PoolShares.Bonded().Evaluate()
|
||||
|
|
Loading…
Reference in New Issue