From 50926ffffffddda3c67c279258c69202977706a2 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sun, 4 Nov 2018 21:28:38 -0500 Subject: [PATCH 1/6] Update to TM v0.26.0 - Part I (#2679) * Update to TM v0.26.0 --- CHANGELOG.md | 2 +- Gopkg.lock | 75 +++++++++-------------- Gopkg.toml | 28 +-------- baseapp/baseapp.go | 2 +- baseapp/baseapp_test.go | 16 ++--- client/context/context.go | 7 ++- client/context/query.go | 52 ++++++++-------- client/lcd/lcd_test.go | 4 +- client/tx/query.go | 2 +- client/utils/utils.go | 2 +- client/utils/utils_test.go | 4 +- cmd/gaia/app/app.go | 2 +- cmd/gaia/cmd/gaiadebug/main.go | 2 +- crypto/keys/keybase.go | 2 +- crypto/keys/types.go | 4 +- docs/sdk/core/app2.md | 2 +- docs/sdk/core/examples/app2.go | 2 +- docs/sdk/core/examples/app2_test.go | 4 +- examples/democoin/x/oracle/keeper.go | 4 +- examples/democoin/x/oracle/keeper_keys.go | 4 +- examples/democoin/x/oracle/oracle_test.go | 4 +- examples/democoin/x/simplestake/keeper.go | 4 +- server/start.go | 12 ++-- store/iavlstore.go | 35 +++++++---- store/iavlstore_test.go | 8 +-- store/list.go | 14 +++-- store/multistoreproof.go | 4 +- store/multistoreproof_test.go | 2 + store/queue.go | 4 +- store/queue_test.go | 6 +- store/rootmultistore.go | 39 ++++++------ store/rootmultistore_test.go | 4 +- types/decimal_test.go | 8 +-- x/auth/account_test.go | 6 +- x/auth/client/txbuilder/txbuilder.go | 5 +- x/auth/feekeeper.go | 4 +- x/auth/mapper.go | 4 +- x/auth/stdtx.go | 3 +- x/bank/client/cli/broadcast.go | 3 +- x/bank/client/rest/broadcast.go | 3 +- x/distribution/keeper/delegation.go | 4 +- x/distribution/keeper/genesis.go | 4 +- x/distribution/keeper/keeper.go | 8 +-- x/distribution/keeper/test_common.go | 2 +- x/distribution/keeper/validator.go | 4 +- x/gov/client/rest/rest.go | 2 +- x/gov/keeper.go | 30 ++++----- x/gov/keeper_test.go | 8 +-- x/gov/queryable.go | 4 +- x/gov/tally.go | 2 +- x/ibc/client/cli/relay.go | 6 +- x/ibc/mapper.go | 8 +-- x/mint/keeper.go | 4 +- x/slashing/client/cli/query.go | 2 +- x/slashing/client/rest/query.go | 2 +- x/slashing/keeper.go | 4 +- x/slashing/signing_info.go | 8 +-- x/slashing/slashing_period.go | 4 +- x/stake/handler.go | 4 +- x/stake/handler_test.go | 18 +++--- x/stake/keeper/delegation.go | 12 ++-- x/stake/keeper/keeper.go | 16 ++--- x/stake/keeper/val_state_change.go | 2 +- x/stake/keeper/validator.go | 8 +-- x/stake/types/delegation.go | 20 +++--- x/stake/types/params.go | 6 +- x/stake/types/pool.go | 6 +- x/stake/types/validator.go | 4 +- 68 files changed, 295 insertions(+), 299 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 764d481f8..16e01163c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -752,7 +752,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging) BREAKING CHANGES -* [stake] MarshalJSON -> MarshalBinary +* [stake] MarshalJSON -> MarshalBinaryLengthPrefixed * Queries against the store must be prefixed with the path "/store" FEATURES diff --git a/Gopkg.lock b/Gopkg.lock index 68350442d..2a2aad42f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -10,6 +10,7 @@ revision = "48b08affede2cea076a3cf13b2e3f72ed262b743" [[projects]] + branch = "master" digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" packages = ["."] @@ -38,7 +39,7 @@ name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "2a560b2036bee5e3679ec2133eb6520b2f195213" + revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" [[projects]] digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" @@ -62,13 +63,6 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" -[[projects]] - digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b" - name = "github.com/ebuchman/fail-test" - packages = ["."] - pruneopts = "UT" - revision = "95f809107225be108efcf10a3509e4ea6ceef3c4" - [[projects]] digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" @@ -171,12 +165,13 @@ version = "v1.2.0" [[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", + "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -245,12 +240,12 @@ version = "v1.0.0" [[projects]] - digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3" + digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" name = "github.com/mitchellh/mapstructure" packages = ["."] pruneopts = "UT" - revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad" - version = "v1.1.1" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" [[projects]] digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" @@ -296,7 +291,7 @@ [[projects]] branch = "master" - digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" + digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -304,11 +299,11 @@ "model", ] pruneopts = "UT" - revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" + revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" [[projects]] branch = "master" - digest = "1:ef1dd9945e58ee9b635273d28c0ef3fa3742a7dedc038ebe207fd63e6ce000ef" + digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" name = "github.com/prometheus/procfs" packages = [ ".", @@ -317,7 +312,7 @@ "xfs", ] pruneopts = "UT" - revision = "418d78d0b9a7b7de3a6bbc8a23def624cc977bb2" + revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" [[projects]] digest = "1:ea0700160aca4ef099f4e06686a665a87691f4248dddd40796925eda2e46bd64" @@ -346,12 +341,12 @@ version = "v1.1.2" [[projects]] - digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" + digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" name = "github.com/spf13/cast" packages = ["."] pruneopts = "UT" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" + revision = "8c9545af88b134710ab1cd196795e7f2388358d7" + version = "v1.3.0" [[projects]] digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" @@ -370,12 +365,12 @@ version = "v1.0.0" [[projects]] - digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" + digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" name = "github.com/spf13/pflag" packages = ["."] pruneopts = "UT" - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" [[projects]] digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" @@ -424,35 +419,23 @@ revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" [[projects]] - branch = "master" - digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" - name = "github.com/tendermint/ed25519" - packages = [ - ".", - "edwards25519", - "extra25519", - ] - pruneopts = "UT" - revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" - -[[projects]] - digest = "1:2c971a45c89ca2ccc735af50919cdee05fbdc54d4bf50625073693300e31ead8" + digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "UT" - revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" - version = "v0.12.0" + revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12" + version = "v0.14.0" [[projects]] - digest = "1:53397098d6acb7613358683cc84ae59281a60c6033f0bff62fa8d3f279c6c430" + digest = "1:9f8c4c93658315a795ffd3e0c943d39f78067dd8382b8d7bcfaf6686b92f3978" name = "github.com/tendermint/iavl" packages = ["."] pruneopts = "UT" - revision = "3acc91fb8811db2c5409a855ae1f8e441fe98e2d" - version = "v0.11.0" + revision = "fa74114f764f9827c4ad5573f990ed25bf8c4bac" + version = "v0.11.1" [[projects]] - digest = "1:f9c7a1f3ee087476f4883c33cc7c1bdbe56b9670b2fb27855ea2f386393272f5" + digest = "1:aeef94796024739a6d2fb3df26450a1e3d9dbaeee0e7d896b180b1ae6a7e342e" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -485,6 +468,7 @@ "libs/db", "libs/errors", "libs/events", + "libs/fail", "libs/flowrate", "libs/log", "libs/pubsub", @@ -505,7 +489,6 @@ "rpc/core", "rpc/core/types", "rpc/grpc", - "rpc/lib", "rpc/lib/client", "rpc/lib/server", "rpc/lib/types", @@ -518,8 +501,8 @@ "version", ] pruneopts = "UT" - revision = "90eda9bfb6e6daeed1c8015df41cb36772d91778" - version = "v0.25.1-rc0" + revision = "c086d0a34102bd42873d20445673ea1d18a539cd" + version = "v0.26.0" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" @@ -530,13 +513,15 @@ version = "v0.1.0" [[projects]] - digest = "1:aaff04fa01d9b824fde6799759cc597b3ac3671b9ad31924c28b6557d0ee5284" + digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d" name = "golang.org/x/crypto" packages = [ "bcrypt", "blowfish", "chacha20poly1305", "curve25519", + "ed25519", + "ed25519/internal/edwards25519", "hkdf", "internal/chacha20", "internal/subtle", diff --git a/Gopkg.toml b/Gopkg.toml index 0902bc412..85fa6603c 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,24 +1,3 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - [[constraint]] name = "github.com/bgentry/speakeasy" version = "~0.1.0" @@ -49,15 +28,15 @@ [[override]] name = "github.com/tendermint/go-amino" - version = "=v0.12.0" + version = "v0.14.0" [[override]] name = "github.com/tendermint/iavl" - version = "=v0.11.0" + version = "=v0.11.1" [[override]] name = "github.com/tendermint/tendermint" - version = "=0.25.1-rc0" + version = "v0.26.0" ## deps without releases: @@ -89,7 +68,6 @@ version = "1.0.0" ## transitive deps, without releases: -# [[override]] name = "github.com/syndtr/goleveldb" diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 17942b976..a2b2f4166 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -337,7 +337,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc } // Encode with json - value := codec.Cdc.MustMarshalBinary(result) + value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) return abci.ResponseQuery{ Code: uint32(sdk.ABCICodeOK), Value: value, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index ff6cbb834..9f6414214 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -358,7 +358,7 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { if len(txBytes) == 0 { return nil, sdk.ErrTxDecode("txBytes are empty") } - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) } @@ -455,7 +455,7 @@ func TestCheckTx(t *testing.T) { for i := int64(0); i < nTxs; i++ { tx := newTxCounter(i, 0) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) r := app.CheckTx(txBytes) assert.True(t, r.IsOK(), fmt.Sprintf("%v", r)) @@ -503,7 +503,7 @@ func TestDeliverTx(t *testing.T) { for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -544,7 +544,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { { app.BeginBlock(abci.RequestBeginBlock{}) tx := newTxCounter(0, 0, 1, 2) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -565,7 +565,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { tx := newTxCounter(1, 3) tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{1}) - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -638,7 +638,7 @@ func TestSimulateTx(t *testing.T) { require.Equal(t, gasConsumed, result.GasUsed) // simulate by calling Query with encoded tx - txBytes, err := cdc.MarshalBinary(tx) + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) require.Nil(t, err) query := abci.RequestQuery{ Path: "/app/simulate", @@ -648,7 +648,7 @@ func TestSimulateTx(t *testing.T) { require.True(t, queryResult.IsOK(), queryResult.Log) var res sdk.Result - codec.Cdc.MustUnmarshalBinary(queryResult.Value, &res) + codec.Cdc.MustUnmarshalBinaryLengthPrefixed(queryResult.Value, &res) require.Nil(t, err, "Result unmarshalling failed") require.True(t, res.IsOK(), res.Log) require.Equal(t, gasConsumed, res.GasUsed, res.Log) @@ -729,7 +729,7 @@ func TestRunInvalidTransaction(t *testing.T) { registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) - txBytes, err := newCdc.MarshalBinary(tx) + txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) require.EqualValues(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), res.Code) diff --git a/client/context/context.go b/client/context/context.go index ce36c37a8..9108b3d0b 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -121,8 +121,13 @@ func createVerifier() tmlite.Verifier { fmt.Printf("Must specify these options: %s when --trust-node is false\n", errMsg.String()) os.Exit(1) } + node := rpcclient.NewHTTP(nodeURI, "/websocket") - verifier, err := tmliteProxy.NewVerifier(chainID, filepath.Join(home, ".gaialite"), node, log.NewNopLogger()) + cacheSize := 10 // TODO: determine appropriate cache size + verifier, err := tmliteProxy.NewVerifier( + chainID, filepath.Join(home, ".gaialite"), + node, log.NewNopLogger(), cacheSize, + ) if err != nil { fmt.Printf("Create verifier failed: %s\n", err.Error()) diff --git a/client/context/query.go b/client/context/query.go index 8fca9becf..06e24211a 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,7 +10,6 @@ import ( "strings" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" @@ -54,7 +53,7 @@ func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sd return res, err } - ctx.Codec.MustUnmarshalBinary(resRaw, &res) + ctx.Codec.MustUnmarshalBinaryLengthPrefixed(resRaw, &res) return } @@ -157,8 +156,8 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro } opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Trusted: ctx.TrustNode, + Height: ctx.Height, + Prove: !ctx.TrustNode, } result, err := node.ABCIQueryWithOptions(path, key, opts) @@ -203,32 +202,33 @@ func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { return fmt.Errorf("missing valid certifier to verify data from distrusted node") } - // the AppHash for height H is in header H+1 - commit, err := ctx.Verify(resp.Height + 1) - if err != nil { - return err - } + // TODO: handle in another TM v0.26 update PR + // // the AppHash for height H is in header H+1 + // commit, err := ctx.Verify(resp.Height + 1) + // if err != nil { + // return err + // } - var multiStoreProof store.MultiStoreProof - cdc := codec.New() + // var multiStoreProof store.MultiStoreProof + // cdc := codec.New() - err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof) - if err != nil { - return errors.Wrap(err, "failed to unmarshalBinary rangeProof") - } + // err = cdc.UnmarshalBinaryLengthPrefixed(resp.Proof, &multiStoreProof) + // if err != nil { + // return errors.Wrap(err, "failed to unmarshalBinary rangeProof") + // } - // verify the substore commit hash against trusted appHash - substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( - multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, - ) - if err != nil { - return errors.Wrap(err, "failed in verifying the proof against appHash") - } + // // verify the substore commit hash against trusted appHash + // substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( + // multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, + // ) + // if err != nil { + // return errors.Wrap(err, "failed in verifying the proof against appHash") + // } - err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) - if err != nil { - return errors.Wrap(err, "failed in the range proof verification") - } + // err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) + // if err != nil { + // return errors.Wrap(err, "failed in the range proof verification") + // } return nil } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index cb25721e8..d3140e5b8 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -158,11 +158,11 @@ func TestNodeStatus(t *testing.T) { res, body := Request(t, port, "GET", "/node_info", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var nodeInfo p2p.NodeInfo + var nodeInfo p2p.DefaultNodeInfo err := cdc.UnmarshalJSON([]byte(body), &nodeInfo) require.Nil(t, err, "Couldn't parse node info") - require.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res) + require.NotEqual(t, p2p.DefaultNodeInfo{}, nodeInfo, "res: %v", res) // syncing res, body = Request(t, port, "GET", "/syncing", nil) diff --git a/client/tx/query.go b/client/tx/query.go index da94404f2..3bbbf9a13 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -125,7 +125,7 @@ type Info struct { func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { var tx auth.StdTx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, err } diff --git a/client/utils/utils.go b/client/utils/utils.go index 9d20f0ab8..fee7c7fe2 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -167,7 +167,7 @@ func adjustGasEstimate(estimate int64, adjustment float64) int64 { func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) { var simulationResult sdk.Result - if err := cdc.UnmarshalBinary(rawRes, &simulationResult); err != nil { + if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil { return 0, err } return simulationResult.GasUsed, nil diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go index 731ded903..c3bf31569 100644 --- a/client/utils/utils_test.go +++ b/client/utils/utils_test.go @@ -12,7 +12,7 @@ import ( func TestParseQueryResponse(t *testing.T) { cdc := app.MakeCodec() - sdkResBytes := cdc.MustMarshalBinary(sdk.Result{GasUsed: 10}) + sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10}) gas, err := parseQueryResponse(cdc, sdkResBytes) assert.Equal(t, gas, int64(10)) assert.Nil(t, err) @@ -28,7 +28,7 @@ func TestCalculateGas(t *testing.T) { if wantErr { return nil, errors.New("") } - return cdc.MustMarshalBinary(sdk.Result{GasUsed: gasUsed}), nil + return cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: gasUsed}), nil } } type args struct { diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 21ef855f0..87972dc37 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -259,7 +259,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci if err != nil { panic(err) } - bz := app.cdc.MustMarshalBinary(tx) + bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx) res := app.BaseApp.DeliverTx(bz) if !res.IsOK() { panic(res.Log) diff --git a/cmd/gaia/cmd/gaiadebug/main.go b/cmd/gaia/cmd/gaiadebug/main.go index 96887376c..240a74cb4 100644 --- a/cmd/gaia/cmd/gaiadebug/main.go +++ b/cmd/gaia/cmd/gaiadebug/main.go @@ -220,7 +220,7 @@ func runTxCmd(cmd *cobra.Command, args []string) error { var tx = auth.StdTx{} cdc := gaia.MakeCodec() - err = cdc.UnmarshalBinary(txBytes, &tx) + err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return err } diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 86ae5c157..0bd500fee 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -273,7 +273,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t if err != nil { return nil, nil, err } - cdc.MustUnmarshalBinary([]byte(signed), sig) + cdc.MustUnmarshalBinaryLengthPrefixed([]byte(signed), sig) return sig, linfo.GetPubKey(), nil } sig, err = priv.Sign(msg) diff --git a/crypto/keys/types.go b/crypto/keys/types.go index ff90c3205..eeb4fdfcf 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -182,11 +182,11 @@ func (i offlineInfo) GetAddress() types.AccAddress { // encoding info func writeInfo(i Info) []byte { - return cdc.MustMarshalBinary(i) + return cdc.MustMarshalBinaryLengthPrefixed(i) } // decoding info func readInfo(bz []byte) (info Info, err error) { - err = cdc.UnmarshalBinary(bz, &info) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &info) return } diff --git a/docs/sdk/core/app2.md b/docs/sdk/core/app2.md index d8040a850..b0e42fd10 100644 --- a/docs/sdk/core/app2.md +++ b/docs/sdk/core/app2.md @@ -178,7 +178,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode(err.Error()) } diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index c1ae43666..24458384c 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -201,7 +201,7 @@ func (tx app2Tx) GetSignature() []byte { func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode(err.Error()) } diff --git a/docs/sdk/core/examples/app2_test.go b/docs/sdk/core/examples/app2_test.go index 913903488..c59dec264 100644 --- a/docs/sdk/core/examples/app2_test.go +++ b/docs/sdk/core/examples/app2_test.go @@ -39,7 +39,7 @@ func TestEncoding(t *testing.T) { cdc := NewCodec() testTxDecoder := tx2Decoder(cdc) - encodedSendTx, err := cdc.MarshalBinary(sendTxBefore) + encodedSendTx, err := cdc.MarshalBinaryLengthPrefixed(sendTxBefore) require.Nil(t, err, "Error encoding sendTx") @@ -69,7 +69,7 @@ func TestEncoding(t *testing.T) { Signature: sig, } - encodedIssueTx, err2 := cdc.MarshalBinary(issueTxBefore) + encodedIssueTx, err2 := cdc.MarshalBinaryLengthPrefixed(issueTxBefore) require.Nil(t, err2, "Error encoding issueTx") diff --git a/examples/democoin/x/oracle/keeper.go b/examples/democoin/x/oracle/keeper.go index e55cd7083..d061d2f8e 100644 --- a/examples/democoin/x/oracle/keeper.go +++ b/examples/democoin/x/oracle/keeper.go @@ -71,7 +71,7 @@ func (keeper Keeper) Info(ctx sdk.Context, p Payload) (res Info) { if bz == nil { return EmptyInfo(ctx) } - keeper.cdc.MustUnmarshalBinary(bz, &res) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } @@ -80,7 +80,7 @@ func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) { store := ctx.KVStore(keeper.key) key := GetInfoKey(p, keeper.cdc) - bz := keeper.cdc.MustMarshalBinary(info) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(info) store.Set(key, bz) } diff --git a/examples/democoin/x/oracle/keeper_keys.go b/examples/democoin/x/oracle/keeper_keys.go index 9b71aeaa1..d678692be 100644 --- a/examples/democoin/x/oracle/keeper_keys.go +++ b/examples/democoin/x/oracle/keeper_keys.go @@ -7,13 +7,13 @@ import ( // GetInfoKey returns the key for OracleInfo func GetInfoKey(p Payload, cdc *codec.Codec) []byte { - bz := cdc.MustMarshalBinary(p) + bz := cdc.MustMarshalBinaryLengthPrefixed(p) return append([]byte{0x00}, bz...) } // GetSignPrefix returns the prefix for signs func GetSignPrefix(p Payload, cdc *codec.Codec) []byte { - bz := cdc.MustMarshalBinary(p) + bz := cdc.MustMarshalBinaryLengthPrefixed(p) return append([]byte{0x01}, bz...) } diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index 7621ea860..1284f75b6 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -82,7 +82,7 @@ func getSequence(ctx sdk.Context, key sdk.StoreKey) int { if seqbz == nil { seq = 0 } else { - codec.New().MustUnmarshalBinary(seqbz, &seq) + codec.New().MustUnmarshalBinaryLengthPrefixed(seqbz, &seq) } return seq @@ -96,7 +96,7 @@ func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error { return sdk.NewError(sdk.CodespaceRoot, 1, "") } - bz := codec.New().MustMarshalBinary(seq + 1) + bz := codec.New().MustMarshalBinaryLengthPrefixed(seq + 1) store.Set([]byte("seq"), bz) return nil diff --git a/examples/democoin/x/simplestake/keeper.go b/examples/democoin/x/simplestake/keeper.go index 7bdc17937..607f61d83 100644 --- a/examples/democoin/x/simplestake/keeper.go +++ b/examples/democoin/x/simplestake/keeper.go @@ -39,7 +39,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo { return bondInfo{} } var bi bondInfo - err := k.cdc.UnmarshalBinary(bz, &bi) + err := k.cdc.UnmarshalBinaryLengthPrefixed(bz, &bi) if err != nil { panic(err) } @@ -48,7 +48,7 @@ func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo { func (k Keeper) setBondInfo(ctx sdk.Context, addr sdk.AccAddress, bi bondInfo) { store := ctx.KVStore(k.key) - bz, err := k.cdc.MarshalBinary(bi) + bz, err := k.cdc.MarshalBinaryLengthPrefixed(bi) if err != nil { panic(err) } diff --git a/server/start.go b/server/start.go index 82fbbbbeb..cf39ff71b 100644 --- a/server/start.go +++ b/server/start.go @@ -93,7 +93,6 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { return nil } -// nolint: unparam func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { cfg := ctx.Config home := cfg.RootDir @@ -135,7 +134,12 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } - // trap signal (run forever) - tmNode.RunForever() - return tmNode, nil + TrapSignal(func() { + if tmNode.IsRunning() { + _ = tmNode.Stop() + } + }) + + // run forever (the node will not be returned) + select {} } diff --git a/store/iavlstore.go b/store/iavlstore.go index d535fd436..449058df5 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -5,7 +5,6 @@ import ( "io" "sync" - "github.com/tendermint/go-amino" "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" @@ -210,44 +209,56 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Height = getHeight(tree, req) switch req.Path { - case "/store", "/key": // Get by key + case "/key": key := req.Data // Data holds the key bytes + res.Key = key if !st.VersionExists(res.Height) { res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error() break } + if req.Prove { - value, proof, err := tree.GetVersionedWithProof(key, res.Height) + value, _, err := tree.GetVersionedWithProof(key, res.Height) if err != nil { res.Log = err.Error() break } + res.Value = value - cdc := amino.NewCodec() - p, err := cdc.MarshalBinary(proof) - if err != nil { - res.Log = err.Error() - break - } - res.Proof = p + // cdc := amino.NewCodec() + + // p, err := cdc.MarshalBinaryLengthPrefixed(proof) + // if err != nil { + // res.Log = err.Error() + // break + // } + + // TODO: handle in another TM v0.26 update PR + // res.Proof = p } else { _, res.Value = tree.GetVersioned(key, res.Height) } + case "/subspace": + var KVs []KVPair + subspace := req.Data res.Key = subspace - var KVs []KVPair + iterator := sdk.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) } + iterator.Close() - res.Value = cdc.MustMarshalBinary(KVs) + res.Value = cdc.MustMarshalBinaryLengthPrefixed(KVs) + default: msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) return sdk.ErrUnknownRequest(msg).QueryResult() } + return } diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 1e9263b7b..d26cee055 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -394,9 +394,9 @@ func TestIAVLStoreQuery(t *testing.T) { {Key: k1, Value: v3}, {Key: k2, Value: v2}, } - valExpSubEmpty := cdc.MustMarshalBinary(KVs0) - valExpSub1 := cdc.MustMarshalBinary(KVs1) - valExpSub2 := cdc.MustMarshalBinary(KVs2) + valExpSubEmpty := cdc.MustMarshalBinaryLengthPrefixed(KVs0) + valExpSub1 := cdc.MustMarshalBinaryLengthPrefixed(KVs1) + valExpSub2 := cdc.MustMarshalBinaryLengthPrefixed(KVs2) cid := iavlStore.Commit() ver := cid.Version @@ -459,7 +459,7 @@ func TestIAVLStoreQuery(t *testing.T) { require.Equal(t, valExpSub2, qres.Value) // default (height 0) will show latest -1 - query0 := abci.RequestQuery{Path: "/store", Data: k1} + query0 := abci.RequestQuery{Path: "/key", Data: k1} qres = iavlStore.Query(query0) require.Equal(t, uint32(sdk.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) diff --git a/store/list.go b/store/list.go index 9905b0d6f..b38f11b80 100644 --- a/store/list.go +++ b/store/list.go @@ -42,21 +42,22 @@ func (m List) Len() (res uint64) { if bz == nil { return 0 } - m.cdc.MustUnmarshalBinary(bz, &res) + + m.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } // Get() returns the element by its index func (m List) Get(index uint64, ptr interface{}) error { bz := m.store.Get(ElemKey(index)) - return m.cdc.UnmarshalBinary(bz, ptr) + return m.cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) } // Set() stores the element to the given position // Setting element out of range will break length counting // Use Push() instead of Set() to append a new element func (m List) Set(index uint64, value interface{}) { - bz := m.cdc.MustMarshalBinary(value) + bz := m.cdc.MustMarshalBinaryLengthPrefixed(value) m.store.Set(ElemKey(index), bz) } @@ -72,7 +73,7 @@ func (m List) Delete(index uint64) { func (m List) Push(value interface{}) { length := m.Len() m.Set(length, value) - m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1)) + m.store.Set(LengthKey(), m.cdc.MustMarshalBinaryLengthPrefixed(length+1)) } // Iterate() is used to iterate over all existing elements in the list @@ -85,13 +86,16 @@ func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) for ; iter.Valid(); iter.Next() { v := iter.Value() - m.cdc.MustUnmarshalBinary(v, ptr) + m.cdc.MustUnmarshalBinaryLengthPrefixed(v, ptr) + k := iter.Key() s := string(k[len(k)-20:]) + index, err := strconv.ParseUint(s, 10, 64) if err != nil { panic(err) } + if fn(index) { break } diff --git a/store/multistoreproof.go b/store/multistoreproof.go index d62bc4aca..74ddb7148 100644 --- a/store/multistoreproof.go +++ b/store/multistoreproof.go @@ -18,7 +18,7 @@ type MultiStoreProof struct { // buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte { var rangeProof iavl.RangeProof - cdc.MustUnmarshalBinary(iavlProof, &rangeProof) + cdc.MustUnmarshalBinaryLengthPrefixed(iavlProof, &rangeProof) msp := MultiStoreProof{ StoreInfos: storeInfos, @@ -26,7 +26,7 @@ func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []store RangeProof: rangeProof, } - proof := cdc.MustMarshalBinary(msp) + proof := cdc.MustMarshalBinaryLengthPrefixed(msp) return proof } diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index 45a102cd3..a6fe95d2b 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -12,6 +12,8 @@ import ( ) func TestVerifyMultiStoreCommitInfo(t *testing.T) { + // TODO: handle in another TM v0.26 update PR + t.SkipNow() appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") diff --git a/store/queue.go b/store/queue.go index 4081f63b3..f41ecb7d7 100644 --- a/store/queue.go +++ b/store/queue.go @@ -27,12 +27,12 @@ func (m Queue) getTop() (res uint64) { return 0 } - m.List.cdc.MustUnmarshalBinary(bz, &res) + m.List.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) return } func (m Queue) setTop(top uint64) { - bz := m.List.cdc.MustMarshalBinary(top) + bz := m.List.cdc.MustMarshalBinaryLengthPrefixed(top) m.List.store.Set(TopKey(), bz) } diff --git a/store/queue_test.go b/store/queue_test.go index 5ea6d906a..58e96f56a 100644 --- a/store/queue_test.go +++ b/store/queue_test.go @@ -81,7 +81,7 @@ func TestKeys(t *testing.T) { var actual int // Checking keys.LengthKey - err := cdc.UnmarshalBinary(store.Get(LengthKey()), &len) + err := cdc.UnmarshalBinaryLengthPrefixed(store.Get(LengthKey()), &len) require.Nil(t, err) require.Equal(t, len, queue.List.Len()) @@ -89,14 +89,14 @@ func TestKeys(t *testing.T) { for i := 0; i < 10; i++ { queue.List.Get(uint64(i), &expected) bz := store.Get(ElemKey(uint64(i))) - err = cdc.UnmarshalBinary(bz, &actual) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &actual) require.Nil(t, err) require.Equal(t, expected, actual) } queue.Pop() - err = cdc.UnmarshalBinary(store.Get(TopKey()), &top) + err = cdc.UnmarshalBinaryLengthPrefixed(store.Get(TopKey()), &top) require.Nil(t, err) require.Equal(t, top, queue.getTop()) } diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 7de465f77..f4801b117 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,13 +295,13 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } - commitInfo, errMsg := getCommitInfo(rs.db, res.Height) - if errMsg != nil { - return sdk.ErrInternal(errMsg.Error()).QueryResult() - } - - res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) + // commitInfo, errMsg := getCommitInfo(rs.db, res.Height) + // if errMsg != nil { + // return sdk.ErrInternal(errMsg.Error()).QueryResult() + // } + // TODO: handle in another TM v0.26 update PR + // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) return res } @@ -386,11 +386,12 @@ type commitInfo struct { // Hash returns the simple merkle root hash of the stores sorted by name. func (ci commitInfo) Hash() []byte { - // TODO cache to ci.hash []byte - m := make(map[string]merkle.Hasher, len(ci.StoreInfos)) + // TODO: cache to ci.hash []byte + m := make(map[string][]byte, len(ci.StoreInfos)) for _, storeInfo := range ci.StoreInfos { - m[storeInfo.Name] = storeInfo + m[storeInfo.Name] = storeInfo.Hash() } + return merkle.SimpleHashFromMap(m) } @@ -422,13 +423,15 @@ type storeCore struct { func (si storeInfo) Hash() []byte { // Doesn't write Name, since merkle.SimpleHashFromMap() will // include them via the keys. - bz, _ := cdc.MarshalBinary(si.Core) // Does not error + bz, _ := cdc.MarshalBinaryLengthPrefixed(si.Core) hasher := tmhash.New() + _, err := hasher.Write(bz) if err != nil { // TODO: Handle with #870 panic(err) } + return hasher.Sum(nil) } @@ -441,16 +444,18 @@ func getLatestVersion(db dbm.DB) int64 { if latestBytes == nil { return 0 } - err := cdc.UnmarshalBinary(latestBytes, &latest) + + err := cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest) if err != nil { panic(err) } + return latest } // Set the latest version. func setLatestVersion(batch dbm.Batch, version int64) { - latestBytes, _ := cdc.MarshalBinary(version) // Does not error + latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version) batch.Set([]byte(latestVersionKey), latestBytes) } @@ -491,21 +496,19 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data") } - // Parse bytes. var cInfo commitInfo - err := cdc.UnmarshalBinary(cInfoBytes, &cInfo) + + err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) if err != nil { return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err) } + return cInfo, nil } // Set a commitInfo for given version. func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { - cInfoBytes, err := cdc.MarshalBinary(cInfo) - if err != nil { - panic(err) - } + cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo) cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) batch.Set([]byte(cInfoKey), cInfoBytes) } diff --git a/store/rootmultistore_test.go b/store/rootmultistore_test.go index d6a714c66..501c5b730 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmultistore_test.go @@ -215,7 +215,7 @@ func getExpectedCommitID(store *rootMultiStore, ver int64) CommitID { } func hashStores(stores map[StoreKey]CommitStore) []byte { - m := make(map[string]merkle.Hasher, len(stores)) + m := make(map[string][]byte, len(stores)) for key, store := range stores { name := key.Name() m[name] = storeInfo{ @@ -224,7 +224,7 @@ func hashStores(stores map[StoreKey]CommitStore) []byte { CommitID: store.LastCommitID(), // StoreType: store.GetStoreType(), }, - } + }.Hash() } return merkle.SimpleHashFromMap(m) } diff --git a/types/decimal_test.go b/types/decimal_test.go index 07329c7dc..b35894771 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -305,11 +305,11 @@ func TestSerializationGocodecJSON(t *testing.T) { func TestSerializationGocodecBinary(t *testing.T) { d := mustNewDecFromStr(t, "0.333") - bz, err := cdc.MarshalBinary(d) + bz, err := cdc.MarshalBinaryLengthPrefixed(d) require.NoError(t, err) var d2 Dec - err = cdc.UnmarshalBinary(bz, &d2) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2) require.NoError(t, err) require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) } @@ -323,11 +323,11 @@ type testDEmbedStruct struct { // TODO make work for UnmarshalJSON func TestEmbeddedStructSerializationGocodec(t *testing.T) { obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} - bz, err := cdc.MarshalBinary(obj) + bz, err := cdc.MarshalBinaryLengthPrefixed(obj) require.Nil(t, err) var obj2 testDEmbedStruct - err = cdc.UnmarshalBinary(bz, &obj2) + err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2) require.Nil(t, err) require.Equal(t, obj.Field1, obj2.Field1) diff --git a/x/auth/account_test.go b/x/auth/account_test.go index b7a78e2d2..e48060fbe 100644 --- a/x/auth/account_test.go +++ b/x/auth/account_test.go @@ -93,16 +93,16 @@ func TestBaseAccountMarshal(t *testing.T) { cdc := codec.New() codec.RegisterCrypto(cdc) - b, err := cdc.MarshalBinary(acc) + b, err := cdc.MarshalBinaryLengthPrefixed(acc) require.Nil(t, err) acc2 := BaseAccount{} - err = cdc.UnmarshalBinary(b, &acc2) + err = cdc.UnmarshalBinaryLengthPrefixed(b, &acc2) require.Nil(t, err) require.Equal(t, acc, acc2) // error on bad bytes acc2 = BaseAccount{} - err = cdc.UnmarshalBinary(b[:len(b)/2], &acc2) + err = cdc.UnmarshalBinaryLengthPrefixed(b[:len(b)/2], &acc2) require.NotNil(t, err) } diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index b488ef359..f516de451 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -125,7 +125,8 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err if err != nil { return nil, err } - return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo)) + + return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo)) } // BuildAndSign builds a single message to be signed, and signs a transaction @@ -166,7 +167,7 @@ func (bldr TxBuilder) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, erro PubKey: info.GetPubKey(), }} - return bldr.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) + return bldr.Codec.MarshalBinaryLengthPrefixed(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) } // SignStdTx appends a signature to a StdTx and returns a copy of a it. If append diff --git a/x/auth/feekeeper.go b/x/auth/feekeeper.go index a6be2e12d..0006a9185 100644 --- a/x/auth/feekeeper.go +++ b/x/auth/feekeeper.go @@ -36,12 +36,12 @@ func (fck FeeCollectionKeeper) GetCollectedFees(ctx sdk.Context) sdk.Coins { } feePool := &(sdk.Coins{}) - fck.cdc.MustUnmarshalBinary(bz, feePool) + fck.cdc.MustUnmarshalBinaryLengthPrefixed(bz, feePool) return *feePool } func (fck FeeCollectionKeeper) setCollectedFees(ctx sdk.Context, coins sdk.Coins) { - bz := fck.cdc.MustMarshalBinary(coins) + bz := fck.cdc.MustMarshalBinaryLengthPrefixed(coins) store := ctx.KVStore(fck.key) store.Set(collectedFeesKey, bz) } diff --git a/x/auth/mapper.go b/x/auth/mapper.go index dd127c731..da5481749 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -148,13 +148,13 @@ func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) int64 { if bz == nil { accNumber = 0 } else { - err := am.cdc.UnmarshalBinary(bz, &accNumber) + err := am.cdc.UnmarshalBinaryLengthPrefixed(bz, &accNumber) if err != nil { panic(err) } } - bz = am.cdc.MustMarshalBinary(accNumber + 1) + bz = am.cdc.MustMarshalBinaryLengthPrefixed(accNumber + 1) store.Set(globalAccountNumberKey, bz) return accNumber diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index ca6eb7b42..84bf88a4b 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -151,10 +151,11 @@ func DefaultTxDecoder(cdc *codec.Codec) sdk.TxDecoder { // StdTx.Msg is an interface. The concrete types // are registered by MakeTxCodec - err := cdc.UnmarshalBinary(txBytes, &tx) + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { return nil, sdk.ErrTxDecode("").TraceSDK(err.Error()) } + return tx, nil } } diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index e7e6bc16d..dd045439e 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -25,7 +25,8 @@ in place of an input filename, the command reads from standard input.`, if err != nil { return } - txBytes, err := cliCtx.Codec.MarshalBinary(stdTx) + + txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx) if err != nil { return } diff --git a/x/bank/client/rest/broadcast.go b/x/bank/client/rest/broadcast.go index c52961caf..71696f12b 100644 --- a/x/bank/client/rest/broadcast.go +++ b/x/bank/client/rest/broadcast.go @@ -22,11 +22,12 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht return } - txBytes, err := cliCtx.Codec.MarshalBinary(m.Tx) + txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } + res, err := cliCtx.BroadcastTx(txBytes) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 4dcc31a51..ac519107d 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -23,14 +23,14 @@ func (k Keeper) GetDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress, panic("Stored delegation-distribution info should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &ddi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &ddi) return } // set the delegator distribution info func (k Keeper) SetDelegationDistInfo(ctx sdk.Context, ddi types.DelegationDistInfo) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(ddi) + b := k.cdc.MustMarshalBinaryLengthPrefixed(ddi) store.Set(GetDelegationDistInfoKey(ddi.DelegatorAddr, ddi.ValOperatorAddr), b) } diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go index 06b153a51..804ea5242 100644 --- a/x/distribution/keeper/genesis.go +++ b/x/distribution/keeper/genesis.go @@ -13,7 +13,7 @@ func (k Keeper) GetAllValidatorDistInfos(ctx sdk.Context) (vdis []types.Validato for ; iterator.Valid(); iterator.Next() { var vdi types.ValidatorDistInfo - k.cdc.MustUnmarshalBinary(iterator.Value(), &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vdi) vdis = append(vdis, vdi) } return vdis @@ -27,7 +27,7 @@ func (k Keeper) GetAllDelegationDistInfos(ctx sdk.Context) (ddis []types.Delegat for ; iterator.Valid(); iterator.Next() { var ddi types.DelegationDistInfo - k.cdc.MustUnmarshalBinary(iterator.Value(), &ddi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &ddi) ddis = append(ddis, ddi) } return ddis diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index d167e0efe..a6fed9635 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -44,14 +44,14 @@ func (k Keeper) GetFeePool(ctx sdk.Context) (feePool types.FeePool) { if b == nil { panic("Stored fee pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &feePool) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &feePool) return } // set the global fee pool distribution info func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(feePool) + b := k.cdc.MustMarshalBinaryLengthPrefixed(feePool) store.Set(FeePoolKey, b) } @@ -77,14 +77,14 @@ func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) (consAddr sdk.ConsA panic("Previous proposer not set") } - k.cdc.MustUnmarshalBinary(b, &consAddr) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &consAddr) return } // get the proposer public key for this block func (k Keeper) SetPreviousProposerConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(consAddr) + b := k.cdc.MustMarshalBinaryLengthPrefixed(consAddr) store.Set(ProposerKey, b) } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index b1bd4ae4e..0a02fb245 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -172,7 +172,7 @@ func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, index := int64(0) for ; iter.Valid(); iter.Next() { var vdi types.ValidatorDistInfo - k.cdc.MustUnmarshalBinary(iter.Value(), &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vdi) if fn(index, vdi) { return } diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index d267d66b2..d2c755cfa 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -23,14 +23,14 @@ func (k Keeper) GetValidatorDistInfo(ctx sdk.Context, panic("Stored validator-distribution info should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &vdi) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &vdi) return } // set the validator distribution info func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInfo) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(vdi) + b := k.cdc.MustMarshalBinaryLengthPrefixed(vdi) store.Set(GetValidatorDistInfoKey(vdi.OperatorAddr), b) } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 374f5cc2a..842edfdac 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -292,7 +292,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han var deposit gov.Deposit cdc.UnmarshalJSON(res, &deposit) if deposit.Empty() { - res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinaryLengthPrefixed(gov.QueryProposalParams{params.ProposalID})) if err != nil || len(res) == 0 { err := errors.Errorf("proposalID [%d] does not exist", proposalID) utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index d061f1206..a3e3cda25 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -106,7 +106,7 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID int64) Proposal { } var proposal Proposal - keeper.cdc.MustUnmarshalBinary(bz, &proposal) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal) return proposal } @@ -114,7 +114,7 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID int64) Proposal { // Implements sdk.AccountKeeper. func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposal) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal) store.Set(KeyProposal(proposal.GetProposalID()), bz) } @@ -175,7 +175,7 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk if bz != nil { return ErrInvalidGenesis(keeper.codespace, "Initial ProposalID already set") } - bz = keeper.cdc.MustMarshalBinary(proposalID) + bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID) store.Set(KeyNextProposalID, bz) return nil } @@ -197,8 +197,8 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd if bz == nil { return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) - bz = keeper.cdc.MustMarshalBinary(proposalID + 1) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) + bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID + 1) store.Set(KeyNextProposalID, bz) return proposalID, nil } @@ -210,7 +210,7 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e if bz == nil { return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID) return proposalID, nil } @@ -298,13 +298,13 @@ func (keeper Keeper) GetVote(ctx sdk.Context, proposalID int64, voterAddr sdk.Ac return Vote{}, false } var vote Vote - keeper.cdc.MustUnmarshalBinary(bz, &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote) return vote, true } func (keeper Keeper) setVote(ctx sdk.Context, proposalID int64, voterAddr sdk.AccAddress, vote Vote) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(vote) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote) store.Set(KeyVote(proposalID, voterAddr), bz) } @@ -330,13 +330,13 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID int64, depositerAddr return Deposit{}, false } var deposit Deposit - keeper.cdc.MustUnmarshalBinary(bz, &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &deposit) return deposit, true } func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID int64, depositerAddr sdk.AccAddress, deposit Deposit) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(deposit) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit) store.Set(KeyDeposit(proposalID, depositerAddr), bz) } @@ -398,7 +398,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID int64) { for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := &Deposit{} - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) _, _, err := keeper.ck.AddCoins(ctx, deposit.Depositer, deposit.Amount) if err != nil { @@ -434,14 +434,14 @@ func (keeper Keeper) getActiveProposalQueue(ctx sdk.Context) ProposalQueue { } var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) return proposalQueue } func (keeper Keeper) setActiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) store.Set(KeyActiveProposalQueue, bz) } @@ -480,14 +480,14 @@ func (keeper Keeper) getInactiveProposalQueue(ctx sdk.Context) ProposalQueue { var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalQueue) return proposalQueue } func (keeper Keeper) setInactiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) + bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalQueue) store.Set(KeyInactiveProposalQueue, bz) } diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 91c41d7d7..0152c50bb 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -122,11 +122,11 @@ func TestDeposits(t *testing.T) { // Test deposit iterator depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) require.Equal(t, addrs[0], deposit.Depositer) require.Equal(t, fourSteak.Plus(fiveSteak), deposit.Amount) depositsIterator.Next() - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) require.Equal(t, addrs[1], deposit.Depositer) require.Equal(t, fourSteak, deposit.Amount) depositsIterator.Next() @@ -184,14 +184,14 @@ func TestVotes(t *testing.T) { // Test vote iterator votesIterator := keeper.GetVotes(ctx, proposalID) require.True(t, votesIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) require.True(t, votesIterator.Valid()) require.Equal(t, addrs[0], vote.Voter) require.Equal(t, proposalID, vote.ProposalID) require.Equal(t, OptionYes, vote.Option) votesIterator.Next() require.True(t, votesIterator.Valid()) - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) require.True(t, votesIterator.Valid()) require.Equal(t, addrs[1], vote.Voter) require.Equal(t, proposalID, vote.ProposalID) diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 93469a5ac..ae7c6d9c5 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -126,7 +126,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := Deposit{} - keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) deposits = append(deposits, deposit) } @@ -155,7 +155,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke votesIterator := keeper.GetVotes(ctx, params.ProposalID) for ; votesIterator.Valid(); votesIterator.Next() { vote := Vote{} - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) votes = append(votes, vote) } diff --git a/x/gov/tally.go b/x/gov/tally.go index b6e42c4b5..2bfaa7dd5 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -39,7 +39,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { vote := &Vote{} - keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), vote) + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), vote) // if validator, just record it in the map // if delegator tally voting power diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index ab7168aca..43ad783f4 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -117,7 +117,7 @@ OUTER: var processed int64 if processedbz == nil { processed = 0 - } else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil { + } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(processedbz, &processed); err != nil { panic(err) } @@ -131,7 +131,7 @@ OUTER: var egressLength int64 if egressLengthbz == nil { egressLength = 0 - } else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil { + } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(egressLengthbz, &egressLength); err != nil { panic(err) } @@ -192,7 +192,7 @@ func (c relayCommander) getSequence(node string) int64 { func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte { var packet ibc.IBCPacket - if err := c.cdc.UnmarshalBinary(bz, &packet); err != nil { + if err := c.cdc.UnmarshalBinaryLengthPrefixed(bz, &packet); err != nil { panic(err) } diff --git a/x/ibc/mapper.go b/x/ibc/mapper.go index 95c88b62f..957ab191a 100644 --- a/x/ibc/mapper.go +++ b/x/ibc/mapper.go @@ -33,13 +33,13 @@ func (ibcm Mapper) PostIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error { // write everything into the state store := ctx.KVStore(ibcm.key) index := ibcm.getEgressLength(store, packet.DestChain) - bz, err := ibcm.cdc.MarshalBinary(packet) + bz, err := ibcm.cdc.MarshalBinaryLengthPrefixed(packet) if err != nil { panic(err) } store.Set(EgressKey(packet.DestChain, index), bz) - bz, err = ibcm.cdc.MarshalBinary(index + 1) + bz, err = ibcm.cdc.MarshalBinaryLengthPrefixed(index + 1) if err != nil { panic(err) } @@ -61,7 +61,7 @@ func (ibcm Mapper) ReceiveIBCPacket(ctx sdk.Context, packet IBCPacket) sdk.Error // Functions for accessing the underlying KVStore. func marshalBinaryPanic(cdc *codec.Codec, value interface{}) []byte { - res, err := cdc.MarshalBinary(value) + res, err := cdc.MarshalBinaryLengthPrefixed(value) if err != nil { panic(err) } @@ -69,7 +69,7 @@ func marshalBinaryPanic(cdc *codec.Codec, value interface{}) []byte { } func unmarshalBinaryPanic(cdc *codec.Codec, bz []byte, ptr interface{}) { - err := cdc.UnmarshalBinary(bz, ptr) + err := cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) if err != nil { panic(err) } diff --git a/x/mint/keeper.go b/x/mint/keeper.go index 6e6a642f4..eba4c3fc1 100644 --- a/x/mint/keeper.go +++ b/x/mint/keeper.go @@ -59,14 +59,14 @@ func (k Keeper) GetMinter(ctx sdk.Context) (minter Minter) { if b == nil { panic("Stored fee pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &minter) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &minter) return } // set the minter func (k Keeper) SetMinter(ctx sdk.Context, minter Minter) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(minter) + b := k.cdc.MustMarshalBinaryLengthPrefixed(minter) store.Set(minterKey, b) } diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index 50ccc6c0e..de4fc5d57 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -34,7 +34,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { } signingInfo := new(slashing.ValidatorSigningInfo) - cdc.MustUnmarshalBinary(res, signingInfo) + cdc.MustUnmarshalBinaryLengthPrefixed(res, signingInfo) switch viper.Get(cli.OutputFlag) { diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index 99db0df63..e55b6b9be 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -45,7 +45,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code var signingInfo slashing.ValidatorSigningInfo - err = cdc.UnmarshalBinary(res, &signingInfo) + err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 647a9df09..718a02435 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -187,7 +187,7 @@ func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) { store := ctx.KVStore(k.storeKey) var pubkey crypto.PubKey - err := k.cdc.UnmarshalBinary(store.Get(getAddrPubkeyRelationKey(address)), &pubkey) + err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(getAddrPubkeyRelationKey(address)), &pubkey) if err != nil { return nil, fmt.Errorf("address %v not found", address) } @@ -196,7 +196,7 @@ func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKe func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(pubkey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(pubkey) store.Set(getAddrPubkeyRelationKey(addr), bz) } diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 8c35693f1..6479be928 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -15,7 +15,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress found = false return } - k.cdc.MustUnmarshalBinary(bz, &info) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &info) found = true return } @@ -23,7 +23,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress // Stored by *validator* address (not operator address) func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(info) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(info) store.Set(GetValidatorSigningInfoKey(address), bz) } @@ -36,14 +36,14 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con missed = false return } - k.cdc.MustUnmarshalBinary(bz, &missed) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) return } // Stored by *validator* address (not operator address) func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(missed) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed) store.Set(GetValidatorMissedBlockBitArrayKey(address, index), bz) } diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 4b1328858..026d141a2 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -61,14 +61,14 @@ func (k Keeper) addOrUpdateValidatorSlashingPeriod(ctx sdk.Context, slashingPeri SlashedSoFar: slashingPeriod.SlashedSoFar, } store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(slashingPeriodValue) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(slashingPeriodValue) store.Set(GetValidatorSlashingPeriodKey(slashingPeriod.ValidatorAddr, slashingPeriod.StartHeight), bz) } // Unmarshal key/value into a ValidatorSlashingPeriod func (k Keeper) unmarshalSlashingPeriodKeyValue(key []byte, value []byte) ValidatorSlashingPeriod { var slashingPeriodValue ValidatorSlashingPeriodValue - k.cdc.MustUnmarshalBinary(value, &slashingPeriodValue) + k.cdc.MustUnmarshalBinaryLengthPrefixed(value, &slashingPeriodValue) address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen]) startHeight := int64(binary.BigEndian.Uint64(key[1+sdk.AddrLen:1+sdk.AddrLen+8]) - uint64(stake.ValidatorUpdateDelay)) return ValidatorSlashingPeriod{ diff --git a/x/stake/handler.go b/x/stake/handler.go index 1d095f3f4..328a90133 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -201,7 +201,7 @@ func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k kee return err.Result() } - finishTime := types.MsgCdc.MustMarshalBinary(ubd.MinTime) + finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime) tags := sdk.NewTags( tags.Action, tags.ActionBeginUnbonding, @@ -219,7 +219,7 @@ func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k k return err.Result() } - finishTime := types.MsgCdc.MustMarshalBinary(red.MinTime) + finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(red.MinTime) tags := sdk.NewTags( tags.Action, tags.ActionBeginRedelegation, diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index c4a558b91..e5c78bcf6 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -94,7 +94,7 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -232,7 +232,7 @@ func TestLegacyValidatorDelegations(t *testing.T) { require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -400,7 +400,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -494,7 +494,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -540,7 +540,7 @@ func TestMultipleMsgDelegate(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -570,7 +570,7 @@ func TestJailValidator(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error: %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -586,7 +586,7 @@ func TestJailValidator(t *testing.T) { msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10)) got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper) require.True(t, got.IsOK(), "expected no error") - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) @@ -621,7 +621,7 @@ func TestValidatorQueue(t *testing.T) { got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper) require.True(t, got.IsOK(), "expected no error: %v", got) var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) origHeader := ctx.BlockHeader() @@ -709,7 +709,7 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) { // change the ctx to Block Time one second before the validator would have unbonded var finishTime time.Time - types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1)) // unbond the delegator from the validator diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 97acc2404..77f1323f2 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -163,14 +163,14 @@ func (k Keeper) GetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time) if bz == nil { return []types.DVPair{} } - k.cdc.MustUnmarshalBinary(bz, &dvPairs) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs) return dvPairs } // Sets a specific unbonding queue timeslice. func (k Keeper) SetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetUnbondingDelegationTimeKey(timestamp), bz) } @@ -199,7 +199,7 @@ func (k Keeper) DequeueAllMatureUnbondingQueue(ctx sdk.Context, currTime time.Ti unbondingTimesliceIterator := k.UnbondingQueueIterator(ctx, ctx.BlockHeader().Time) for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() { timeslice := []types.DVPair{} - k.cdc.MustUnmarshalBinary(unbondingTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(unbondingTimesliceIterator.Value(), ×lice) matureUnbonds = append(matureUnbonds, timeslice...) store.Delete(unbondingTimesliceIterator.Key()) } @@ -300,14 +300,14 @@ func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Ti if bz == nil { return []types.DVVTriplet{} } - k.cdc.MustUnmarshalBinary(bz, &dvvTriplets) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets) return dvvTriplets } // Sets a specific redelegation queue timeslice. func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetRedelegationTimeKey(timestamp), bz) } @@ -336,7 +336,7 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time) for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { timeslice := []types.DVVTriplet{} - k.cdc.MustUnmarshalBinary(redelegationTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(redelegationTimesliceIterator.Value(), ×lice) matureRedelegations = append(matureRedelegations, timeslice...) store.Delete(redelegationTimesliceIterator.Key()) } diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index 17ee31268..b61ead54c 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -60,14 +60,14 @@ func (k Keeper) GetPool(ctx sdk.Context) (pool types.Pool) { if b == nil { panic("stored pool should not have been nil") } - k.cdc.MustUnmarshalBinary(b, &pool) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &pool) return } // set the pool func (k Keeper) SetPool(ctx sdk.Context, pool types.Pool) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(pool) + b := k.cdc.MustMarshalBinaryLengthPrefixed(pool) store.Set(PoolKey, b) } @@ -80,14 +80,14 @@ func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Int) { if b == nil { return sdk.ZeroInt() } - k.cdc.MustUnmarshalBinary(b, &power) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &power) return } // Set the last total validator power. func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinary(power) + b := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(LastTotalPowerKey, b) } @@ -101,14 +101,14 @@ func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) if bz == nil { return sdk.ZeroInt() } - k.cdc.MustUnmarshalBinary(bz, &power) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) return } // Set the last validator power. func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(power) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(GetLastValidatorPowerKey(operator), bz) } @@ -128,13 +128,13 @@ func (k Keeper) GetIntraTxCounter(ctx sdk.Context) int16 { return 0 } var counter int16 - k.cdc.MustUnmarshalBinary(b, &counter) + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &counter) return counter } // set the current in-block validator operation counter func (k Keeper) SetIntraTxCounter(ctx sdk.Context, counter int16) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(counter) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(counter) store.Set(IntraTxCounterKey, bz) } diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index ea057c0fd..c259fe025 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -73,7 +73,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // calculate the new power bytes newPower := validator.BondedTokens().RoundInt64() - newPowerBytes := k.cdc.MustMarshalBinary(sdk.NewInt(newPower)) + newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(sdk.NewInt(newPower)) // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 9fd7434d3..5770ada2f 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -291,14 +291,14 @@ func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) if bz == nil { return []sdk.ValAddress{} } - k.cdc.MustUnmarshalBinary(bz, &valAddrs) + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &valAddrs) return valAddrs } // Sets a specific validator queue timeslice. func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []sdk.ValAddress) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinary(keys) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) store.Set(GetValidatorQueueTimeKey(timestamp), bz) } @@ -325,7 +325,7 @@ func (k Keeper) GetAllMatureValidatorQueue(ctx sdk.Context, currTime time.Time) validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinary(validatorTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) matureValsAddrs = append(matureValsAddrs, timeslice...) } return matureValsAddrs @@ -337,7 +337,7 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinary(validatorTimesliceIterator.Value(), ×lice) + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) for _, valAddr := range timeslice { val, found := k.GetValidator(ctx, valAddr) if !found || val.GetStatus() != sdk.Unbonding { diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 88b67018d..e73155427 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -47,7 +47,7 @@ func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { delegation.Shares, delegation.Height, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // return the delegation without fields contained within the key for the store @@ -62,7 +62,7 @@ func MustUnmarshalDelegation(cdc *codec.Codec, key, value []byte) Delegation { // return the delegation without fields contained within the key for the store func UnmarshalDelegation(cdc *codec.Codec, key, value []byte) (delegation Delegation, err error) { var storeValue delegationValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { err = fmt.Errorf("%v: %v", ErrNoDelegation(DefaultCodespace).Data(), err) return @@ -139,7 +139,7 @@ func MustMarshalUBD(cdc *codec.Codec, ubd UnbondingDelegation) []byte { ubd.InitialBalance, ubd.Balance, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a unbonding delegation from a store key and value @@ -154,7 +154,7 @@ func MustUnmarshalUBD(cdc *codec.Codec, key, value []byte) UnbondingDelegation { // unmarshal a unbonding delegation from a store key and value func UnmarshalUBD(cdc *codec.Codec, key, value []byte) (ubd UnbondingDelegation, err error) { var storeValue ubdValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } @@ -179,8 +179,8 @@ func UnmarshalUBD(cdc *codec.Codec, key, value []byte) (ubd UnbondingDelegation, // nolint func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool { - bz1 := MsgCdc.MustMarshalBinary(&d) - bz2 := MsgCdc.MustMarshalBinary(&d2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d2) return bytes.Equal(bz1, bz2) } @@ -231,7 +231,7 @@ func MustMarshalRED(cdc *codec.Codec, red Redelegation) []byte { red.SharesSrc, red.SharesDst, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a redelegation from a store key and value @@ -246,7 +246,7 @@ func MustUnmarshalRED(cdc *codec.Codec, key, value []byte) Redelegation { // unmarshal a redelegation from a store key and value func UnmarshalRED(cdc *codec.Codec, key, value []byte) (red Redelegation, err error) { var storeValue redValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } @@ -275,8 +275,8 @@ func UnmarshalRED(cdc *codec.Codec, key, value []byte) (red Redelegation, err er // nolint func (d Redelegation) Equal(d2 Redelegation) bool { - bz1 := MsgCdc.MustMarshalBinary(&d) - bz2 := MsgCdc.MustMarshalBinary(&d2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&d2) return bytes.Equal(bz1, bz2) } diff --git a/x/stake/types/params.go b/x/stake/types/params.go index 5915570c1..699758ace 100644 --- a/x/stake/types/params.go +++ b/x/stake/types/params.go @@ -49,8 +49,8 @@ func (p *Params) KeyValuePairs() params.KeyValuePairs { // Equal returns a boolean determining if two Param types are identical. func (p Params) Equal(p2 Params) bool { - bz1 := MsgCdc.MustMarshalBinary(&p) - bz2 := MsgCdc.MustMarshalBinary(&p2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p2) return bytes.Equal(bz1, bz2) } @@ -85,7 +85,7 @@ func MustUnmarshalParams(cdc *codec.Codec, value []byte) Params { // unmarshal the current staking params value from store key func UnmarshalParams(cdc *codec.Codec, value []byte) (params Params, err error) { - err = cdc.UnmarshalBinary(value, ¶ms) + err = cdc.UnmarshalBinaryLengthPrefixed(value, ¶ms) if err != nil { return } diff --git a/x/stake/types/pool.go b/x/stake/types/pool.go index e2015dcaf..4b227aa80 100644 --- a/x/stake/types/pool.go +++ b/x/stake/types/pool.go @@ -16,8 +16,8 @@ type Pool struct { // nolint func (p Pool) Equal(p2 Pool) bool { - bz1 := MsgCdc.MustMarshalBinary(&p) - bz2 := MsgCdc.MustMarshalBinary(&p2) + bz1 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p) + bz2 := MsgCdc.MustMarshalBinaryLengthPrefixed(&p2) return bytes.Equal(bz1, bz2) } @@ -90,7 +90,7 @@ func MustUnmarshalPool(cdc *codec.Codec, value []byte) Pool { // unmarshal the current pool value from store key func UnmarshalPool(cdc *codec.Codec, value []byte) (pool Pool, err error) { - err = cdc.UnmarshalBinary(value, &pool) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &pool) if err != nil { return } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 52d30f0a3..9301da3ec 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -87,7 +87,7 @@ func MustMarshalValidator(cdc *codec.Codec, validator Validator) []byte { UnbondingMinTime: validator.UnbondingMinTime, Commission: validator.Commission, } - return cdc.MustMarshalBinary(val) + return cdc.MustMarshalBinaryLengthPrefixed(val) } // unmarshal a redelegation from a store key and value @@ -106,7 +106,7 @@ func UnmarshalValidator(cdc *codec.Codec, operatorAddr, value []byte) (validator return } var storeValue validatorValue - err = cdc.UnmarshalBinary(value, &storeValue) + err = cdc.UnmarshalBinaryLengthPrefixed(value, &storeValue) if err != nil { return } From 5b74e1d0b63404d31455c4beb709d3dea2c261d1 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 19:36:35 -0800 Subject: [PATCH 2/6] Bez/tm0.26 update pt 2 redux (#2684) * Update to TM v0.26.0 * Update TODOs * Proof and verification updates * Fix linting * Fix key path creation * Temporarily fix tendermint revision to make tests pass --- Gopkg.lock | 5 +- Gopkg.toml | 2 +- client/context/query.go | 75 ++++++++----- store/iavlstore.go | 17 +-- store/multistoreproof.go | 181 +++++++++++++++++++------------ store/multistoreproof_test.go | 197 ++++++++++++++++------------------ store/rootmultistore.go | 17 ++- 7 files changed, 271 insertions(+), 223 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 2a2aad42f..f9be7bd97 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -435,7 +435,7 @@ version = "v0.11.1" [[projects]] - digest = "1:aeef94796024739a6d2fb3df26450a1e3d9dbaeee0e7d896b180b1ae6a7e342e" + digest = "1:92d7d1678577fd1a6f3348168cef87880bbc710ef5f4e9a1216f45c56567d734" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -501,8 +501,7 @@ "version", ] pruneopts = "UT" - revision = "c086d0a34102bd42873d20445673ea1d18a539cd" - version = "v0.26.0" + revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" [[projects]] digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" diff --git a/Gopkg.toml b/Gopkg.toml index 85fa6603c..a66574ea6 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -36,7 +36,7 @@ [[override]] name = "github.com/tendermint/tendermint" - version = "v0.26.0" + revision = "ebee4377b15f2958b08994485375dd2ee8a649ac" # TODO replace w/ 0.26.1 ## deps without releases: diff --git a/client/context/query.go b/client/context/query.go index 06e24211a..572a13778 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" @@ -197,38 +198,34 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) { } // verifyProof perform response proof verification. -func (ctx CLIContext) verifyProof(_ string, resp abci.ResponseQuery) error { +func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) error { if ctx.Verifier == nil { return fmt.Errorf("missing valid certifier to verify data from distrusted node") } - // TODO: handle in another TM v0.26 update PR - // // the AppHash for height H is in header H+1 - // commit, err := ctx.Verify(resp.Height + 1) - // if err != nil { - // return err - // } + // the AppHash for height H is in header H+1 + commit, err := ctx.Verify(resp.Height + 1) + if err != nil { + return err + } - // var multiStoreProof store.MultiStoreProof - // cdc := codec.New() + // TODO: Instead of reconstructing, stash on CLIContext field? + prt := store.DefaultProofRuntime() - // err = cdc.UnmarshalBinaryLengthPrefixed(resp.Proof, &multiStoreProof) - // if err != nil { - // return errors.Wrap(err, "failed to unmarshalBinary rangeProof") - // } + // TODO: Better convention for path? + storeName, err := parseQueryStorePath(queryPath) + if err != nil { + return err + } - // // verify the substore commit hash against trusted appHash - // substoreCommitHash, err := store.VerifyMultiStoreCommitInfo( - // multiStoreProof.StoreName, multiStoreProof.StoreInfos, commit.Header.AppHash, - // ) - // if err != nil { - // return errors.Wrap(err, "failed in verifying the proof against appHash") - // } + kp := merkle.KeyPath{} + kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) + kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) - // err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) - // if err != nil { - // return errors.Wrap(err, "failed in the range proof verification") - // } + err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value) + if err != nil { + return errors.Wrap(err, "failed to prove merkle proof") + } return nil } @@ -241,20 +238,40 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([ } // isQueryStoreWithProof expects a format like /// -// queryType can be app or store. +// queryType must be "store" and subpath must be "key" to require a proof. func isQueryStoreWithProof(path string) bool { if !strings.HasPrefix(path, "/") { return false } paths := strings.SplitN(path[1:], "/", 3) - if len(paths) != 3 { + switch { + case len(paths) != 3: return false - } - - if store.RequireProof("/" + paths[2]) { + case paths[0] != "store": + return false + case store.RequireProof("/" + paths[2]): return true } return false } + +// parseQueryStorePath expects a format like /store//key. +func parseQueryStorePath(path string) (storeName string, err error) { + if !strings.HasPrefix(path, "/") { + return "", errors.New("expected path to start with /") + } + + paths := strings.SplitN(path[1:], "/", 3) + switch { + case len(paths) != 3: + return "", errors.New("expected format like /store//key") + case paths[0] != "store": + return "", errors.New("expected format like /store//key") + case paths[2] != "key": + return "", errors.New("expected format like /store//key") + } + + return paths[1], nil +} diff --git a/store/iavlstore.go b/store/iavlstore.go index 449058df5..3b127357d 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -7,6 +7,7 @@ import ( "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" @@ -209,8 +210,8 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Height = getHeight(tree, req) switch req.Path { - case "/key": - key := req.Data // Data holds the key bytes + case "/key": // get by key + key := req.Data // data holds the key bytes res.Key = key if !st.VersionExists(res.Height) { @@ -224,18 +225,8 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { res.Log = err.Error() break } - res.Value = value - // cdc := amino.NewCodec() - - // p, err := cdc.MarshalBinaryLengthPrefixed(proof) - // if err != nil { - // res.Log = err.Error() - // break - // } - - // TODO: handle in another TM v0.26 update PR - // res.Proof = p + res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(key, proof).ProofOp()}} } else { _, res.Value = tree.GetVersioned(key, res.Height) } diff --git a/store/multistoreproof.go b/store/multistoreproof.go index 74ddb7148..96f0a4837 100644 --- a/store/multistoreproof.go +++ b/store/multistoreproof.go @@ -2,90 +2,139 @@ package store import ( "bytes" + "fmt" - "github.com/pkg/errors" "github.com/tendermint/iavl" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" ) // MultiStoreProof defines a collection of store proofs in a multi-store type MultiStoreProof struct { StoreInfos []storeInfo - StoreName string - RangeProof iavl.RangeProof } -// buildMultiStoreProof build MultiStoreProof based on iavl proof and storeInfos -func buildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) []byte { - var rangeProof iavl.RangeProof - cdc.MustUnmarshalBinaryLengthPrefixed(iavlProof, &rangeProof) - - msp := MultiStoreProof{ - StoreInfos: storeInfos, - StoreName: storeName, - RangeProof: rangeProof, - } - - proof := cdc.MustMarshalBinaryLengthPrefixed(msp) - return proof +func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof { + return &MultiStoreProof{StoreInfos: storeInfos} } -// VerifyMultiStoreCommitInfo verify multiStoreCommitInfo against appHash -func VerifyMultiStoreCommitInfo(storeName string, storeInfos []storeInfo, appHash []byte) ([]byte, error) { - var substoreCommitHash []byte - var height int64 - for _, storeInfo := range storeInfos { - if storeInfo.Name == storeName { - substoreCommitHash = storeInfo.Core.CommitID.Hash - height = storeInfo.Core.CommitID.Version - } - } - if len(substoreCommitHash) == 0 { - return nil, cmn.NewError("failed to get substore root commit hash by store name") - } - +// ComputeRootHash returns the root hash for a given multi-store proof. +func (proof *MultiStoreProof) ComputeRootHash() []byte { ci := commitInfo{ - Version: height, - StoreInfos: storeInfos, + Version: -1, // TODO: Not needed; improve code. + StoreInfos: proof.StoreInfos, } - if !bytes.Equal(appHash, ci.Hash()) { - return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash") - } - return substoreCommitHash, nil + return ci.Hash() } -// VerifyRangeProof verify iavl RangeProof -func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error { - - // verify the proof to ensure data integrity. - err := rangeProof.Verify(substoreCommitHash) - if err != nil { - return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash") - } - - if len(value) != 0 { - // verify existence proof - err = rangeProof.VerifyItem(key, value) - if err != nil { - return errors.Wrap(err, "failed in existence verification") - } - } else { - // verify absence proof - err = rangeProof.VerifyAbsence(key) - if err != nil { - return errors.Wrap(err, "failed in absence verification") - } - } - - return nil -} - -// RequireProof return whether proof is require for the subpath +// RequireProof returns whether proof is required for the subpath. func RequireProof(subpath string) bool { - // Currently, only when query subpath is "/store" or "/key", will proof be included in response. - // If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212 - if subpath == "/store" || subpath == "/key" { + // XXX: create a better convention. + // Currently, only when query subpath is "/key", will proof be included in + // response. If there are some changes about proof building in iavlstore.go, + // we must change code here to keep consistency with iavlStore#Query. + if subpath == "/key" { return true } + return false } + +//----------------------------------------------------------------------------- + +var _ merkle.ProofOperator = MultiStoreProofOp{} + +// the multi-store proof operation constant value +const ProofOpMultiStore = "multistore" + +// TODO: document +type MultiStoreProofOp struct { + // Encoded in ProofOp.Key + key []byte + + // To encode in ProofOp.Data. + Proof *MultiStoreProof `json:"proof"` +} + +func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp { + return MultiStoreProofOp{ + key: key, + Proof: proof, + } +} + +// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a +// given proof operation. +func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) { + if pop.Type != ProofOpMultiStore { + return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore) + } + + // XXX: a bit strange as we'll discard this, but it works + var op MultiStoreProofOp + + err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op) + if err != nil { + return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into MultiStoreProofOp") + } + + return NewMultiStoreProofOp(pop.Key, op.Proof), nil +} + +// ProofOp return a merkle proof operation from a given multi-store proof +// operation. +func (op MultiStoreProofOp) ProofOp() merkle.ProofOp { + bz := cdc.MustMarshalBinaryLengthPrefixed(op) + return merkle.ProofOp{ + Type: ProofOpMultiStore, + Key: op.key, + Data: bz, + } +} + +// String implements the Stringer interface for a mult-store proof operation. +func (op MultiStoreProofOp) String() string { + return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey()) +} + +// GetKey returns the key for a multi-store proof operation. +func (op MultiStoreProofOp) GetKey() []byte { + return op.key +} + +// Run executes a multi-store proof operation for a given value. It returns +// the root hash if the value matches all the store's commitID's hash or an +// error otherwise. +func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) { + if len(args) != 1 { + return nil, cmn.NewError("Value size is not 1") + } + + value := args[0] + root := op.Proof.ComputeRootHash() + + for _, si := range op.Proof.StoreInfos { + if si.Name == string(op.key) { + if bytes.Equal(value, si.Core.CommitID.Hash) { + return [][]byte{root}, nil + } + + return nil, cmn.NewError("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value) + } + } + + return nil, cmn.NewError("key %v not found in multistore proof", op.key) +} + +//----------------------------------------------------------------------------- + +// XXX: This should be managed by the rootMultiStore which may want to register +// more proof ops? +func DefaultProofRuntime() (prt *merkle.ProofRuntime) { + prt = merkle.NewProofRuntime() + prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) + prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) + return +} diff --git a/store/multistoreproof_test.go b/store/multistoreproof_test.go index a6fe95d2b..19feef160 100644 --- a/store/multistoreproof_test.go +++ b/store/multistoreproof_test.go @@ -1,125 +1,108 @@ package store import ( - "encoding/hex" "testing" - "github.com/stretchr/testify/assert" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/db" + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" ) -func TestVerifyMultiStoreCommitInfo(t *testing.T) { - // TODO: handle in another TM v0.26 update PR - t.SkipNow() - appHash, _ := hex.DecodeString("69959B1B4E68E0F7BD3551A50C8F849B81801AF2") - - substoreRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") - storeName := "acc" - - var storeInfos []storeInfo - - gocRootHash, _ := hex.DecodeString("62c171bb022e47d1f745608ff749e676dbd25f78") - storeInfos = append(storeInfos, storeInfo{ - Name: "gov", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: gocRootHash, - }, - }, - }) - - storeInfos = append(storeInfos, storeInfo{ - Name: "main", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: nil, - }, - }, - }) - - accRootHash, _ := hex.DecodeString("ea5d468431015c2cd6295e9a0bb1fc0e49033828") - storeInfos = append(storeInfos, storeInfo{ - Name: "acc", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: accRootHash, - }, - }, - }) - - storeInfos = append(storeInfos, storeInfo{ - Name: "ibc", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: nil, - }, - }, - }) - - stakeRootHash, _ := hex.DecodeString("987d1d27b8771d93aa3691262f661d2c85af7ca4") - storeInfos = append(storeInfos, storeInfo{ - Name: "stake", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: stakeRootHash, - }, - }, - }) - - slashingRootHash, _ := hex.DecodeString("388ee6e5b11f367069beb1eefd553491afe9d73e") - storeInfos = append(storeInfos, storeInfo{ - Name: "slashing", - Core: storeCore{ - CommitID: CommitID{ - Version: 689, - Hash: slashingRootHash, - }, - }, - }) - - commitHash, err := VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) +func TestVerifyIAVLStoreQueryProof(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + iStore, err := LoadIAVLStore(db, CommitID{}, sdk.PruneNothing) + store := iStore.(*iavlStore) require.Nil(t, err) - require.Equal(t, commitHash, substoreRootHash) + store.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() - appHash, _ = hex.DecodeString("29de216bf5e2531c688de36caaf024cd3bb09ee3") + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) - _, err = VerifyMultiStoreCommitInfo(storeName, storeInfos, appHash) - require.Error(t, err, "appHash doesn't match to the merkle root of multiStoreCommitInfo") + // Verify proof. + prt := DefaultProofRuntime() + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY_NOT", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE_NOT")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte(nil)) + require.NotNil(t, err) } -func TestVerifyRangeProof(t *testing.T) { - tree := iavl.NewMutableTree(db.NewMemDB(), 0) +func TestVerifyMultiStoreQueryProof(t *testing.T) { + // Create main tree for testing. + db := dbm.NewMemDB() + store := NewCommitMultiStore(db) + iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") - rand := cmn.NewRand() - rand.Seed(0) // for determinism - for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} { - key := []byte{ikey} - tree.Set(key, []byte(rand.Str(8))) - } + store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.LoadVersion(0) - root := tree.WorkingHash() + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := store.Commit() - key := []byte{0x32} - val, proof, err := tree.GetWithProof(key) - assert.Nil(t, err) - assert.NotEmpty(t, val) - assert.NotEmpty(t, proof) - err = VerifyRangeProof(key, val, root, proof) - assert.Nil(t, err) + // Get Proof + res := store.Query(abci.RequestQuery{ + Path: "/iavlStoreKey/key", // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(t, res.Proof) - key = []byte{0x40} - val, proof, err = tree.GetWithProof(key) - assert.Nil(t, err) - assert.Empty(t, val) - assert.NotEmpty(t, proof) - err = VerifyRangeProof(key, val, root, proof) - assert.Nil(t, err) + // Verify proof. + prt := DefaultProofRuntime() + err := prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.Nil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY_NOT", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "iavlStoreKey/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE_NOT")) + require.NotNil(t, err) + + // Verify (bad) proof. + err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) + require.NotNil(t, err) } diff --git a/store/rootmultistore.go b/store/rootmultistore.go index f4801b117..6c491bda1 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -295,10 +295,16 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { return res } - // commitInfo, errMsg := getCommitInfo(rs.db, res.Height) - // if errMsg != nil { - // return sdk.ErrInternal(errMsg.Error()).QueryResult() - // } + commitInfo, errMsg := getCommitInfo(rs.db, res.Height) + if errMsg != nil { + return sdk.ErrInternal(errMsg.Error()).QueryResult() + } + + // Restore origin path and append proof op. + res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp( + []byte(storeName), + NewMultiStoreProof(commitInfo.StoreInfos), + ).ProofOp()) // TODO: handle in another TM v0.26 update PR // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) @@ -313,11 +319,14 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) { err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) return } + paths := strings.SplitN(path[1:], "/", 2) storeName = paths[0] + if len(paths) == 2 { subpath = "/" + paths[1] } + return } From f4338d6f756d15c0fe55e6e61b6867a5d5e3b354 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 19:42:11 -0800 Subject: [PATCH 3/6] Fix merge conflict bug; Update PENDING --- PENDING.md | 2 +- store/iavlstore.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index 84a08a5ab..2c3c3cd1d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -12,7 +12,7 @@ BREAKING CHANGES * [simulation] \#2665 only argument to simulation.Invariant is now app * Tendermint - + * Upgrade to version 0.26.0 FEATURES diff --git a/store/iavlstore.go b/store/iavlstore.go index 3b127357d..bb588f029 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -220,7 +220,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } if req.Prove { - value, _, err := tree.GetVersionedWithProof(key, res.Height) + value, proof, err := tree.GetVersionedWithProof(key, res.Height) if err != nil { res.Log = err.Error() break From c20fcbfd8f6f3c4be406f10d38d3eb1bfcdd4ced Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 5 Nov 2018 04:26:46 +0000 Subject: [PATCH 4/6] New genesis workflow (#2602) New genesis workflow: * `gaiad init` is now used to generate an empty `genesis.json`. * Genesis accounts need to be populated manually before running `gaiad collect-gentxs`. * This should support starfish too, see #2615 for more info. * Closes: #2596 #2615 * Validate validator address and address against respective account ex ante * Fix local testnet failures * New genesis tests * Run make format * Add --pubkey flag * gaiad collect-gentxs takes no args --- PENDING.md | 1 + client/lcd/test_helpers.go | 11 +- cmd/gaia/app/app.go | 3 +- cmd/gaia/app/genesis.go | 166 ++++++++++--------- cmd/gaia/app/genesis_test.go | 44 ++++-- cmd/gaia/cli_test/cli_test.go | 41 ++++- cmd/gaia/cmd/gaiad/main.go | 3 +- cmd/gaia/init/collect.go | 119 ++++++++++++++ cmd/gaia/init/gentx.go | 44 ++++-- cmd/gaia/init/init.go | 201 +++++------------------- cmd/gaia/init/init_test.go | 1 - cmd/gaia/init/testnet.go | 67 ++++++-- examples/basecoin/cmd/basecoind/main.go | 13 +- examples/democoin/cmd/democoind/main.go | 17 +- server/init.go | 9 +- server/mock/app.go | 7 +- server/mock/app_test.go | 3 +- x/stake/client/cli/flags.go | 16 +- x/stake/client/cli/tx.go | 8 +- 19 files changed, 463 insertions(+), 311 deletions(-) create mode 100644 cmd/gaia/init/collect.go diff --git a/PENDING.md b/PENDING.md index 2c3c3cd1d..9acbf325b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -7,6 +7,7 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * Gaia + * [gaiad init] \#2602 New genesis workflow * SDK * [simulation] \#2665 only argument to simulation.Invariant is now app diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 2a81239ac..39c76865c 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -214,6 +214,7 @@ func InitializeTestLCD( genTxs := []json.RawMessage{} // append any additional (non-proposing) validators + var accs []gapp.GenesisAccount for i := 0; i < nValidators; i++ { operPrivKey := secp256k1.GenPrivKey() operAddr := operPrivKey.PubKey().Address() @@ -242,9 +243,17 @@ func InitializeTestLCD( genTxs = append(genTxs, txBytes) valConsPubKeys = append(valConsPubKeys, pubKey) valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) + + accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(operAddr)) + accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("steak", 150)} + accs = append(accs, gapp.NewGenesisAccount(&accAuth)) } - genesisState, err := gapp.GaiaAppGenState(cdc, genTxs) + appGenState := gapp.NewDefaultGenesisState() + appGenState.Accounts = accs + genDoc.AppState, err = cdc.MarshalJSON(appGenState) + require.NoError(t, err) + genesisState, err := gapp.GaiaAppGenState(cdc, *genDoc, genTxs) require.NoError(t, err) // add some tokens to init accounts diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 87972dc37..5f1f61f1e 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -273,7 +273,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // sanity check if len(req.Validators) > 0 { if len(req.Validators) != len(validators) { - panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d) ", len(req.Validators), len(validators))) + panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d)", + len(req.Validators), len(validators))) } sort.Sort(abci.ValidatorUpdates(req.Validators)) sort.Sort(abci.ValidatorUpdates(validators)) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index df28bcf6c..cfbbe5cc2 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -26,6 +26,7 @@ var ( // bonded tokens given to genesis validators/accounts freeFermionVal = int64(100) freeFermionsAcc = sdk.NewInt(150) + bondDenom = "steak" ) // State to Unmarshal @@ -90,58 +91,60 @@ func GaiaAppInit() server.AppInit { // Create the core parameters for genesis initialization for gaia // note that the pubkey input is this machines pubkey -func GaiaAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) { - if len(appGenTxs) == 0 { - err = errors.New("must provide at least genesis transaction") - return +func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + genesisState GenesisState, err error) { + + if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { + return genesisState, err } - // start with the default staking genesis state - stakeData := stake.DefaultGenesisState() - slashingData := slashing.DefaultGenesisState() - - // get genesis flag account information - genaccs := make([]GenesisAccount, len(appGenTxs)) + // if there are no gen txs to be processed, return the default empty state + if len(appGenTxs) == 0 { + return genesisState, errors.New("there must be at least one genesis tx") + } + stakeData := genesisState.StakeData for i, genTx := range appGenTxs { var tx auth.StdTx - err = cdc.UnmarshalJSON(genTx, &tx) - if err != nil { - return + if err := cdc.UnmarshalJSON(genTx, &tx); err != nil { + return genesisState, err } msgs := tx.GetMsgs() if len(msgs) != 1 { - err = errors.New("must provide genesis StdTx with exactly 1 CreateValidator message") - return + return genesisState, errors.New( + "must provide genesis StdTx with exactly 1 CreateValidator message") + } + if _, ok := msgs[0].(stake.MsgCreateValidator); !ok { + return genesisState, fmt.Errorf( + "Genesis transaction %v does not contain a MsgCreateValidator", i) } - msg := msgs[0].(stake.MsgCreateValidator) - - // create the genesis account, give'm few steaks and a buncha token with there name - genaccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply } - // create the final app state - genesisState = GenesisState{ - Accounts: genaccs, - StakeData: stakeData, + for _, acc := range genesisState.Accounts { + // create the genesis account, give'm few steaks and a buncha token with there name + for _, coin := range acc.Coins { + if coin.Denom == bondDenom { + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens. + Add(sdk.NewDecFromInt(coin.Amount)) // increase the supply + } + } + } + genesisState.StakeData = stakeData + genesisState.GenTxs = appGenTxs + return genesisState, nil +} + +// NewDefaultGenesisState generates the default state for gaia. +func NewDefaultGenesisState() GenesisState { + return GenesisState{ + Accounts: nil, + StakeData: stake.DefaultGenesisState(), MintData: mint.DefaultGenesisState(), DistrData: distr.DefaultGenesisState(), GovData: gov.DefaultGenesisState(), - SlashingData: slashingData, - GenTxs: appGenTxs, + SlashingData: slashing.DefaultGenesisState(), + GenTxs: nil, } - - return -} - -func genesisAccountFromMsgCreateValidator(msg stake.MsgCreateValidator, amount sdk.Int) GenesisAccount { - accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr)) - accAuth.Coins = []sdk.Coin{ - {msg.Description.Moniker + "Token", sdk.NewInt(1000)}, - {"steak", amount}, - } - return NewGenesisAccount(&accAuth) } // GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants @@ -175,27 +178,43 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) { } // GaiaAppGenState but with JSON -func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func GaiaAppGenStateJSON(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { // create the final app state - genesisState, err := GaiaAppGenState(cdc, appGenTxs) + genesisState, err := GaiaAppGenState(cdc, genDoc, appGenTxs) if err != nil { return nil, err } - appState, err = codec.MarshalJSONIndent(cdc, genesisState) - return + return codec.MarshalJSONIndent(cdc, genesisState) } -// CollectStdTxs processes and validates application's genesis StdTxs and returns the list of validators, -// appGenTxs, and persistent peers required to generate genesis.json. -func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( - validators []tmtypes.GenesisValidator, appGenTxs []auth.StdTx, persistentPeers string, err error) { +// CollectStdTxs processes and validates application's genesis StdTxs and returns +// the list of appGenTxs, and persistent peers required to generate genesis.json. +func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) ( + appGenTxs []auth.StdTx, persistentPeers string, err error) { + var fos []os.FileInfo fos, err = ioutil.ReadDir(genTxsDir) if err != nil { - return + return appGenTxs, persistentPeers, err } - var addresses []string + // prepare a map of all accounts in genesis state to then validate + // against the validators addresses + var appState GenesisState + if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { + return appGenTxs, persistentPeers, err + } + addrMap := make(map[string]GenesisAccount, len(appState.Accounts)) + for i := 0; i < len(appState.Accounts); i++ { + acc := appState.Accounts[i] + strAddr := string(acc.Address) + addrMap[strAddr] = acc + } + + // addresses and IPs (and port) validator server info + var addressesIPs []string + for _, fo := range fos { filename := filepath.Join(genTxsDir, fo.Name()) if !fo.IsDir() && (filepath.Ext(filename) != ".json") { @@ -204,48 +223,55 @@ func CollectStdTxs(moniker string, genTxsDir string, cdc *codec.Codec) ( // get the genStdTx var jsonRawTx []byte - jsonRawTx, err = ioutil.ReadFile(filename) - if err != nil { - return + if jsonRawTx, err = ioutil.ReadFile(filename); err != nil { + return appGenTxs, persistentPeers, err } var genStdTx auth.StdTx - err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx) - if err != nil { - return + if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil { + return appGenTxs, persistentPeers, err } appGenTxs = append(appGenTxs, genStdTx) - nodeAddr := genStdTx.GetMemo() - if len(nodeAddr) == 0 { - err = fmt.Errorf("couldn't find node's address in %s", fo.Name()) - return + // the memo flag is used to store + // the ip and node-id, for example this may be: + // "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656" + nodeAddrIP := genStdTx.GetMemo() + if len(nodeAddrIP) == 0 { + return appGenTxs, persistentPeers, fmt.Errorf( + "couldn't find node's address and IP in %s", fo.Name()) } + // genesis transactions must be single-message msgs := genStdTx.GetMsgs() if len(msgs) != 1 { - err = errors.New("each genesis transaction must provide a single genesis message") - return + + return appGenTxs, persistentPeers, errors.New( + "each genesis transaction must provide a single genesis message") } - // TODO: this could be decoupled from stake.MsgCreateValidator - // TODO: and we likely want to do it for real world Gaia + // validate the validator address and funds against the accounts in the state msg := msgs[0].(stake.MsgCreateValidator) - validators = append(validators, tmtypes.GenesisValidator{ - PubKey: msg.PubKey, - Power: freeFermionVal, - Name: msg.Description.Moniker, - }) + addr := string(sdk.AccAddress(msg.ValidatorAddr)) + acc, ok := addrMap[addr] + if !ok { + return appGenTxs, persistentPeers, fmt.Errorf( + "account %v not in genesis.json: %+v", addr, addrMap) + } + if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) { + err = fmt.Errorf("insufficient fund for the delegation: %s < %s", + acc.Coins.AmountOf(msg.Delegation.Denom), msg.Delegation.Amount) + } // exclude itself from persistent peers if msg.Description.Moniker != moniker { - addresses = append(addresses, nodeAddr) + addressesIPs = append(addressesIPs, nodeAddrIP) } } - sort.Strings(addresses) - persistentPeers = strings.Join(addresses, ",") + sort.Strings(addressesIPs) + persistentPeers = strings.Join(addressesIPs, ",") - return + return appGenTxs, persistentPeers, nil } func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 1acc9f393..6c331856c 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -1,11 +1,13 @@ package app import ( + "encoding/json" + "github.com/tendermint/tendermint/crypto/secp256k1" + tmtypes "github.com/tendermint/tendermint/types" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/stretchr/testify/require" @@ -27,7 +29,8 @@ var ( func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { // start with the default staking genesis state - stakeData := stake.DefaultGenesisState() + appState := NewDefaultGenesisState() + stakeData := appState.StakeData genAccs := make([]GenesisAccount, len(genTxs)) for i, genTx := range genTxs { @@ -35,17 +38,15 @@ func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState { require.Equal(t, 1, len(msgs)) msg := msgs[0].(stake.MsgCreateValidator) - // get genesis flag account information - genAccs[i] = genesisAccountFromMsgCreateValidator(msg, freeFermionsAcc) - stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDecFromInt(freeFermionsAcc)) // increase the supply + acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddr)) + acc.Coins = sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)} + genAccs[i] = NewGenesisAccount(&acc) + stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewDec(150)) // increase the supply } // create the final app state - return GenesisState{ - Accounts: genAccs, - StakeData: stakeData, - GovData: gov.DefaultGenesisState(), - } + appState.Accounts = genAccs + return appState } func TestToAccount(t *testing.T) { @@ -68,6 +69,19 @@ func TestGaiaAppGenTx(t *testing.T) { func TestGaiaAppGenState(t *testing.T) { cdc := MakeCodec() _ = cdc + var genDoc tmtypes.GenesisDoc + + // test unmarshalling error + _, err := GaiaAppGenState(cdc, genDoc, []json.RawMessage{}) + require.Error(t, err) + + appState := makeGenesisState(t, []auth.StdTx{}) + genDoc.AppState, err = json.Marshal(appState) + require.NoError(t, err) + + // test validation error + _, err = GaiaAppGenState(cdc, genDoc, []json.RawMessage{}) + require.Error(t, err) // TODO test must provide at least genesis transaction // TODO test with both one and two genesis transactions: @@ -77,7 +91,8 @@ func TestGaiaAppGenState(t *testing.T) { func makeMsg(name string, pk crypto.PubKey) auth.StdTx { desc := stake.NewDescription(name, "", "", "") comm := stakeTypes.CommissionMsg{} - msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin("steak", 50), desc, comm) + msg := stake.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(bondDenom, + 50), desc, comm) return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "") } @@ -106,3 +121,10 @@ func TestGaiaGenesisValidation(t *testing.T) { err = GaiaValidateGenesisState(genesisState) require.NotNil(t, err) } + +func TestNewDefaultGenesisAccount(t *testing.T) { + addr := secp256k1.GenPrivKeySecp256k1([]byte("")).PubKey().Address() + acc := NewDefaultGenesisAccount(sdk.AccAddress(addr)) + require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("fooToken")) + require.Equal(t, sdk.NewInt(150), acc.Coins.AmountOf(bondDenom)) +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index b4791bf12..c80b597c7 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -5,9 +5,11 @@ package clitest import ( "encoding/json" "fmt" + "github.com/tendermint/tendermint/types" "io/ioutil" "os" "path" + "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -207,7 +209,7 @@ func TestGaiaCLIGasAuto(t *testing.T) { func TestGaiaCLICreateValidator(t *testing.T) { chainID, servAddr, port := initializeFixtures(t) - flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) + flags := fmt.Sprintf("--home=%s --chain-id=%v --node=%s", gaiacliHome, chainID, servAddr) // start gaiad server proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) @@ -544,7 +546,7 @@ func TestGaiaCLIConfig(t *testing.T) { servAddr, port, err := server.FreeTCPAddr() require.NoError(t, err) node := fmt.Sprintf("%s:%s", servAddr, port) - chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + chainID := executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y") config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) require.NoError(t, err) @@ -594,12 +596,27 @@ func initializeFixtures(t *testing.T) (chainID, servAddr, port string) { tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - - chainID = executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) - + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf( + "gaiacli keys show foo --output=json --home=%s", gaiacliHome)) + chainID = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) + genFile := filepath.Join(gaiadHome, "config", "genesis.json") + genDoc := readGenesisFile(t, genFile) + var appState app.GenesisState + err := codec.Cdc.UnmarshalJSON(genDoc.AppState, &appState) + require.NoError(t, err) + appState.Accounts = []app.GenesisAccount{app.NewDefaultGenesisAccount(fooAddr)} + appStateJSON, err := codec.Cdc.MarshalJSON(appState) + require.NoError(t, err) + genDoc.AppState = appStateJSON + genDoc.SaveAs(genFile) + executeWrite(t, fmt.Sprintf( + "gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome), + app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiad collect-gentxs --home=%s", gaiadHome), app.DefaultKeyPass) // get a free port, also setup some common flags - servAddr, port, err := server.FreeTCPAddr() + servAddr, port, err = server.FreeTCPAddr() require.NoError(t, err) return } @@ -618,6 +635,18 @@ func writeToNewTempFile(t *testing.T, s string) *os.File { return fp } +func readGenesisFile(t *testing.T, genFile string) types.GenesisDoc { + var genDoc types.GenesisDoc + fp, err := os.Open(genFile) + require.NoError(t, err) + fileContents, err := ioutil.ReadAll(fp) + require.NoError(t, err) + defer fp.Close() + err = codec.Cdc.UnmarshalJSON(fileContents, &genDoc) + require.NoError(t, err) + return genDoc +} + //___________________________________________________________________________________ // executors diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index ed777bf47..ae076571b 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -39,7 +39,8 @@ func main() { } appInit := app.GaiaAppInit() rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc, appInit)) - rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) + rootCmd.AddCommand(gaiaInit.CollectGenTxsCmd(ctx, cdc)) + rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, server.AppInit{})) rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) server.AddCommands(ctx, cdc, rootCmd, appInit, diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go new file mode 100644 index 000000000..34b65e560 --- /dev/null +++ b/cmd/gaia/init/collect.go @@ -0,0 +1,119 @@ +package init + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/go-amino" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/types" +) + +type initConfig struct { + ChainID string + GenTxsDir string + Name string + NodeID string + ValPubKey crypto.PubKey +} + +// nolint +func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "collect-gentxs", + Short: "Collect genesis txs and output a genesis.json file", + RunE: func(_ *cobra.Command, _ []string) error { + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + + name := viper.GetString(client.FlagName) + nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) + if err != nil { + return err + } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + + toPrint := printInfo{ + Moniker: config.Moniker, + ChainID: genDoc.ChainID, + NodeID: nodeID, + } + + initCfg := initConfig{ + ChainID: genDoc.ChainID, + GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), + Name: name, + NodeID: nodeID, + ValPubKey: valPubKey, + } + appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) + if err != nil { + return err + } + + toPrint.AppMessage = appMessage + // print out some key information + return displayInfo(cdc, toPrint) + }, + } + + cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") + return cmd +} + +func genAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig, + genDoc types.GenesisDoc) (appState json.RawMessage, err error) { + + genFile := config.GenesisFile() + // process genesis transactions, else create default genesis.json + var appGenTxs []auth.StdTx + var persistentPeers string + var genTxs []json.RawMessage + var jsonRawTx json.RawMessage + + appGenTxs, persistentPeers, err = app.CollectStdTxs( + cdc, config.Moniker, initCfg.GenTxsDir, genDoc) + if err != nil { + return + } + genTxs = make([]json.RawMessage, len(appGenTxs)) + config.P2P.PersistentPeers = persistentPeers + for i, stdTx := range appGenTxs { + jsonRawTx, err = cdc.MarshalJSON(stdTx) + if err != nil { + return + } + genTxs[i] = jsonRawTx + } + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs) + if err != nil { + return + } + + err = WriteGenesisFile(genFile, initCfg.ChainID, nil, appState) + return +} + +func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { + genContents, err := ioutil.ReadFile(genFile) + if err != nil { + return + } + err = cdc.UnmarshalJSON(genContents, &genDoc) + return +} diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 1b24a3576..7ed820375 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -55,9 +55,20 @@ following delegation and commission default parameters: if err != nil { return err } + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + // Read --pubkey, if empty take it from priv_validator.json + if valPubKeyString := viper.GetString(cli.FlagPubKey); valPubKeyString != "" { + valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString) + if err != nil { + return err + } + } // Run gaiad tx create-validator - prepareFlagsForTxCreateValidator(config, nodeID, ip, valPubKey) + prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) createValidatorCmd := cli.GetCmdCreateValidator(cdc) w, err := ioutil.TempFile("", "gentx") @@ -82,28 +93,41 @@ following delegation and commission default parameters: }, } + cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") + cmd.Flags().AddFlagSet(cli.FsCommissionCreate) + cmd.Flags().AddFlagSet(cli.FsAmount) + cmd.Flags().AddFlagSet(cli.FsPk) cmd.MarkFlagRequired(client.FlagName) return cmd } -func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip string, valPubKey crypto.PubKey) { - viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home +func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID string, + valPubKey crypto.PubKey) { + viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome)) // --home + viper.Set(client.FlagChainID, chainID) viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) // --from viper.Set(cli.FlagNodeID, nodeID) // --node-id viper.Set(cli.FlagIP, ip) // --ip viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) // --pubkey - viper.Set(cli.FlagAmount, defaultAmount) // --amount - viper.Set(cli.FlagCommissionRate, defaultCommissionRate) - viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) - viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) - viper.Set(cli.FlagGenesisFormat, true) // --genesis-format - viper.Set(cli.FlagMoniker, config.Moniker) // --moniker + viper.Set(cli.FlagGenesisFormat, true) // --genesis-format + viper.Set(cli.FlagMoniker, config.Moniker) // --moniker if config.Moniker == "" { viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName)) } + if viper.GetString(cli.FlagAmount) == "" { + viper.Set(cli.FlagAmount, defaultAmount) + } + if viper.GetString(cli.FlagCommissionRate) == "" { + viper.Set(cli.FlagCommissionRate, defaultCommissionRate) + } + if viper.GetString(cli.FlagCommissionMaxRate) == "" { + viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate) + } + if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" { + viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + } } func prepareFlagsForTxSign() { diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 467ea3fc2..0290ae4fb 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -2,24 +2,19 @@ package init import ( "encoding/json" - "errors" "fmt" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/x/auth" - authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/privval" "os" "path/filepath" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" @@ -27,26 +22,11 @@ import ( ) const ( - flagWithTxs = "with-txs" - flagOverwrite = "overwrite" - flagClientHome = "home-client" - flagOverwriteKey = "overwrite-key" - flagSkipGenesis = "skip-genesis" - flagMoniker = "moniker" + flagOverwrite = "overwrite" + flagClientHome = "home-client" + flagMoniker = "moniker" ) -type initConfig struct { - ChainID string - GenTxsDir string - Name string - NodeID string - ClientHome string - WithTxs bool - Overwrite bool - OverwriteKey bool - ValPubKey crypto.PubKey -} - type printInfo struct { Moniker string `json:"moniker"` ChainID string `json:"chain_id"` @@ -70,22 +50,16 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob cmd := &cobra.Command{ Use: "init", Short: "Initialize private validator, p2p, genesis, and application configuration files", - Long: `Initialize validators's and node's configuration files. - -Note that only node's configuration files will be written if the flag --skip-genesis is -enabled, and the genesis file will not be generated. -`, - Args: cobra.NoArgs, + Long: `Initialize validators's and node's configuration files.`, + Args: cobra.NoArgs, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) - - name := viper.GetString(client.FlagName) chainID := viper.GetString(client.FlagChainID) if chainID == "" { chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6)) } - nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) + nodeID, _, err := InitializeNodeValidatorFiles(config) if err != nil { return err } @@ -93,37 +67,26 @@ enabled, and the genesis file will not be generated. if viper.GetString(flagMoniker) != "" { config.Moniker = viper.GetString(flagMoniker) } - if config.Moniker == "" && name != "" { - config.Moniker = name - } - toPrint := printInfo{ - ChainID: chainID, - Moniker: config.Moniker, - NodeID: nodeID, - } - if viper.GetBool(flagSkipGenesis) { - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - return displayInfo(cdc, toPrint) - } - initCfg := initConfig{ - ChainID: chainID, - GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"), - Name: name, - NodeID: nodeID, - ClientHome: viper.GetString(flagClientHome), - WithTxs: viper.GetBool(flagWithTxs), - Overwrite: viper.GetBool(flagOverwrite), - OverwriteKey: viper.GetBool(flagOverwriteKey), - ValPubKey: valPubKey, + var appState json.RawMessage + genFile := config.GenesisFile() + if appState, err = initializeEmptyGenesis(cdc, genFile, chainID, + viper.GetBool(flagOverwrite)); err != nil { + return err } - appMessage, err := initWithConfig(cdc, config, initCfg) - // print out some key information - if err != nil { + if err = WriteGenesisFile(genFile, chainID, nil, appState); err != nil { return err } - toPrint.AppMessage = appMessage + toPrint := printInfo{ + ChainID: chainID, + Moniker: config.Moniker, + NodeID: nodeID, + AppMessage: appState, + } + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return displayInfo(cdc, toPrint) }, } @@ -131,12 +94,7 @@ enabled, and the genesis file will not be generated. cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/") - cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") - cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag") - cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key") - cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json") + cmd.Flags().String(flagMoniker, "", "set the validator's moniker") return cmd } @@ -151,105 +109,6 @@ func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey return } -func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) ( - appMessage json.RawMessage, err error) { - genFile := config.GenesisFile() - if !initCfg.Overwrite && common.FileExists(genFile) { - err = fmt.Errorf("genesis.json file already exists: %v", genFile) - return - } - - // process genesis transactions, else create default genesis.json - var appGenTxs []auth.StdTx - var persistentPeers string - var genTxs []json.RawMessage - var appState json.RawMessage - var jsonRawTx json.RawMessage - chainID := initCfg.ChainID - - if initCfg.WithTxs { - _, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc) - if err != nil { - return - } - genTxs = make([]json.RawMessage, len(appGenTxs)) - config.P2P.PersistentPeers = persistentPeers - for i, stdTx := range appGenTxs { - jsonRawTx, err = cdc.MarshalJSON(stdTx) - if err != nil { - return - } - genTxs[i] = jsonRawTx - } - } else { - var ip, keyPass, secret string - var addr sdk.AccAddress - var signedTx auth.StdTx - - if initCfg.Name == "" { - err = errors.New("must specify validator's moniker (--name)") - return - } - - config.Moniker = initCfg.Name - ip, err = server.ExternalIP() - if err != nil { - return - } - memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip) - buf := client.BufferStdin() - prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.DefaultKeyPass) - keyPass, err = client.GetPassword(prompt, buf) - if err != nil && keyPass != "" { - // An error was returned that either failed to read the password from - // STDIN or the given password is not empty but failed to meet minimum - // length requirements. - return - } - if keyPass == "" { - keyPass = app.DefaultKeyPass - } - - addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey) - if err != nil { - return - } - appMessage, err = json.Marshal(map[string]string{"secret": secret}) - if err != nil { - return - } - - msg := stake.NewMsgCreateValidator( - sdk.ValAddress(addr), - initCfg.ValPubKey, - sdk.NewInt64Coin("steak", 100), - stake.NewDescription(config.Moniker, "", "", ""), - stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - ) - txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID) - signedTx, err = txBldr.SignStdTx( - initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo), false, - ) - if err != nil { - return - } - jsonRawTx, err = cdc.MarshalJSON(signedTx) - if err != nil { - return - } - genTxs = []json.RawMessage{jsonRawTx} - } - - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - appState, err = app.GaiaAppGenStateJSON(cdc, genTxs) - if err != nil { - return - } - err = WriteGenesisFile(genFile, chainID, nil, appState) - - return -} - // WriteGenesisFile creates and writes the genesis configuration to disk. An // error is returned if building or writing the configuration to file fails. // nolint: unparam @@ -279,3 +138,13 @@ func ReadOrCreatePrivValidator(privValFile string) crypto.PubKey { } return privValidator.GetPubKey() } + +func initializeEmptyGenesis(cdc *codec.Codec, genFile string, chainID string, + overwrite bool) (appState json.RawMessage, err error) { + if !overwrite && common.FileExists(genFile) { + err = fmt.Errorf("genesis.json file already exists: %v", genFile) + return + } + + return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState()) +} diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index 48a5d9247..bd5274c26 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -42,7 +42,6 @@ func setupClientHome(t *testing.T) func() { clientDir, err := ioutil.TempDir("", "mock-sdk-cmd") require.Nil(t, err) viper.Set(flagClientHome, clientDir) - viper.Set(flagOverwriteKey, true) return func() { if err := os.RemoveAll(clientDir); err != nil { // TODO: Handle with #870 diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 3002b83a0..5f729b0a4 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/tendermint/tendermint/types" "net" "os" "path/filepath" @@ -35,7 +36,9 @@ var ( const nodeDirPerm = 0755 // get cmd to initialize all files for tendermint testnet and application -func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command { +func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec, + appInit server.AppInit) *cobra.Command { + cmd := &cobra.Command{ Use: "testnet", Short: "Initialize files for a Gaiad testnet", @@ -50,7 +53,7 @@ Example: `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config - return testnetWithConfig(config, cdc, appInit) + return testnetWithConfig(config, cdc) }, } cmd.Flags().Int(nValidators, 4, @@ -69,7 +72,7 @@ Example: return cmd } -func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppInit) error { +func testnetWithConfig(config *cfg.Config, cdc *codec.Codec) error { outDir := viper.GetString(outputDir) numValidators := viper.GetInt(nValidators) @@ -80,6 +83,8 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI valPubKeys := make([]crypto.PubKey, numValidators) // Generate private key, node ID, initial transaction + var accs []app.GenesisAccount + var genFiles []string for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDaemonHomeName := viper.GetString(nodeDaemonHome) @@ -115,8 +120,12 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI } memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + // write genesis + genFiles = append(genFiles, config.GenesisFile()) + buf := client.BufferStdin() - prompt := fmt.Sprintf("Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) + prompt := fmt.Sprintf( + "Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass) keyPass, err := client.GetPassword(prompt, buf) if err != nil && keyPass != "" { // An error was returned that either failed to read the password from @@ -138,12 +147,20 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI if err != nil { return err } - // Save private key seed words + // save private key seed words err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint) if err != nil { return err } + accs = append(accs, app.GenesisAccount{ + Address: addr, + Coins: sdk.Coins{ + sdk.NewInt64Coin(fmt.Sprintf("%sToken", nodeDirName), 1000), + sdk.NewInt64Coin("steak", 150), + }, + }) + msg := stake.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], @@ -173,8 +190,27 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI } } - for i := 0; i < numValidators; i++ { + // Generate empty genesis.json + appGenState := app.NewDefaultGenesisState() + appGenState.Accounts = accs + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) + if err != nil { + return err + } + genDoc := types.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + // Save all genesis.json files + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + + for i := 0; i < numValidators; i++ { nodeDirName := fmt.Sprintf("%s%d", viper.GetString(nodeDirPrefix), i) nodeDaemonHomeName := viper.GetString(nodeDaemonHome) nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName) @@ -186,16 +222,17 @@ func testnetWithConfig(config *cfg.Config, cdc *codec.Codec, appInit server.AppI nodeID, valPubKey := nodeIDs[i], valPubKeys[i] // Run `init` and generate genesis.json and config.toml initCfg := initConfig{ - ChainID: chainID, - GenTxsDir: gentxsDir, - Name: moniker, - WithTxs: true, - Overwrite: true, - OverwriteKey: false, - NodeID: nodeID, - ValPubKey: valPubKey, + ChainID: chainID, + GenTxsDir: gentxsDir, + Name: moniker, + NodeID: nodeID, + ValPubKey: valPubKey, } - if _, err := initWithConfig(cdc, config, initCfg); err != nil { + genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + if err != nil { + return err + } + if _, err := genAppStateFromConfig(cdc, config, initCfg, genDoc); err != nil { return err } } diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index fc23db78f..4f40a1450 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -41,7 +41,6 @@ func main() { appInit := server.DefaultAppInit rootCmd.AddCommand(InitCmd(ctx, cdc, appInit)) - rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc, appInit)) server.AddCommands(ctx, cdc, rootCmd, appInit, newApp, exportAppStateAndTMValidators) @@ -85,7 +84,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } - appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + appState, err := appInit.AppGenState( + cdc, tmtypes.GenesisDoc{}, []json.RawMessage{genTx}) if err != nil { return err } @@ -108,13 +108,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + []tmtypes.GenesisValidator{validator}, appStateJSON) }, } cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagChainID, "", + "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.MarkFlagRequired(client.FlagName) return cmd @@ -124,7 +126,8 @@ func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Applicatio return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) ( + json.RawMessage, []tmtypes.GenesisValidator, error) { bapp := app.NewBasecoinApp(logger, db) return bapp.ExportAppStateAndValidators() } diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index b0256b174..2f48a64f3 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -36,8 +36,9 @@ var CoolAppInit = server.AppInit{ } // coolGenAppParams sets up the app_state and appends the cool app state -func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { - appState, err = server.SimpleAppGenState(cdc, appGenTxs) +func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { + appState, err = server.SimpleAppGenState(cdc, tmtypes.GenesisDoc{}, appGenTxs) if err != nil { return } @@ -90,7 +91,8 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } - appState, err := appInit.AppGenState(cdc, []json.RawMessage{genTx}) + appState, err := appInit.AppGenState(cdc, tmtypes.GenesisDoc{}, + []json.RawMessage{genTx}) if err != nil { return err } @@ -113,13 +115,15 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return err } fmt.Fprintf(os.Stderr, "%s\n", string(out)) - return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, []tmtypes.GenesisValidator{validator}, appStateJSON) + return gaiaInit.WriteGenesisFile(config.GenesisFile(), chainID, + []tmtypes.GenesisValidator{validator}, appStateJSON) }, } cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") - cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(client.FlagChainID, "", + "genesis file chain-id, if left blank will be randomly created") cmd.Flags().String(client.FlagName, "", "validator's moniker") cmd.MarkFlagRequired(client.FlagName) return cmd @@ -129,7 +133,8 @@ func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { return app.NewDemocoinApp(logger, db) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) { +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) ( + json.RawMessage, []tmtypes.GenesisValidator, error) { dapp := app.NewDemocoinApp(logger, db) return dapp.ExportAppStateAndValidators() } diff --git a/server/init.go b/server/init.go index 6a5d821c1..75e13f452 100644 --- a/server/init.go +++ b/server/init.go @@ -19,7 +19,8 @@ import ( type AppInit struct { // AppGenState creates the core parameters initialization. It takes in a // pubkey meant to represent the pubkey of the validator of this machine. - AppGenState func(cdc *codec.Codec, appGenTx []json.RawMessage) (appState json.RawMessage, err error) + AppGenState func(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) } // SimpleGenTx is a simple genesis tx @@ -35,7 +36,8 @@ var DefaultAppInit = AppInit{ } // Generate a genesis transaction -func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { +func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) ( + appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { var addr sdk.AccAddress var secret string addr, secret, err = GenerateCoinKey() @@ -63,7 +65,8 @@ func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (appGenTx, cliPrint json } // create the genesis app state -func SimpleAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func SimpleAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( + appState json.RawMessage, err error) { if len(appGenTxs) != 1 { err = errors.New("must provide a single genesis transaction") diff --git a/server/mock/app.go b/server/mock/app.go index 18afa164e..dd8f3f5ab 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -3,6 +3,7 @@ package mock import ( "encoding/json" "fmt" + "github.com/tendermint/tendermint/types" "path/filepath" abci "github.com/tendermint/tendermint/abci/types" @@ -102,7 +103,8 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci // AppGenState can be passed into InitCmd, returns a static string of a few // key-values that can be parsed by InitChainer -func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenState(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (appState json. + RawMessage, err error) { appState = json.RawMessage(`{ "values": [ { @@ -119,7 +121,8 @@ func AppGenState(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, } // AppGenStateEmpty returns an empty transaction state for mocking. -func AppGenStateEmpty(_ *codec.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) { +func AppGenStateEmpty(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) ( + appState json.RawMessage, err error) { appState = json.RawMessage(``) return } diff --git a/server/mock/app_test.go b/server/mock/app_test.go index 05ec86521..a5f2a078b 100644 --- a/server/mock/app_test.go +++ b/server/mock/app_test.go @@ -1,6 +1,7 @@ package mock import ( + "github.com/tendermint/tendermint/types" "testing" "github.com/stretchr/testify/require" @@ -20,7 +21,7 @@ func TestInitApp(t *testing.T) { require.NoError(t, err) // initialize it future-way - appState, err := AppGenState(nil, nil) + appState, err := AppGenState(nil, types.GenesisDoc{}, nil) require.NoError(t, err) //TODO test validators in the init chain? diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index e4948f945..d571bef9e 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -35,11 +35,11 @@ const ( // common flagsets to add to various functions var ( - fsPk = flag.NewFlagSet("", flag.ContinueOnError) - fsAmount = flag.NewFlagSet("", flag.ContinueOnError) + FsPk = flag.NewFlagSet("", flag.ContinueOnError) + FsAmount = flag.NewFlagSet("", flag.ContinueOnError) fsShares = flag.NewFlagSet("", flag.ContinueOnError) fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError) - fsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) + FsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError) fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError) fsValidator = flag.NewFlagSet("", flag.ContinueOnError) @@ -48,8 +48,8 @@ var ( ) func init() { - fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220") - fsAmount.String(FlagAmount, "", "Amount of coins to bond") + FsPk.String(FlagPubKey, "", "Bech32-encoded PubKey of the validator. ") + FsAmount.String(FlagAmount, "", "Amount of coins to bond") fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") fsShares.String(FlagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") fsDescriptionCreate.String(FlagMoniker, "", "validator name") @@ -57,9 +57,9 @@ func init() { fsDescriptionCreate.String(FlagWebsite, "", "optional website") fsDescriptionCreate.String(FlagDetails, "", "optional details") fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage") - fsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") - fsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") - fsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") + FsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "validator name") fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "optional identity signature (ex. UPort or Keybase)") fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "optional website") diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index c43e1276e..3b8d8e200 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -103,10 +103,10 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsPk) - cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().AddFlagSet(FsPk) + cmd.Flags().AddFlagSet(FsAmount) cmd.Flags().AddFlagSet(fsDescriptionCreate) - cmd.Flags().AddFlagSet(fsCommissionCreate) + cmd.Flags().AddFlagSet(FsCommissionCreate) cmd.Flags().AddFlagSet(fsDelegator) cmd.Flags().Bool(FlagGenesisFormat, false, "Export the transaction in gen-tx format; it implies --generate-only") cmd.Flags().String(FlagIP, "", fmt.Sprintf("Node's public IP. It takes effect only when used in combination with --%s", FlagGenesisFormat)) @@ -204,7 +204,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { }, } - cmd.Flags().AddFlagSet(fsAmount) + cmd.Flags().AddFlagSet(FsAmount) cmd.Flags().AddFlagSet(fsValidator) return cmd From 256ec0f07b0559ab594da5cf9cae42203ccae7bc Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 5 Nov 2018 05:44:43 +0100 Subject: [PATCH 5/6] Simulation improvements (logging fix, random genesis parameters) (#2617) * Print out initial update on every block * Randomize simulation parameters * Randomize initial liveness weightings * Randomize genesis parameters * fixed power store invariant * IterateValidatorsBonded -> IterateBondedValidatorsByPower * WriteValidators uses IterateLastValidators rather than IterateBondedValidatorsByPower * fixed democoin interface Closes #2556 Closes #2396 Via #2671: closes #2669 closes #2670 closes #2620 Offshoot issues: #2618 #2619 #2620 #2661 --- PENDING.md | 5 ++ baseapp/baseapp.go | 1 + cmd/gaia/app/sim_test.go | 78 +++++++++++++++++---- examples/democoin/mock/validator.go | 9 ++- scripts/multisim.sh | 3 +- types/stake.go | 6 +- x/gov/tally.go | 2 +- x/mock/simulation/constants.go | 37 ---------- x/mock/simulation/params.go | 66 +++++++++++++++++ x/mock/simulation/random_simulate_blocks.go | 42 +++++------ x/stake/genesis.go | 2 +- x/stake/keeper/sdk_types.go | 26 ++++++- x/stake/keeper/validator.go | 14 ++++ x/stake/simulation/invariants.go | 32 ++++++--- 14 files changed, 236 insertions(+), 87 deletions(-) delete mode 100644 x/mock/simulation/constants.go create mode 100644 x/mock/simulation/params.go diff --git a/PENDING.md b/PENDING.md index 9acbf325b..ad20be35f 100644 --- a/PENDING.md +++ b/PENDING.md @@ -43,6 +43,10 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - #2556 [x/mock/simulation] Fix debugging output + - #2396 [x/mock/simulation] Change parameters to get more slashes + - #2617 [x/mock/simulation] Randomize all genesis parameters + - #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - \#1924 [x/mock/simulation] Use a transition matrix for block size - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - #2610 [x/stake] Block redelegation to and from the same validator @@ -58,6 +62,7 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia + - #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * SDK - #2625 [x/gov] fix AppendTag function usage error diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a2b2f4166..827536d21 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -394,6 +394,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger). WithMinimumFees(app.minimumFees) + // Passes the rest of the path as an argument to the querier. // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path resBytes, err := querier(ctx, path[2:], req) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 9f94a82f7..0216616a8 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "testing" + "time" "github.com/stretchr/testify/require" @@ -50,42 +51,93 @@ func init() { func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { var genesisAccounts []GenesisAccount - amt := int64(10000) + amount := int64(r.Intn(1e6)) + numInitiallyBonded := int64(r.Intn(250)) + numAccs := int64(len(accs)) + if numInitiallyBonded > numAccs { + numInitiallyBonded = numAccs + } + fmt.Printf("Selected randomly generated parameters for simulated genesis: {amount of steak per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded) // Randomly generate some genesis accounts for _, acc := range accs { - coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amt)}} + coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amount)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ Address: acc.Address, Coins: coins, }) } - // Default genesis state - govGenesis := gov.DefaultGenesisState() - stakeGenesis := stake.DefaultGenesisState() - slashingGenesis := slashing.DefaultGenesisState() + // Random genesis states + govGenesis := gov.GenesisState{ + StartingProposalID: int64(r.Intn(100)), + DepositProcedure: gov.DepositProcedure{ + MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))}, + MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + }, + VotingProcedure: gov.VotingProcedure{ + VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second, + }, + TallyingProcedure: gov.TallyingProcedure{ + Threshold: sdk.NewDecWithPrec(5, 1), + Veto: sdk.NewDecWithPrec(334, 3), + GovernancePenalty: sdk.NewDecWithPrec(1, 2), + }, + } + fmt.Printf("Selected randomly generated governance parameters: %+v\n", govGenesis) + stakeGenesis := stake.GenesisState{ + Pool: stake.InitialPool(), + Params: stake.Params{ + UnbondingTime: time.Duration(r.Intn(60*60*24*3*2)) * time.Second, + MaxValidators: uint16(r.Intn(250)), + BondDenom: "steak", + }, + } + fmt.Printf("Selected randomly generated staking parameters: %+v\n", stakeGenesis) + slashingGenesis := slashing.GenesisState{ + Params: slashing.Params{ + MaxEvidenceAge: stakeGenesis.Params.UnbondingTime, + DoubleSignUnbondDuration: time.Duration(r.Intn(60*60*24)) * time.Second, + SignedBlocksWindow: int64(r.Intn(1000)), + DowntimeUnbondDuration: time.Duration(r.Intn(86400)) * time.Second, + MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1), + SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))), + SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))), + }, + } + fmt.Printf("Selected randomly generated slashing parameters: %+v\n", slashingGenesis) + mintGenesis := mint.GenesisState{ + Minter: mint.Minter{ + InflationLastTime: time.Unix(0, 0), + Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), + }, + Params: mint.Params{ + MintDenom: "steak", + InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), + InflationMax: sdk.NewDecWithPrec(20, 2), + InflationMin: sdk.NewDecWithPrec(7, 2), + GoalBonded: sdk.NewDecWithPrec(67, 2), + }, + } + fmt.Printf("Selected randomly generated minting parameters: %v\n", mintGenesis) var validators []stake.Validator var delegations []stake.Delegation - // XXX Try different numbers of initially bonded validators - numInitiallyBonded := int64(50) valAddrs := make([]sdk.ValAddress, numInitiallyBonded) for i := 0; i < int(numInitiallyBonded); i++ { valAddr := sdk.ValAddress(accs[i].Address) valAddrs[i] = valAddr validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{}) - validator.Tokens = sdk.NewDec(amt) - validator.DelegatorShares = sdk.NewDec(amt) - delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0} + validator.Tokens = sdk.NewDec(amount) + validator.DelegatorShares = sdk.NewDec(amount) + delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } - stakeGenesis.Pool.LooseTokens = sdk.NewDec(amt*250 + (numInitiallyBonded * amt)) + stakeGenesis.Pool.LooseTokens = sdk.NewDec((amount * numAccs) + (numInitiallyBonded * amount)) stakeGenesis.Validators = validators stakeGenesis.Bonds = delegations - mintGenesis := mint.DefaultGenesisState() genesis := GenesisState{ Accounts: genesisAccounts, diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index fc00b79be..1d10c48b2 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -82,8 +82,13 @@ func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64, } } -// IterateValidatorsBonded implements sdk.ValidatorSet -func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { +// IterateBondedValidatorsByPower implements sdk.ValidatorSet +func (vs *ValidatorSet) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { + vs.IterateValidators(ctx, fn) +} + +// IterateLastValidators implements sdk.ValidatorSet +func (vs *ValidatorSet) IterateLastValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) { vs.IterateValidators(ctx, fn) } diff --git a/scripts/multisim.sh b/scripts/multisim.sh index 8ffa338b8..ff5784e4b 100755 --- a/scripts/multisim.sh +++ b/scripts/multisim.sh @@ -1,6 +1,7 @@ #!/bin/bash -seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391) +seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \ +11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827) blocks=$1 echo "Running multi-seed simulation with seeds ${seeds[@]}" diff --git a/types/stake.go b/types/stake.go index f5d3a4aae..7b10b17f8 100644 --- a/types/stake.go +++ b/types/stake.go @@ -64,7 +64,11 @@ type ValidatorSet interface { func(index int64, validator Validator) (stop bool)) // iterate through bonded validators by operator address, execute func for each validator - IterateValidatorsBonded(Context, + IterateBondedValidatorsByPower(Context, + func(index int64, validator Validator) (stop bool)) + + // iterate through the consensus validator set of the last block by operator address, execute func for each validator + IterateLastValidators(Context, func(index int64, validator Validator) (stop bool)) Validator(Context, ValAddress) Validator // get a particular validator by operator address diff --git a/x/gov/tally.go b/x/gov/tally.go index 2bfaa7dd5..e839bbe03 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -23,7 +23,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall totalVotingPower := sdk.ZeroDec() currValidators := make(map[string]validatorGovInfo) - keeper.vs.IterateValidatorsBonded(ctx, func(index int64, validator sdk.Validator) (stop bool) { + keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) { currValidators[validator.GetOperator().String()] = validatorGovInfo{ Address: validator.GetOperator(), Power: validator.GetPower(), diff --git a/x/mock/simulation/constants.go b/x/mock/simulation/constants.go deleted file mode 100644 index 2c4543b72..000000000 --- a/x/mock/simulation/constants.go +++ /dev/null @@ -1,37 +0,0 @@ -package simulation - -const ( - // Fraction of double-signing evidence from a past height - pastEvidenceFraction float64 = 0.5 - - // Minimum time per block - minTimePerBlock int64 = 1000 / 2 - - // Maximum time per block - maxTimePerBlock int64 = 1000 - - // Number of keys - numKeys int = 250 - - // Chance that double-signing evidence is found on a given block - evidenceFraction float64 = 0.5 - - // TODO Remove in favor of binary search for invariant violation - onOperation bool = false -) - -var ( - // Currently there are 3 different liveness types, fully online, spotty connection, offline. - initialLivenessWeightings = []int{40, 5, 5} - livenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{ - {90, 20, 1}, - {10, 50, 5}, - {0, 10, 1000}, - }) - // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 - blockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ - {85, 5, 0}, - {15, 92, 1}, - {0, 3, 99}, - }) -) diff --git a/x/mock/simulation/params.go b/x/mock/simulation/params.go new file mode 100644 index 000000000..404a85e54 --- /dev/null +++ b/x/mock/simulation/params.go @@ -0,0 +1,66 @@ +package simulation + +import ( + "math/rand" +) + +const ( + // Minimum time per block + minTimePerBlock int64 = 10000 / 2 + + // Maximum time per block + maxTimePerBlock int64 = 10000 + + // TODO Remove in favor of binary search for invariant violation + onOperation bool = false +) + +var ( + // Currently there are 3 different liveness types, fully online, spotty connection, offline. + defaultLivenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{ + {90, 20, 1}, + {10, 50, 5}, + {0, 10, 1000}, + }) + + // 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0 + defaultBlockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{ + {85, 5, 0}, + {15, 92, 1}, + {0, 3, 99}, + }) +) + +// Simulation parameters +type Params struct { + PastEvidenceFraction float64 + NumKeys int + EvidenceFraction float64 + InitialLivenessWeightings []int + LivenessTransitionMatrix TransitionMatrix + BlockSizeTransitionMatrix TransitionMatrix +} + +// Return default simulation parameters +func DefaultParams() Params { + return Params{ + PastEvidenceFraction: 0.5, + NumKeys: 250, + EvidenceFraction: 0.5, + InitialLivenessWeightings: []int{40, 5, 5}, + LivenessTransitionMatrix: defaultLivenessTransitionMatrix, + BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, + } +} + +// Return random simulation parameters +func RandomParams(r *rand.Rand) Params { + return Params{ + PastEvidenceFraction: r.Float64(), + NumKeys: r.Intn(250), + EvidenceFraction: r.Float64(), + InitialLivenessWeightings: []int{r.Intn(80), r.Intn(10), r.Intn(10)}, + LivenessTransitionMatrix: defaultLivenessTransitionMatrix, + BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, + } +} diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index a4da39523..5784e7a22 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -31,13 +31,13 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } -func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, +func initChain(r *rand.Rand, params Params, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) { res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { str := fmt.Sprintf("%v", validator.PubKey) - validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} + validators[str] = mockValidator{validator, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} } for i := 0; i < len(setups); i++ { @@ -65,11 +65,13 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, testingMode, t, b := getTestingMode(tb) fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) r := rand.New(rand.NewSource(seed)) + params := RandomParams(r) + fmt.Printf("Randomized simulation params: %+v\n", params) timestamp := randTimestamp(r) fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) timeDiff := maxTimePerBlock - minTimePerBlock - accs := RandomAccounts(r, numKeys) + accs := RandomAccounts(r, params.NumKeys) // Setup event stats events := make(map[string]uint) @@ -77,7 +79,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, events[what]++ } - validators := initChain(r, accs, setups, app, appStateFn) + validators := initChain(r, params, accs, setups, app, appStateFn) // Second variable to keep pending validator set (delayed one block since TM 0.24) // Initially this is the same as the initial validator set nextValidators := validators @@ -90,7 +92,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) go func() { receivedSignal := <-c - fmt.Printf("Exiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) + fmt.Printf("\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) simError = fmt.Errorf("Exited due to %s", receivedSignal) stopEarly = true }() @@ -98,7 +100,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, var pastTimes []time.Time var pastVoteInfos [][]abci.VoteInfo - request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) + request := RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, event, header) // These are operations which have been queued by previous operations operationQueue := make(map[int][]Operation) timeOperationQueue := []FutureOperation{} @@ -108,7 +110,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, blockLogBuilders = make([]*strings.Builder, numBlocks) } displayLogs := logPrinter(testingMode, blockLogBuilders) - blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) + blockSimulator := createBlockSimulator(testingMode, tb, t, params, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs) if !testingMode { b.ResetTimer() } else { @@ -181,11 +183,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, } // Generate a random RequestBeginBlock with the current validator set for the next block - request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header) + request = RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, event, header) // Update the validator set, which will be reflected in the application on the next block validators = nextValidators - nextValidators = updateValidators(tb, r, validators, res.ValidatorUpdates, event) + nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, event) } if stopEarly { DisplayEvents(events) @@ -203,7 +205,7 @@ type blockSimFn func( // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, +func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { @@ -231,7 +233,8 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { - lastBlocksizeState, blocksize = getBlockSize(r, lastBlocksizeState, avgBlockSize) + fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize) + lastBlocksizeState, blocksize = getBlockSize(r, params, lastBlocksizeState, avgBlockSize) for j := 0; j < blocksize; j++ { logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) logWriter(logUpdate) @@ -272,11 +275,10 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B // "over stuffed" blocks with average size of 2 * avgblocksize, // normal sized blocks, hitting avgBlocksize on average, // and empty blocks, with no txs / only txs scheduled from the past. -func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { - // TODO: Make blockSizeTransitionMatrix non-global +func getBlockSize(r *rand.Rand, params Params, lastBlockSizeState, avgBlockSize int) (state, blocksize int) { // TODO: Make default blocksize transition matrix actually make the average // blocksize equal to avgBlockSize. - state = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState) + state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState) if state == 0 { blocksize = r.Intn(avgBlockSize * 4) } else if state == 1 { @@ -379,7 +381,7 @@ func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.He // RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction // nolint: unparam -func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64, +func RandomRequestBeginBlock(r *rand.Rand, params Params, validators map[string]mockValidator, pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock { if len(validators) == 0 { return abci.RequestBeginBlock{Header: header} @@ -388,7 +390,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, i := 0 for _, key := range getKeys(validators) { mVal := validators[key] - mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState) + mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) signed := true if mVal.livenessState == 1 { @@ -422,11 +424,11 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, evidence := make([]abci.Evidence, 0) // Anything but the first block if len(pastTimes) > 0 { - for r.Float64() < evidenceFraction { + for r.Float64() < params.EvidenceFraction { height := header.Height time := header.Time vals := voteInfos - if r.Float64() < pastEvidenceFraction { + if r.Float64() < params.PastEvidenceFraction { height = int64(r.Intn(int(header.Height) - 1)) time = pastTimes[height] vals = pastVoteInfos[height] @@ -457,7 +459,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, // updateValidators mimicks Tendermint's update logic // nolint: unparam -func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { +func updateValidators(tb testing.TB, r *rand.Rand, params Params, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator { for _, update := range updates { str := fmt.Sprintf("%v", update.PubKey) @@ -476,7 +478,7 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida event("endblock/validatorupdates/updated") } else { // Set this new validator - current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)} + current[str] = mockValidator{update, GetMemberOfInitialState(r, params.InitialLivenessWeightings)} event("endblock/validatorupdates/added") } } diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 28c5973c7..63b038613 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -72,7 +72,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { // WriteValidators returns a slice of bonded genesis validators. func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { - keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) { + keeper.IterateLastValidators(ctx, func(_ int64, validator sdk.Validator) (stop bool) { vals = append(vals, tmtypes.GenesisValidator{ PubKey: validator.GetConsPubKey(), Power: validator.GetPower().RoundInt64(), diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 4e859a42a..1dea473f8 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -28,9 +28,31 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato } // iterate through the active validator set and perform the provided function -func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { +func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + maxValidators := k.MaxValidators(ctx) + + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid() && i < int64(maxValidators); iterator.Next() { + address := iterator.Value() + validator := k.mustGetValidator(ctx, address) + + if validator.Status == sdk.Bonded { + stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { + break + } + i++ + } + } +} + +// iterate through the active validator set and perform the provided function +func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { + iterator := k.LastValidatorsIterator(ctx) i := int64(0) for ; iterator.Valid(); iterator.Next() { address := AddressFromLastValidatorPowerKey(iterator.Key()) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 5770ada2f..edf781bb5 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -261,6 +261,13 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator return validators[:i] // trim } +// returns an iterator for the consensus validators in the last block +func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + return iterator +} + // get the current group of bonded validators sorted by power-rank func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) @@ -283,6 +290,13 @@ func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { return validators[:i] // trim } +// returns an iterator for the current validator power store +func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + return iterator +} + // gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators // that expire at a certain time. func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) { diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 8e6f18432..819bc8b37 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -1,6 +1,7 @@ package simulation import ( + "bytes" "fmt" "github.com/cosmos/cosmos-sdk/baseapp" @@ -10,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" + "github.com/cosmos/cosmos-sdk/x/stake/keeper" abci "github.com/tendermint/tendermint/abci/types" ) @@ -24,10 +26,12 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, if err != nil { return err } + err = PositivePowerInvariant(k)(app) if err != nil { return err } + err = ValidatorSetInvariant(k)(app) return err } @@ -100,19 +104,29 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, } } -// PositivePowerInvariant checks that all stored validators have > 0 power +// PositivePowerInvariant checks that all stored validators have > 0 power. func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { return func(app *baseapp.BaseApp) error { ctx := app.NewContext(false, abci.Header{}) - var err error - k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { - if !validator.GetPower().GT(sdk.ZeroDec()) { - err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetConsPubKey()) - return true + + iterator := k.ValidatorsPowerStoreIterator(ctx) + pool := k.GetPool(ctx) + + for ; iterator.Valid(); iterator.Next() { + validator, found := k.GetValidator(ctx, iterator.Value()) + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value())) } - return false - }) - return err + + powerKey := keeper.GetValidatorsByPowerIndexKey(validator, pool) + + if !bytes.Equal(iterator.Key(), powerKey) { + return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+ + "\n\tkey should be: %v\n\tkey in store: %v", validator.GetPower(), powerKey, iterator.Key()) + } + } + iterator.Close() + return nil } } From 336415baea2950ccca4a9a20005de949943f5c6f Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 4 Nov 2018 22:11:03 -0800 Subject: [PATCH 6/6] Fix simulation bugs; Incorprates #2676 from Sunny (#2677) * Fix simulation bugs; Incorprates #2676 from Sunny * Address review feedback; Update PENDING --- PENDING.md | 20 ++++++++--------- cmd/gaia/app/app.go | 14 +++++++----- x/distribution/keeper/hooks.go | 7 ++++-- x/distribution/keeper/test_common.go | 2 +- x/mock/simulation/random_simulate_blocks.go | 6 ++--- x/slashing/keeper.go | 10 ++++++++- x/slashing/test_common.go | 4 ++-- x/stake/handler.go | 25 +++++++++++++++------ x/stake/handler_test.go | 9 ++++---- x/stake/keeper/delegation.go | 7 ++++++ x/stake/keeper/keeper.go | 2 +- x/stake/keeper/slash.go | 7 +----- x/stake/keeper/slash_test.go | 24 ++++++++++---------- x/stake/keeper/val_state_change.go | 9 ++------ x/stake/keeper/validator.go | 7 ++++-- x/stake/keeper/validator_test.go | 10 +++++---- x/stake/types/errors.go | 5 +++++ x/stake/types/validator.go | 3 +++ 18 files changed, 103 insertions(+), 68 deletions(-) diff --git a/PENDING.md b/PENDING.md index ad20be35f..11c13ea75 100644 --- a/PENDING.md +++ b/PENDING.md @@ -42,15 +42,15 @@ IMPROVEMENTS * Gaia * SDK - - #2573 [x/distribution] add accum invariance - - #2556 [x/mock/simulation] Fix debugging output - - #2396 [x/mock/simulation] Change parameters to get more slashes - - #2617 [x/mock/simulation] Randomize all genesis parameters - - #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. + - \#2573 [x/distribution] add accum invariance + - \#2556 [x/mock/simulation] Fix debugging output + - \#2396 [x/mock/simulation] Change parameters to get more slashes + - \#2617 [x/mock/simulation] Randomize all genesis parameters + - \#2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store. - \#1924 [x/mock/simulation] Use a transition matrix for block size - \#2660 [x/mock/simulation] Staking transactions get tested far more frequently - - #2610 [x/stake] Block redelegation to and from the same validator - - #2652 [x/auth] Add benchmark for get and set account + - \#2610 [x/stake] Block redelegation to and from the same validator + - \#2652 [x/auth] Add benchmark for get and set account * Tendermint @@ -62,10 +62,10 @@ BUG FIXES * Gaia CLI (`gaiacli`) * Gaia - - #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` + - \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators` * SDK - - #2625 [x/gov] fix AppendTag function usage error - + - \#2625 [x/gov] fix AppendTag function usage error + - \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator * Tendermint diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 5f1f61f1e..1927d235d 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -109,7 +109,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.cdc, app.keyParams, app.tkeyParams, ) - app.stakeKeeper = stake.NewKeeper( + stakeKeeper := stake.NewKeeper( app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), @@ -117,30 +117,32 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio ) app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.paramsKeeper.Subspace(mint.DefaultParamspace), - app.stakeKeeper, app.feeCollectionKeeper, + &stakeKeeper, app.feeCollectionKeeper, ) app.distrKeeper = distr.NewKeeper( app.cdc, app.keyDistr, app.paramsKeeper.Subspace(distr.DefaultParamspace), - app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper, + app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper, app.RegisterCodespace(stake.DefaultCodespace), ) app.slashingKeeper = slashing.NewKeeper( app.cdc, app.keySlashing, - app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), + &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), app.RegisterCodespace(slashing.DefaultCodespace), ) app.govKeeper = gov.NewKeeper( app.cdc, app.keyGov, - app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, app.stakeKeeper, + app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace), ) // register the staking hooks - app.stakeKeeper = app.stakeKeeper.WithHooks( + // NOTE: stakeKeeper above are passed by reference, + // so that it can be modified like below: + app.stakeKeeper = *stakeKeeper.SetHooks( NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) // register message routes diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index efd2c0610..cdebaf93c 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -1,6 +1,8 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -38,11 +40,12 @@ func (k Keeper) onValidatorBonded(ctx sdk.Context, valAddr sdk.ValAddress) { k.onValidatorModified(ctx, valAddr) } -// XXX Consider removing this after debugging. +// Sanity check, very useful! func (k Keeper) onValidatorPowerDidChange(ctx sdk.Context, valAddr sdk.ValAddress) { vi := k.GetValidatorDistInfo(ctx, valAddr) if vi.FeePoolWithdrawalHeight != ctx.BlockHeight() { - panic("expected validator dist info FeePoolWithdrawalHeight to be updated, but was not.") + panic(fmt.Sprintf("expected validator (%v) dist info FeePoolWithdrawalHeight to be updated to %v, but was %v.", + valAddr.String(), ctx.BlockHeight(), vi.FeePoolWithdrawalHeight)) } } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 0a02fb245..660abbd0e 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -130,7 +130,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), ck, sk, fck, types.DefaultCodespace) // set the distribution hooks on staking - sk = sk.WithHooks(keeper.Hooks()) + sk.SetHooks(keeper.Hooks()) // set genesis items required for distribution keeper.SetFeePool(ctx, types.InitialFeePool()) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 5784e7a22..a568997e6 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -14,7 +14,7 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - common "github.com/tendermint/tendermint/libs/common" + cmn "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" @@ -65,7 +65,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, testingMode, t, b := getTestingMode(tb) fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) r := rand.New(rand.NewSource(seed)) - params := RandomParams(r) + params := RandomParams(r) // := DefaultParams() fmt.Printf("Randomized simulation params: %+v\n", params) timestamp := randTimestamp(r) fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) @@ -365,7 +365,7 @@ func getKeys(validators map[string]mockValidator) []string { } // randomProposer picks a random proposer from the current validator set -func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.HexBytes { +func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes { keys := getKeys(validators) if len(keys) == 0 { return nil diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 718a02435..4a52ddcb6 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -49,6 +49,15 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr)) } + // Get validator. + validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr) + if validator == nil || validator.GetStatus() == sdk.Unbonded { + // Defensive. + // Simulation doesn't take unbonding periods into account, and + // Tendermint might break this assumption at some point. + return + } + // Double sign too old maxEvidenceAge := k.MaxEvidenceAge(ctx) if age > maxEvidenceAge { @@ -80,7 +89,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, revisedFraction) // Jail validator if not already jailed - validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr) if !validator.GetJailed() { k.validatorSet.Jail(ctx, consAddr) } diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 55a6fda18..2f1113aa1 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -87,8 +87,8 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s } require.Nil(t, err) paramstore := paramsKeeper.Subspace(DefaultParamspace) - keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace) - sk = sk.WithHooks(keeper.Hooks()) + keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, DefaultCodespace) + sk.SetHooks(keeper.Hooks()) require.NotPanics(t, func() { InitGenesis(ctx, keeper, GenesisState{defaults}, genesis) diff --git a/x/stake/handler.go b/x/stake/handler.go index 328a90133..83aec00ea 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -31,11 +31,27 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } // Called every block, update validator set -func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) { +func EndBlocker(ctx sdk.Context, k keeper.Keeper) (validatorUpdates []abci.ValidatorUpdate) { endBlockerTags := sdk.EmptyTags() + // Reset the intra-transaction counter. + k.SetIntraTxCounter(ctx, 0) + + // Calculate validator set changes. + // + // NOTE: ApplyAndReturnValidatorSetUpdates has to come before + // UnbondAllMatureValidatorQueue. + // This fixes a bug when the unbonding period is instant (is the case in + // some of the tests). The test expected the validator to be completely + // unbonded after the Endblocker (go from Bonded -> Unbonding during + // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during + // UnbondAllMatureValidatorQueue). + validatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) + + // Unbond all mature validators from the unbonding queue. k.UnbondAllMatureValidatorQueue(ctx) + // Remove all mature unbonding delegations from the ubd queue. matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr) @@ -49,6 +65,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid )) } + // Remove all mature redelegations from the red queue. matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr) @@ -62,12 +79,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid tags.DstValidator, []byte(dvvTriplet.ValidatorDstAddr.String()), )) } - - // reset the intra-transaction counter - k.SetIntraTxCounter(ctx, 0) - - // calculate validator set changes - ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) return } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index e5c78bcf6..fcc268f55 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -494,11 +494,12 @@ func TestMultipleMsgCreateValidator(t *testing.T) { got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time + // Jump to finishTime for unbonding period and remove from unbonding queue types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) - //Check that the account is unbonded + // Check that the validator is deleted from state validators := keeper.GetValidators(ctx, 100) require.Equal(t, len(validatorAddrs)-(i+1), len(validators), "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) @@ -1013,7 +1014,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { EndBlocker(ctx, keeper) // validator power should have been reduced to zero - // ergo validator should have been removed from the store - _, found = keeper.GetValidator(ctx, valA) - require.False(t, found) + // validator should be in unbonding state + validator, _ = keeper.GetValidator(ctx, valA) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 77f1323f2..fbb62dcbf 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -349,6 +349,13 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Coin, validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) { + // In some situations, the exchange rate becomes invalid, e.g. if + // validator loses all tokens due to slashing. In this case, + // make all future delegations invalid. + if validator.DelegatorShareExRate().IsZero() { + return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace()) + } + // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr) if !found { diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index b61ead54c..a74f86084 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -36,7 +36,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramst } // Set the validator hooks -func (k Keeper) WithHooks(sh sdk.StakingHooks) Keeper { +func (k *Keeper) SetHooks(sh sdk.StakingHooks) *Keeper { if k.hooks != nil { panic("cannot set validator hooks twice") } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 5879428f2..e7bd72764 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -108,12 +108,6 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) k.SetPool(ctx, pool) - // remove validator if it has no more tokens - if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded { - // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // Log that a slash occurred! logger.Info(fmt.Sprintf( "validator %s slashed by slash factor of %s; burned %v tokens", @@ -236,6 +230,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re if sharesToUnbond.GT(delegation.Shares) { sharesToUnbond = delegation.Shares } + tokensToBurn, err := k.unbond(ctx, redelegation.DelegatorAddr, redelegation.ValidatorDstAddr, sharesToUnbond) if err != nil { panic(fmt.Errorf("error unbonding delegator: %v", err)) diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index aab97b811..9c23576c3 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -348,9 +348,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator // power decreased by 1 again, validator is out of stake - // ergo validator should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with a redelegation @@ -450,16 +450,16 @@ func TestSlashWithRedelegation(t *testing.T) { // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator - // validator decreased to zero power, should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator decreased to zero power, should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) // slash the validator again, by 100% // no stake remains to be slashed ctx = ctx.WithBlockHeight(12) - // validator no longer in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) // read updating redelegation @@ -472,9 +472,9 @@ func TestSlashWithRedelegation(t *testing.T) { // no more bonded tokens burned require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - // power still zero, still not in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // power still zero, still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with both an unbonding delegation and a redelegation diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index c259fe025..307270c16 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -78,8 +78,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) - // XXX Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight. - // XXX This hook probably shouldn't exist. Maybe rethink the hook system. + // Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight. + // This hook is extremely useful, otherwise lazy accum bugs will be difficult to solve. if k.hooks != nil { k.hooks.OnValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr) } @@ -108,11 +108,6 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // bonded to unbonding k.bondedToUnbonding(ctx, validator) - // remove validator if it has no more tokens - if validator.Tokens.IsZero() { - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // delete from the bonded validator index k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes)) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index edf781bb5..4646480ac 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -155,6 +155,7 @@ func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types. // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator { + pool := k.GetPool(ctx) k.DeleteValidatorByPowerIndex(ctx, validator, pool) validator, pool = validator.RemoveTokens(pool, tokensToRemove) @@ -189,6 +190,9 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { if !found { return } + if validator.Status != sdk.Unbonded { + panic("Cannot call RemoveValidator on bonded or unbonding validators") + } // delete the old validator record store := ctx.KVStore(k.storeKey) @@ -357,10 +361,9 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { if !found || val.GetStatus() != sdk.Unbonding { continue } + k.unbondingToUnbonded(ctx, val) if val.GetDelegatorShares().IsZero() { k.RemoveValidator(ctx, val.OperatorAddr) - } else { - k.unbondingToUnbonded(ctx, val) } } store.Delete(validatorTimesliceIterator.Key()) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 7acf1cc02..fe806335f 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -186,9 +186,9 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - // validator should have been deleted - _, found := keeper.GetValidator(ctx, addrVals[0]) - require.False(t, found) + // validator should be unbonding + validator, _ = keeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator @@ -276,7 +276,9 @@ func TestValidatorBasics(t *testing.T) { assert.True(ValEq(t, validators[2], resVals[2])) // remove a record - keeper.RemoveValidator(ctx, validators[1].OperatorAddr) + validators[1].Status = sdk.Unbonded // First must set to Unbonded. + keeper.SetValidator(ctx, validators[1]) // ... + keeper.RemoveValidator(ctx, validators[1].OperatorAddr) // Now it can be removed. _, found = keeper.GetValidator(ctx, addrVals[1]) require.False(t, found) } diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index d76eb39c1..1a6ed6a64 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -173,6 +173,11 @@ func ErrConflictingRedelegation(codespace sdk.CodespaceType) sdk.Error { "conflicting redelegation from this source validator to this dest validator already exists, you must wait for it to finish") } +func ErrDelegatorShareExRateInvalid(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "cannot delegate to validators with invalid (zero) ex-rate") +} + func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided") } diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 9301da3ec..ca56e0ea4 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -392,6 +392,9 @@ func (v Validator) AddTokensFromDel(pool Pool, amount sdk.Int) (Validator, Pool, pool = pool.looseTokensToBonded(amountDec) } + if exRate.IsZero() { + panic("zero exRate should not happen") + } v.Tokens = v.Tokens.Add(amountDec) issuedShares := amountDec.Quo(exRate) v.DelegatorShares = v.DelegatorShares.Add(issuedShares)