diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a6b28673e..155945ae69 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,7 +83,7 @@ jobs: name: Test cli command: | export PATH="$GOBIN:$PATH" - make test_cli_retry + make test_cli test_cover: <<: *defaults diff --git a/CHANGELOG.md b/CHANGELOG.md index cf6a481f84..b6cadd3ed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ IMPROVEMENTS * added contributing guidelines BUG FIXES +* [x/slashing] \#1510 Unrevoked validators cannot un-revoke themselves * [gaia] Added self delegation for validators in the genesis creation * [lcd] tests now don't depend on raw json text * [stake] error strings lower case diff --git a/Dockerfile b/Dockerfile index 80bbae85f3..5dfd625deb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,35 @@ # Simple usage with a mounted data directory: # > docker build -t gaia . -# > docker run -v $HOME/.gaiad:/root/.gaiad gaia init -# > docker run -v $HOME/.gaiad:/root/.gaiad gaia start - -FROM alpine:edge +# > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.gaiad:/root/.gaiad -v ~/.gaiacli:/root/.gaiacli gaia gaiad init +# > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.gaiad:/root/.gaiad -v ~/.gaiacli:/root/.gaiacli gaia gaiad start +FROM golang:alpine AS build-env # Set up dependencies -ENV PACKAGES go glide make git libc-dev bash +ENV PACKAGES make git libc-dev bash gcc linux-headers eudev-dev -# Set up GOPATH & PATH - -ENV GOPATH /root/go -ENV BASE_PATH $GOPATH/src/github.com/cosmos -ENV REPO_PATH $BASE_PATH/cosmos-sdk -ENV WORKDIR /cosmos/ -ENV PATH $GOPATH/bin:$PATH - -# Link expected Go repo path - -RUN mkdir -p $WORKDIR $GOPATH/pkg $ $GOPATH/bin $BASE_PATH +# Set working directory for the build +WORKDIR /go/src/github.com/cosmos/cosmos-sdk # Add source files - -ADD . $REPO_PATH +COPY . . # Install minimum necessary dependencies, build Cosmos SDK, remove packages RUN apk add --no-cache $PACKAGES && \ - cd $REPO_PATH && make get_tools && make get_vendor_deps && make build && make install && \ - apk del $PACKAGES + make get_tools && \ + make get_vendor_deps && \ + make build && \ + make install -# Set entrypoint +# Final image +FROM alpine:edge -ENTRYPOINT ["gaiad"] +# Install ca-certificates +RUN apk add --update ca-certificates +WORKDIR /root + +# Copy over binaries from the build-env +COPY --from=build-env /go/bin/gaiad /usr/bin/gaiad +COPY --from=build-env /go/bin/gaiacli /usr/bin/gaiacli + +# Run gaiad by default, omit entrypoint to ease using container with gaiacli +CMD ["gaiad"] diff --git a/Makefile b/Makefile index 96ffb47a2c..7d46c79149 100644 --- a/Makefile +++ b/Makefile @@ -92,15 +92,9 @@ test: test_unit test_cli: @go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -test_cli_retry: - for i in 1 2 3; do make test_cli && break || sleep 2; done - test_unit: @go test $(PACKAGES_NOCLITEST) -test_unit_retry: - for i in 1 2 3; do make test_unit && break || sleep 2; done - test_race: @go test -race $(PACKAGES_NOCLITEST) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index b6032e035b..4eea49fd39 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -11,10 +11,10 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" - tmtypes "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" diff --git a/client/context/helpers.go b/client/context/helpers.go index 54c1463ee6..89c1713533 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -9,9 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + cmn "github.com/tendermint/tendermint/libs/common" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" @@ -141,13 +141,22 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msgs []sdk.Msg, cdc sequence := ctx.Sequence memo := ctx.Memo + fee := sdk.Coin{} + if ctx.Fee != "" { + parsedFee, err := sdk.ParseCoin(ctx.Fee) + if err != nil { + return nil, err + } + fee = parsedFee + } + signMsg := auth.StdSignMsg{ ChainID: chainID, AccountNumber: accnum, Sequence: sequence, Msgs: msgs, Memo: memo, - Fee: auth.NewStdFee(ctx.Gas, sdk.Coin{}), // TODO run simulate to estimate gas? + Fee: auth.NewStdFee(ctx.Gas, fee), // TODO run simulate to estimate gas? } keybase, err := keys.GetKeyBase() diff --git a/client/context/types.go b/client/context/types.go index 3b7573dceb..58df9b5bf2 100644 --- a/client/context/types.go +++ b/client/context/types.go @@ -11,6 +11,7 @@ type CoreContext struct { ChainID string Height int64 Gas int64 + Fee string TrustNode bool NodeURI string FromAddressName string @@ -41,6 +42,12 @@ func (c CoreContext) WithGas(gas int64) CoreContext { return c } +// WithFee - return a copy of the context with an updated fee +func (c CoreContext) WithFee(fee string) CoreContext { + c.Fee = fee + return c +} + // WithTrustNode - return a copy of the context with an updated TrustNode flag func (c CoreContext) WithTrustNode(trustNode bool) CoreContext { c.TrustNode = trustNode diff --git a/client/context/viper.go b/client/context/viper.go index 3637e06abc..c0b48fc85d 100644 --- a/client/context/viper.go +++ b/client/context/viper.go @@ -31,6 +31,7 @@ func NewCoreContextFromViper() CoreContext { ChainID: chainID, Height: viper.GetInt64(client.FlagHeight), Gas: viper.GetInt64(client.FlagGas), + Fee: viper.GetString(client.FlagFee), TrustNode: viper.GetBool(client.FlagTrustNode), FromAddressName: viper.GetString(client.FlagName), NodeURI: nodeURI, diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 229e8d7137..2c46c5ffb7 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -13,9 +13,9 @@ import ( cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/common" p2p "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/libs/common" client "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/client/keys" diff --git a/client/lcd/root.go b/client/lcd/root.go index e79ffd2f13..5c427546a5 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -9,8 +9,8 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" - tmserver "github.com/tendermint/tendermint/rpc/lib/server" cmn "github.com/tendermint/tendermint/libs/common" + tmserver "github.com/tendermint/tendermint/rpc/lib/server" client "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 0467262396..ac99611cf0 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -19,14 +19,14 @@ import ( abci "github.com/tendermint/tendermint/abci/types" tmcfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/cli" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" nm "github.com/tendermint/tendermint/node" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" - "github.com/tendermint/tendermint/libs/cli" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/client/keys" @@ -169,7 +169,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.Address) ( //time.Sleep(time.Second) //tests.WaitForHeight(2, port) - tests.WaitForStart(port) + tests.WaitForLCDStart(port) tests.WaitForHeight(1, port) // for use in defer diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 77b179765e..b355c4ed51 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -5,10 +5,10 @@ import ( "os" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -37,12 +37,13 @@ type GaiaApp struct { cdc *wire.Codec // keys to access the substores - keyMain *sdk.KVStoreKey - keyAccount *sdk.KVStoreKey - keyIBC *sdk.KVStoreKey - keyStake *sdk.KVStoreKey - keySlashing *sdk.KVStoreKey - keyGov *sdk.KVStoreKey + keyMain *sdk.KVStoreKey + keyAccount *sdk.KVStoreKey + keyIBC *sdk.KVStoreKey + keyStake *sdk.KVStoreKey + keySlashing *sdk.KVStoreKey + keyGov *sdk.KVStoreKey + keyFeeCollection *sdk.KVStoreKey // Manage getting and setting accounts accountMapper auth.AccountMapper @@ -59,14 +60,15 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // 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"), - keyGov: sdk.NewKVStoreKey("gov"), + 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"), + keyGov: sdk.NewKVStoreKey("gov"), + keyFeeCollection: sdk.NewKVStoreKey("fee"), } // define the accountMapper @@ -82,6 +84,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace)) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) + app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) // register message routes app.Router(). @@ -96,7 +99,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { 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, app.keyGov) + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 62fe0631a3..07230a4f90 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -7,6 +7,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" @@ -16,7 +19,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/tendermint/tendermint/crypto" ) func TestGaiaCLISend(t *testing.T) { @@ -34,9 +36,10 @@ func TestGaiaCLISend(t *testing.T) { flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) // start gaiad server - proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) + proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) defer proc.Stop(false) - tests.WaitForStart(port) + tests.WaitForTMStart(port) + tests.WaitForNextHeightTM(port) fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") fooCech, err := sdk.Bech32ifyAcc(fooAddr) @@ -90,9 +93,10 @@ func TestGaiaCLICreateValidator(t *testing.T) { flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) // start gaiad server - proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) + proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) defer proc.Stop(false) - tests.WaitForStart(port) + tests.WaitForTMStart(port) + tests.WaitForNextHeightTM(port) fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") fooCech, err := sdk.Bech32ifyAcc(fooAddr) @@ -130,19 +134,20 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.Equal(t, "2/1", validator.PoolShares.Amount.String()) // unbond a single share - unbondStr := fmt.Sprintf("gaiacli stake unbond %v", flags) + unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags) unbondStr += fmt.Sprintf(" --name=%v", "bar") unbondStr += fmt.Sprintf(" --address-validator=%v", barCech) unbondStr += fmt.Sprintf(" --address-delegator=%v", barCech) - unbondStr += fmt.Sprintf(" --shares=%v", "1") - unbondStr += fmt.Sprintf(" --sequence=%v", "1") - t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr)) + unbondStr += fmt.Sprintf(" --shares-amount=%v", "1") - executeWrite(t, unbondStr, pass) + success := executeWrite(t, unbondStr, pass) + require.True(t, success) tests.WaitForNextHeightTM(port) + /* // this won't be what we expect because we've only started unbonding, haven't completed barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc) + */ validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %v --output=json %v", barCech, flags)) require.Equal(t, "1/1", validator.PoolShares.Amount.String()) } @@ -162,9 +167,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) { flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) // start gaiad server - proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) + proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) defer proc.Stop(false) - tests.WaitForStart(port) + tests.WaitForTMStart(port) + tests.WaitForNextHeightTM(port) fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") fooCech, err := sdk.Bech32ifyAcc(fooAddr) @@ -203,14 +209,27 @@ func TestGaiaCLISubmitProposal(t *testing.T) { //___________________________________________________________________________________ // executors -func executeWrite(t *testing.T, cmdStr string, writes ...string) { +func executeWrite(t *testing.T, cmdStr string, writes ...string) bool { proc := tests.GoExecuteT(t, cmdStr) for _, write := range writes { _, err := proc.StdinPipe.Write([]byte(write + "\n")) require.NoError(t, err) } + stdout, stderr, err := proc.ReadAll() + if err != nil { + fmt.Println("Err on proc.ReadAll()", err, cmdStr) + } + // Log output. + if len(stdout) > 0 { + t.Log("Stdout:", cmn.Green(string(stdout))) + } + if len(stderr) > 0 { + t.Log("Stderr:", cmn.Red(string(stderr))) + } + proc.Wait() + return proc.ExitState.Success() // bz := proc.StdoutBuffer.Bytes() // fmt.Println("EXEC WRITE", string(bz)) } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index d96275de65..9c8434eb06 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -6,10 +6,10 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/server" diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 100e25435d..1c44a0bbdf 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -4,10 +4,10 @@ import ( "encoding/json" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 1cc22b8877..0bb84c146a 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -7,10 +7,10 @@ import ( "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/server" diff --git a/server/constructors.go b/server/constructors.go index d93a1defec..ab7a949e6c 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -5,9 +5,9 @@ import ( "path/filepath" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" ) // AppCreator lets us lazily initialize app, using home dir diff --git a/server/init.go b/server/init.go index 33a41564cf..0cc99e79fa 100644 --- a/server/init.go +++ b/server/init.go @@ -18,12 +18,12 @@ import ( "github.com/tendermint/tendermint/crypto" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/p2p" - pvm "github.com/tendermint/tendermint/privval" - tmtypes "github.com/tendermint/tendermint/types" tmcli "github.com/tendermint/tendermint/libs/cli" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/p2p" + pvm "github.com/tendermint/tendermint/privval" + tmtypes "github.com/tendermint/tendermint/types" clkeys "github.com/cosmos/cosmos-sdk/client/keys" serverconfig "github.com/cosmos/cosmos-sdk/server/config" diff --git a/server/mock/app.go b/server/mock/app.go index c99150b979..5229da41ee 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -7,9 +7,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" - tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" gc "github.com/cosmos/cosmos-sdk/server/config" diff --git a/server/start.go b/server/start.go index 4f90930262..37e40c9992 100644 --- a/server/start.go +++ b/server/start.go @@ -8,10 +8,10 @@ import ( "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/node" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" - cmn "github.com/tendermint/tendermint/libs/common" ) const ( diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 28d9e7e68a..10926e4bcb 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -7,8 +7,8 @@ import ( "golang.org/x/crypto/ripemd160" abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/crypto/merkle" + dbm "github.com/tendermint/tendermint/libs/db" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/store/rootmultistore_test.go b/store/rootmultistore_test.go index 137f04eabb..f564114626 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmultistore_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/crypto/merkle" + dbm "github.com/tendermint/tendermint/libs/db" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/tests/gobash.go b/tests/gobash.go index b2d765281a..71db2d2dda 100644 --- a/tests/gobash.go +++ b/tests/gobash.go @@ -1,6 +1,8 @@ package tests import ( + "fmt" + "io/ioutil" "strings" "testing" @@ -21,13 +23,15 @@ func ExecuteT(t *testing.T, cmd string) (out string) { } // Start process and wait. - proc, err := StartProcess("", name, args, nil, nil) + proc, err := StartProcess("", name, args) require.NoError(t, err) - proc.Wait() // Get the output. - outbz := proc.StdoutBuffer.Bytes() - errbz := proc.StderrBuffer.Bytes() + outbz, errbz, err := proc.ReadAll() + if err != nil { + fmt.Println("Err on proc.ReadAll()", err, args) + } + proc.Wait() // Log output. if len(outbz) > 0 { @@ -56,36 +60,38 @@ func GoExecuteT(t *testing.T, cmd string) (proc *Process) { } // Start process. - proc, err := StartProcess("", name, args, nil, nil) + proc, err := StartProcess("", name, args) require.NoError(t, err) - - // Run goroutines to log stdout. - go func() { - buf := make([]byte, 10240) // TODO Document the effects. - for { - n, err := proc.StdoutBuffer.Read(buf) - if err != nil { - return - } - if n > 0 { - t.Log("Stdout:", cmn.Green(string(buf[:n]))) - } - } - }() - - // Run goroutines to log stderr. - go func() { - buf := make([]byte, 10240) // TODO Document the effects. - for { - n, err := proc.StderrBuffer.Read(buf) - if err != nil { - return - } - if n > 0 { - t.Log("Stderr:", cmn.Red(string(buf[:n]))) - } - } - }() - + return proc +} + +// Same as GoExecuteT but spawns a go routine to ReadAll off stdout. +func GoExecuteTWithStdout(t *testing.T, cmd string) (proc *Process) { + t.Log("Running", cmn.Cyan(cmd)) + + // Split cmd to name and args. + split := strings.Split(cmd, " ") + require.True(t, len(split) > 0, "no command provided") + name, args := split[0], []string(nil) + if len(split) > 1 { + args = split[1:] + } + + // Start process. + proc, err := CreateProcess("", name, args) + require.NoError(t, err) + + // Without this, the test halts ?! + go func() { + _, err := ioutil.ReadAll(proc.StdoutPipe) + if err != nil { + fmt.Println("-------------ERR-----------------------", err) + return + } + }() + + err = proc.Cmd.Start() + require.NoError(t, err) + proc.Pid = proc.Cmd.Process.Pid return proc } diff --git a/tests/process.go b/tests/process.go index d2459a0b5b..08f975cf82 100644 --- a/tests/process.go +++ b/tests/process.go @@ -1,8 +1,8 @@ package tests import ( - "bytes" "io" + "io/ioutil" "os" "os/exec" "time" @@ -10,26 +10,37 @@ import ( // execution process type Process struct { - ExecPath string - Args []string - Pid int - StartTime time.Time - EndTime time.Time - Cmd *exec.Cmd `json:"-"` - ExitState *os.ProcessState `json:"-"` - WaitCh chan struct{} `json:"-"` - StdinPipe io.WriteCloser `json:"-"` - StdoutBuffer *bytes.Buffer `json:"-"` - StderrBuffer *bytes.Buffer `json:"-"` + ExecPath string + Args []string + Pid int + StartTime time.Time + EndTime time.Time + Cmd *exec.Cmd `json:"-"` + ExitState *os.ProcessState `json:"-"` + StdinPipe io.WriteCloser `json:"-"` + StdoutPipe io.ReadCloser `json:"-"` + StderrPipe io.ReadCloser `json:"-"` } // dir: The working directory. If "", os.Getwd() is used. // name: Command name // args: Args to command. (should not include name) -// outFile, errFile: If not nil, will use, otherwise new Buffers will be -// allocated. Either way, Process.Cmd.StdoutPipe and Process.Cmd.StderrPipe will be nil -// respectively. -func StartProcess(dir string, name string, args []string, outFile, errFile io.WriteCloser) (*Process, error) { +func StartProcess(dir string, name string, args []string) (*Process, error) { + proc, err := CreateProcess(dir, name, args) + if err != nil { + return nil, err + } + // cmd start + if err := proc.Cmd.Start(); err != nil { + return nil, err + } + proc.Pid = proc.Cmd.Process.Pid + + return proc, nil +} + +// Same as StartProcess but doesn't start the process +func CreateProcess(dir string, name string, args []string) (*Process, error) { var cmd = exec.Command(name, args...) // is not yet started. // cmd dir if dir == "" { @@ -46,52 +57,27 @@ func StartProcess(dir string, name string, args []string, outFile, errFile io.Wr if err != nil { return nil, err } - // cmd stdout, stderr - var outBuffer, errBuffer *bytes.Buffer - if outFile != nil { - cmd.Stdout = outFile - } else { - outBuffer = bytes.NewBuffer(nil) - cmd.Stdout = outBuffer - } - if errFile != nil { - cmd.Stderr = errFile - } else { - errBuffer = bytes.NewBuffer(nil) - cmd.Stderr = errBuffer - } - // cmd start - if err := cmd.Start(); err != nil { + + stdout, err := cmd.StdoutPipe() + if err != nil { return nil, err } + + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, err + } + proc := &Process{ - ExecPath: name, - Args: args, - Pid: cmd.Process.Pid, - StartTime: time.Now(), - Cmd: cmd, - ExitState: nil, - WaitCh: make(chan struct{}), - StdinPipe: stdin, + ExecPath: name, + Args: args, + StartTime: time.Now(), + Cmd: cmd, + ExitState: nil, + StdinPipe: stdin, + StdoutPipe: stdout, + StderrPipe: stderr, } - if outBuffer != nil { - proc.StdoutBuffer = outBuffer - } - if errBuffer != nil { - proc.StderrBuffer = errBuffer - } - go func() { - err := proc.Cmd.Wait() - if err != nil { - // fmt.Printf("Process exit: %v\n", err) - if exitError, ok := err.(*exec.ExitError); ok { - proc.ExitState = exitError.ProcessState - } - } - proc.ExitState = proc.Cmd.ProcessState - proc.EndTime = time.Now() // TODO make this goroutine-safe - close(proc.WaitCh) - }() return proc, nil } @@ -106,5 +92,26 @@ func (proc *Process) Stop(kill bool) error { // wait for the process func (proc *Process) Wait() { - <-proc.WaitCh + err := proc.Cmd.Wait() + if err != nil { + // fmt.Printf("Process exit: %v\n", err) + if exitError, ok := err.(*exec.ExitError); ok { + proc.ExitState = exitError.ProcessState + } + } + proc.ExitState = proc.Cmd.ProcessState + proc.EndTime = time.Now() // TODO make this goroutine-safe +} + +// ReadAll calls ioutil.ReadAll on the StdoutPipe and StderrPipe. +func (proc *Process) ReadAll() (stdout []byte, stderr []byte, err error) { + outbz, err := ioutil.ReadAll(proc.StdoutPipe) + if err != nil { + return nil, nil, err + } + errbz, err := ioutil.ReadAll(proc.StderrPipe) + if err != nil { + return nil, nil, err + } + return outbz, errbz, nil } diff --git a/tests/util.go b/tests/util.go index afa127bf01..1138bc95e9 100644 --- a/tests/util.go +++ b/tests/util.go @@ -21,13 +21,20 @@ func WaitForNextHeightTM(port string) { // Wait for N tendermint blocks to pass using the Tendermint RPC // on localhost func WaitForNextNBlocksTM(n int64, port string) { + + // get the latest block and wait for n more url := fmt.Sprintf("http://localhost:%v", port) cl := tmclient.NewHTTP(url, "/websocket") resBlock, err := cl.Block(nil) - if err != nil { - panic(err) + var height int64 + if err != nil || resBlock.Block == nil { + // wait for the first block to exist + WaitForHeightTM(1, port) + height = 1 + n + } else { + height = resBlock.Block.Height + n } - waitForHeightTM(resBlock.Block.Height+n, url) + waitForHeightTM(height, url) } // Wait for the given height from the Tendermint RPC @@ -57,7 +64,6 @@ func waitForHeightTM(height int64, url string) { if resBlock.Block != nil && resBlock.Block.Height >= height { - fmt.Println("HEIGHT", resBlock.Block.Height) return } time.Sleep(time.Millisecond * 100) @@ -115,10 +121,23 @@ func waitForHeight(height int64, url string) { } } -// wait for tendermint to start -func WaitForStart(port string) { - var err error +// wait for tendermint to start by querying the LCD +func WaitForLCDStart(port string) { url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) + WaitForStart(url) +} + +// wait for tendermint to start by querying tendermint +func WaitForTMStart(port string) { + url := fmt.Sprintf("http://localhost:%v/block", port) + WaitForStart(url) +} + +// WaitForStart waits for the node to start by pinging the url +// every 100ms for 5s until it returns 200. If it takes longer than 5s, +// it panics. +func WaitForStart(url string) { + var err error // ping the status endpoint a few times a second // for a few seconds until we get a good response. @@ -131,6 +150,8 @@ func WaitForStart(port string) { if err != nil || res == nil { continue } + // body, _ := ioutil.ReadAll(res.Body) + // fmt.Println("BODY", string(body)) err = res.Body.Close() if err != nil { panic(err) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 18416174bd..a64d3eb0f1 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -106,7 +106,7 @@ func TestSlashingMsgs(t *testing.T) { // no signing info yet checkValidatorSigningInfo(t, mapp, keeper, addr1, false) - // unrevoke should fail with unknown validator + // unrevoke should fail with validator not revoked res := mock.SignCheck(mapp.BaseApp, []sdk.Msg{unrevokeMsg}, []int64{0}, []int64{1}, priv1) - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeInvalidValidator), res.Code) + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), res.Code) } diff --git a/x/slashing/errors.go b/x/slashing/errors.go index 9f5d9c4eb0..3139c1662f 100644 --- a/x/slashing/errors.go +++ b/x/slashing/errors.go @@ -12,8 +12,9 @@ const ( // Default slashing codespace DefaultCodespace sdk.CodespaceType = 10 - CodeInvalidValidator CodeType = 101 - CodeValidatorJailed CodeType = 102 + CodeInvalidValidator CodeType = 101 + CodeValidatorJailed CodeType = 102 + CodeValidatorNotRevoked CodeType = 103 ) func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error { @@ -25,3 +26,6 @@ func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked") } +func ErrValidatorNotRevoked(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeValidatorNotRevoked, "validator not revoked, cannot be unrevoked") +} diff --git a/x/slashing/handler.go b/x/slashing/handler.go index e786f34aca..21d50aef8f 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -26,6 +26,10 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { return ErrNoValidatorForAddress(k.codespace).Result() } + if !validator.GetRevoked() { + return ErrValidatorNotRevoked(k.codespace).Result() + } + addr := validator.GetPubKey().Address() // Signing info must exist diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go new file mode 100644 index 0000000000..c2c5d9166f --- /dev/null +++ b/x/slashing/handler_test.go @@ -0,0 +1,28 @@ +package slashing + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake" +) + +func TestCannotUnrevokeUnlessRevoked(t *testing.T) { + // initial setup + ctx, ck, sk, keeper := createTestInput(t) + slh := NewHandler(keeper) + amtInt := int64(100) + addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + got := stake.NewHandler(sk)(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.Sub(amt)}}) + require.True(t, sdk.NewRatFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + + // assert non-revoked validator can't be unrevoked + got = slh(ctx, NewMsgUnrevoke(addr)) + require.False(t, got.IsOK(), "allowed unrevoke of non-revoked validator") + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), got.Code) +} diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 068c19ce22..7200caef3f 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "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" @@ -154,9 +155,10 @@ func GetCmdRedelegate(storeName string, cdc *wire.Codec) *cobra.Command { Short: "redelegate illiquid tokens from one validator to another", } cmd.AddCommand( - GetCmdBeginRedelegate(storeName, cdc), - GetCmdCompleteRedelegate(cdc), - ) + client.PostCommands( + GetCmdBeginRedelegate(storeName, cdc), + GetCmdCompleteRedelegate(cdc), + )...) return cmd } @@ -301,9 +303,10 @@ func GetCmdUnbond(storeName string, cdc *wire.Codec) *cobra.Command { Short: "begin or complete unbonding shares from a validator", } cmd.AddCommand( - GetCmdBeginUnbonding(storeName, cdc), - GetCmdCompleteUnbonding(cdc), - ) + client.PostCommands( + GetCmdBeginUnbonding(storeName, cdc), + GetCmdCompleteUnbonding(cdc), + )...) return cmd }