From 6b55093c756e857d48fb1d7b5de3323c0e28091e Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 13 Sep 2018 11:17:32 -0700 Subject: [PATCH 01/12] Merge PR #2324: rename wire to codec * rename wire to codec * fix formatting and cli * fix the docs --- PENDING.md | 1 + baseapp/baseapp.go | 4 +- baseapp/baseapp_test.go | 26 +++++------ client/context/context.go | 6 +-- client/context/query.go | 4 +- client/keys/{wire.go => codec.go} | 8 ++-- client/lcd/{wire.go => codec.go} | 0 client/lcd/lcd_test.go | 6 +-- client/lcd/root.go | 6 +-- client/lcd/test_helpers.go | 6 +-- client/rpc/block.go | 2 +- client/rpc/{wire.go => codec.go} | 0 client/tx/query.go | 14 +++--- client/tx/root.go | 6 +-- client/tx/search.go | 10 ++--- cmd/gaia/app/app.go | 26 +++++------ cmd/gaia/app/app_test.go | 4 +- cmd/gaia/app/genesis.go | 14 +++--- cmd/gaia/cli_test/cli_test.go | 6 +-- cmd/gaia/cmd/gaiadebug/hack.go | 22 ++++----- wire/wire.go => codec/codec.go | 6 +-- crypto/keys/{wire.go => codec.go} | 0 docs/sdk/core/app2.md | 6 +-- docs/sdk/core/examples/app2.go | 8 ++-- docs/sdk/core/examples/app3.go | 8 ++-- docs/sdk/core/examples/app4.go | 4 +- .../simple-governance/app-codec.md | 14 +++--- .../simple-governance/app-rest.md | 2 +- .../simple-governance/app-structure.md | 2 +- .../simple-governance/intro.md | 2 +- .../simple-governance/module-cli.md | 2 +- .../simple-governance/module-codec.md | 13 ++++++ .../simple-governance/module-init.md | 4 +- .../simple-governance/module-keeper.md | 2 +- .../simple-governance/module-wire.md | 13 ------ docs/spec/ibc/appendices.md | 4 +- docs/spec/ibc/channels-and-packets.md | 2 +- docs/spec/ibc/conclusion.md | 2 +- docs/spec/ibc/references.md | 2 +- examples/basecoin/app/app.go | 22 ++++----- examples/basecoin/app/app_test.go | 6 +-- examples/basecoin/types/account.go | 4 +- examples/democoin/app/app.go | 24 +++++----- examples/democoin/app/app_test.go | 4 +- examples/democoin/cmd/democoind/main.go | 4 +- examples/democoin/types/account.go | 4 +- examples/democoin/x/assoc/validator_set.go | 6 +-- .../democoin/x/assoc/validator_set_test.go | 4 +- examples/democoin/x/cool/app_test.go | 2 +- examples/democoin/x/cool/client/cli/tx.go | 6 +-- .../democoin/x/cool/{wire.go => codec.go} | 6 +-- examples/democoin/x/cool/keeper_test.go | 4 +- examples/democoin/x/oracle/README.md | 2 +- examples/democoin/x/oracle/keeper.go | 6 +-- examples/democoin/x/oracle/keeper_keys.go | 8 ++-- examples/democoin/x/oracle/oracle_test.go | 10 ++--- examples/democoin/x/pow/app_test.go | 2 +- examples/democoin/x/pow/client/cli/tx.go | 4 +- examples/democoin/x/pow/codec.go | 10 +++++ examples/democoin/x/pow/handler_test.go | 4 +- examples/democoin/x/pow/keeper_test.go | 4 +- examples/democoin/x/pow/wire.go | 10 ----- .../x/simplestake/client/cli/commands.go | 6 +-- .../x/simplestake/{wire.go => codec.go} | 6 +-- examples/democoin/x/simplestake/keeper.go | 8 ++-- .../democoin/x/simplestake/keeper_test.go | 6 +-- examples/kvstore/kvstore | Bin 20725148 -> 20725285 bytes server/export.go | 6 +-- server/export_test.go | 4 +- server/init.go | 28 ++++++------ server/init_test.go | 4 +- server/mock/app.go | 8 ++-- server/start_test.go | 4 +- server/testnet.go | 6 +-- server/tm_cmds.go | 6 +-- server/util.go | 8 ++-- server/util_test.go | 4 +- store/{types.go => codec.go} | 0 store/wire.go | 4 +- types/{wire.go => codec.go} | 4 +- types/decimal_test.go | 10 ++--- types/errors.go | 4 +- types/lib/linear.go | 10 ++--- types/lib/linear_test.go | 8 ++-- x/auth/account.go | 6 +-- x/auth/account_test.go | 13 +++--- x/auth/ante_test.go | 20 ++++----- x/auth/client/cli/account.go | 10 ++--- x/auth/client/rest/query.go | 6 +-- x/auth/client/rest/sign.go | 6 +-- x/auth/client/txbuilder/txbuilder.go | 6 +-- x/auth/codec.go | 19 ++++++++ x/auth/feekeeper.go | 8 ++-- x/auth/feekeeper_test.go | 8 ++-- x/auth/mapper.go | 8 ++-- x/auth/mapper_test.go | 4 +- x/auth/stdtx.go | 4 +- x/auth/wire.go | 19 -------- x/bank/bench_test.go | 2 +- x/bank/client/cli/sendtx.go | 4 +- x/bank/client/rest/broadcast.go | 6 +-- x/bank/client/rest/sendtx.go | 12 ++--- x/bank/codec.go | 17 +++++++ x/bank/keeper_test.go | 8 ++-- x/bank/simulation/sim_test.go | 2 +- x/bank/wire.go | 17 ------- x/distribution/keeper.go | 4 +- x/gov/client/cli/tx.go | 22 ++++----- x/gov/client/rest/rest.go | 22 ++++----- x/gov/client/rest/util.go | 8 ++-- x/gov/{wire.go => codec.go} | 8 ++-- x/gov/keeper.go | 14 +++--- x/gov/queryable.go | 16 +++---- x/gov/simulation/msgs.go | 1 - x/gov/simulation/sim_test.go | 4 +- x/gov/test_common.go | 4 +- x/ibc/app_test.go | 2 +- x/ibc/client/cli/ibctx.go | 4 +- x/ibc/client/cli/relay.go | 6 +-- x/ibc/client/rest/transfer.go | 6 +-- x/ibc/{wire.go => codec.go} | 6 +-- x/ibc/ibc_test.go | 8 ++-- x/ibc/mapper.go | 10 ++--- x/ibc/types.go | 6 +-- x/mock/app.go | 12 ++--- x/params/keeper.go | 8 ++-- x/params/keeper_test.go | 8 ++-- x/slashing/app_test.go | 2 +- x/slashing/client/cli/query.go | 6 +-- x/slashing/client/cli/tx.go | 4 +- x/slashing/client/rest/query.go | 6 +-- x/slashing/client/rest/rest.go | 4 +- x/slashing/client/rest/tx.go | 6 +-- x/slashing/codec.go | 12 +++++ x/slashing/keeper.go | 6 +-- x/slashing/msg.go | 4 +- x/slashing/test_common.go | 16 +++---- x/slashing/wire.go | 12 ----- x/stake/app_test.go | 2 +- x/stake/client/cli/query.go | 42 +++++++++--------- x/stake/client/cli/tx.go | 22 ++++----- x/stake/client/rest/query.go | 24 +++++----- x/stake/client/rest/rest.go | 4 +- x/stake/client/rest/tx.go | 8 ++-- x/stake/client/rest/utils.go | 16 +++---- x/stake/keeper/keeper.go | 6 +-- x/stake/keeper/test_common.go | 8 ++-- x/stake/simulation/sim_test.go | 2 +- x/stake/stake.go | 2 +- x/stake/types/{wire.go => codec.go} | 14 +++--- x/stake/types/delegation.go | 20 ++++----- x/stake/types/params.go | 6 +-- x/stake/types/pool.go | 6 +-- x/stake/types/validator.go | 12 ++--- x/stake/types/validator_test.go | 6 +-- 155 files changed, 614 insertions(+), 615 deletions(-) rename client/keys/{wire.go => codec.go} (70%) rename client/lcd/{wire.go => codec.go} (100%) rename client/rpc/{wire.go => codec.go} (100%) rename wire/wire.go => codec/codec.go (93%) rename crypto/keys/{wire.go => codec.go} (100%) create mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-codec.md delete mode 100644 docs/sdk/sdk-by-examples/simple-governance/module-wire.md rename examples/democoin/x/cool/{wire.go => codec.go} (53%) create mode 100644 examples/democoin/x/pow/codec.go delete mode 100644 examples/democoin/x/pow/wire.go rename examples/democoin/x/simplestake/{wire.go => codec.go} (57%) rename store/{types.go => codec.go} (100%) rename types/{wire.go => codec.go} (60%) create mode 100644 x/auth/codec.go delete mode 100644 x/auth/wire.go create mode 100644 x/bank/codec.go delete mode 100644 x/bank/wire.go rename x/gov/{wire.go => codec.go} (70%) rename x/ibc/{wire.go => codec.go} (59%) create mode 100644 x/slashing/codec.go delete mode 100644 x/slashing/wire.go rename x/stake/types/{wire.go => codec.go} (77%) diff --git a/PENDING.md b/PENDING.md index 33b60198c2..6d6754dc5a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -45,6 +45,7 @@ BREAKING CHANGES * [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211) * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) * [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308 + * [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New(). * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 10d9f55f77..1f99440142 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -14,10 +14,10 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/wire" ) // Key to store the header in the DB itself. @@ -331,7 +331,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc } // Encode with json - value := wire.Cdc.MustMarshalBinary(result) + value := codec.Cdc.MustMarshalBinary(result) return abci.ResponseQuery{ Code: uint32(sdk.ABCICodeOK), Value: value, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index b871627862..8ce69f0a1b 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -14,8 +14,8 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) var ( @@ -34,14 +34,14 @@ func defaultLogger() log.Logger { func newBaseApp(name string, options ...func(*BaseApp)) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() - codec := wire.NewCodec() + codec := codec.New() registerTestCodec(codec) return NewBaseApp(name, logger, db, testTxDecoder(codec), options...) } -func registerTestCodec(cdc *wire.Codec) { +func registerTestCodec(cdc *codec.Codec) { // register Tx, Msg - sdk.RegisterWire(cdc) + sdk.RegisterCodec(cdc) // register test types cdc.RegisterConcrete(&txTest{}, "cosmos-sdk/baseapp/txTest", nil) @@ -350,7 +350,7 @@ func (msg msgCounter2) ValidateBasic() sdk.Error { } // amino decode -func testTxDecoder(cdc *wire.Codec) sdk.TxDecoder { +func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx txTest if len(txBytes) == 0 { @@ -448,7 +448,7 @@ func TestCheckTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - codec := wire.NewCodec() + codec := codec.New() registerTestCodec(codec) for i := int64(0); i < nTxs; i++ { @@ -489,7 +489,7 @@ func TestDeliverTx(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) // Create same codec used in txDecoder - codec := wire.NewCodec() + codec := codec.New() registerTestCodec(codec) nBlocks := 3 @@ -532,7 +532,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { app := setupBaseApp(t, anteOpt, routerOpt) // Create same codec used in txDecoder - codec := wire.NewCodec() + codec := codec.New() registerTestCodec(codec) // run a multi-msg tx @@ -613,8 +613,8 @@ func TestSimulateTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - codec := wire.NewCodec() - registerTestCodec(codec) + cdc := codec.New() + registerTestCodec(cdc) nBlocks := 3 for blockN := 0; blockN < nBlocks; blockN++ { @@ -634,7 +634,7 @@ func TestSimulateTx(t *testing.T) { require.Equal(t, gasConsumed, result.GasUsed) // simulate by calling Query with encoded tx - txBytes, err := codec.MarshalBinary(tx) + txBytes, err := cdc.MarshalBinary(tx) require.Nil(t, err) query := abci.RequestQuery{ Path: "/app/simulate", @@ -644,7 +644,7 @@ func TestSimulateTx(t *testing.T) { require.True(t, queryResult.IsOK(), queryResult.Log) var res sdk.Result - wire.Cdc.MustUnmarshalBinary(queryResult.Value, &res) + codec.Cdc.MustUnmarshalBinary(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) @@ -721,7 +721,7 @@ func TestRunInvalidTransaction(t *testing.T) { tx.Msgs = append(tx.Msgs, msgNoDecode{}) // new codec so we can encode the tx, but we shouldn't be able to decode - newCdc := wire.NewCodec() + newCdc := codec.New() registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) diff --git a/client/context/context.go b/client/context/context.go index 3e785a28e2..0983c970ff 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -6,7 +6,7 @@ import ( "io" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/viper" @@ -22,7 +22,7 @@ const ctxAccStoreName = "acc" // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. type CLIContext struct { - Codec *wire.Codec + Codec *codec.Codec AccDecoder auth.AccountDecoder Client rpcclient.Client Logger io.Writer @@ -98,7 +98,7 @@ func createCertifier() tmlite.Certifier { } // WithCodec returns a copy of the context with an updated codec. -func (ctx CLIContext) WithCodec(cdc *wire.Codec) CLIContext { +func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { ctx.Codec = cdc return ctx } diff --git a/client/context/query.go b/client/context/query.go index fc35e53515..30cd2db300 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -12,8 +12,8 @@ import ( "strings" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" - "github.com/cosmos/cosmos-sdk/wire" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" @@ -343,7 +343,7 @@ func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error { } var multiStoreProof store.MultiStoreProof - cdc := wire.NewCodec() + cdc := codec.New() err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof) if err != nil { return errors.Wrap(err, "failed to unmarshalBinary rangeProof") diff --git a/client/keys/wire.go b/client/keys/codec.go similarity index 70% rename from client/keys/wire.go rename to client/keys/codec.go index a163f995a2..6bbb168507 100644 --- a/client/keys/wire.go +++ b/client/keys/codec.go @@ -1,14 +1,14 @@ package keys import ( - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" ) -var cdc *wire.Codec +var cdc *codec.Codec func init() { - cdc = wire.NewCodec() - wire.RegisterCrypto(cdc) + cdc = codec.New() + codec.RegisterCrypto(cdc) } // marshal keys diff --git a/client/lcd/wire.go b/client/lcd/codec.go similarity index 100% rename from client/lcd/wire.go rename to client/lcd/codec.go diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index b3c27bfbab..312ac397b2 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -22,9 +22,9 @@ import ( client "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/client/keys" rpc "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/codec" tests "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" @@ -61,7 +61,7 @@ func TestKeys(t *testing.T) { require.Equal(t, http.StatusOK, res.StatusCode, body) var resp keys.KeyOutput - err = wire.Cdc.UnmarshalJSON([]byte(body), &resp) + err = codec.Cdc.UnmarshalJSON([]byte(body), &resp) require.Nil(t, err, body) addr2Bech32 := resp.Address @@ -181,7 +181,7 @@ func TestBlock(t *testing.T) { res, body = Request(t, port, "GET", "/blocks/1", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - err = wire.Cdc.UnmarshalJSON([]byte(body), &resultBlock) + err = codec.Cdc.UnmarshalJSON([]byte(body), &resultBlock) require.Nil(t, err, "Couldn't parse block") require.NotEqual(t, ctypes.ResultBlock{}, resultBlock) diff --git a/client/lcd/root.go b/client/lcd/root.go index f18ee5dfd4..088344b846 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" @@ -27,7 +27,7 @@ import ( // ServeCommand will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest -func ServeCommand(cdc *wire.Codec) *cobra.Command { +func ServeCommand(cdc *codec.Codec) *cobra.Command { flagListenAddr := "laddr" flagCORS := "cors" flagMaxOpenConnections := "max-open" @@ -71,7 +71,7 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command { return cmd } -func createHandler(cdc *wire.Codec) http.Handler { +func createHandler(cdc *codec.Codec) http.Handler { r := mux.NewRouter() kb, err := keys.GetKeyBase() //XXX diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 5936904077..1075c078b9 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -15,11 +15,11 @@ import ( "github.com/cosmos/cosmos-sdk/client" keys "github.com/cosmos/cosmos-sdk/client/keys" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/viper" @@ -176,7 +176,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewDec(100)) } - appState, err := wire.MarshalJSONIndent(cdc, genesisState) + appState, err := codec.MarshalJSONIndent(cdc, genesisState) require.NoError(t, err) genDoc.AppState = appState @@ -245,7 +245,7 @@ func startTM( // startLCD starts the LCD. // // NOTE: This causes the thread to block. -func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) { +func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) { return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) } diff --git a/client/rpc/block.go b/client/rpc/block.go index d16f383424..4e6ed0c5fe 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -42,7 +42,7 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { } // TODO move maarshalling into cmd/rest functions - // output, err := tmwire.MarshalJSON(res) + // output, err := tmcodec.MarshalJSON(res) output, err := cdc.MarshalJSON(res) if err != nil { return nil, err diff --git a/client/rpc/wire.go b/client/rpc/codec.go similarity index 100% rename from client/rpc/wire.go rename to client/rpc/codec.go diff --git a/client/tx/query.go b/client/tx/query.go index f95776b109..20a455d79b 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -16,13 +16,13 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" ) // QueryTxCmd implements the default command for a tx query. -func QueryTxCmd(cdc *wire.Codec) *cobra.Command { +func QueryTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Matches this txhash over all committed blocks", @@ -51,7 +51,7 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command { return cmd } -func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) { +func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return nil, err @@ -72,10 +72,10 @@ func queryTx(cdc *wire.Codec, cliCtx context.CLIContext, hashHexStr string, trus return nil, err } - return wire.MarshalJSONIndent(cdc, info) + return codec.MarshalJSONIndent(cdc, info) } -func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (Info, error) { +func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (Info, error) { // TODO: verify the proof if requested tx, err := parseTx(cdc, res.Tx) if err != nil { @@ -98,7 +98,7 @@ type Info struct { Result abci.ResponseDeliverTx `json:"result"` } -func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) { +func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { var tx auth.StdTx err := cdc.UnmarshalBinary(txBytes, &tx) @@ -112,7 +112,7 @@ func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) { // REST // transaction query REST handler -func QueryTxRequestHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc { +func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) hashHexStr := vars["hash"] diff --git a/client/tx/root.go b/client/tx/root.go index 7e18d5aae1..19376a25b1 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -5,11 +5,11 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" ) // AddCommands adds a number of tx-query related subcommands -func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { +func AddCommands(cmd *cobra.Command, cdc *codec.Codec) { cmd.AddCommand( SearchTxCmd(cdc), QueryTxCmd(cdc), @@ -17,7 +17,7 @@ func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { } // register REST routes -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec) { +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc("/txs", SearchTxRequestHandlerFn(cliCtx, cdc)).Methods("GET") // r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST") diff --git a/client/tx/search.go b/client/tx/search.go index 06b3c09729..000dc57ebb 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -24,7 +24,7 @@ const ( ) // default client command to search through tagged transactions -func SearchTxCmd(cdc *wire.Codec) *cobra.Command { +func SearchTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "txs", Short: "Search for all transactions that match the given tags.", @@ -70,7 +70,7 @@ $ gaiacli tendermint txs --tag test1,test2 --any return cmd } -func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Info, error) { +func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]Info, error) { if len(tags) == 0 { return nil, errors.New("must declare at least one tag to search") } @@ -103,7 +103,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *wire.Codec, tags []string) ([]Inf } // parse the indexed txs into an array of Info -func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) { +func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) { var err error out := make([]Info, len(res)) for i := range res { @@ -119,7 +119,7 @@ func FormatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]Info, error) { // REST // Search Tx REST Handler -func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { +func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tag := r.FormValue("tag") if tag == "" { diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 7cf235b163..627fb5987e 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -12,8 +12,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/gov" @@ -36,7 +36,7 @@ var ( // Extended ABCI application type GaiaApp struct { *bam.BaseApp - cdc *wire.Codec + cdc *codec.Codec // keys to access the substores keyMain *sdk.KVStoreKey @@ -128,16 +128,16 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio } // custom tx codec -func MakeCodec() *wire.Codec { - var cdc = wire.NewCodec() - ibc.RegisterWire(cdc) - bank.RegisterWire(cdc) - stake.RegisterWire(cdc) - slashing.RegisterWire(cdc) - gov.RegisterWire(cdc) - auth.RegisterWire(cdc) - sdk.RegisterWire(cdc) - wire.RegisterCrypto(cdc) +func MakeCodec() *codec.Codec { + var cdc = codec.New() + ibc.RegisterCodec(cdc) + bank.RegisterCodec(cdc) + stake.RegisterCodec(cdc) + slashing.RegisterCodec(cdc) + gov.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) return cdc } @@ -222,7 +222,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), GovData: gov.WriteGenesis(ctx, app.govKeeper), } - appState, err = wire.MarshalJSONIndent(app.cdc, genState) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 63d650cef6..0fb45863ca 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/stretchr/testify/require" @@ -25,7 +25,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { StakeData: stake.DefaultGenesisState(), } - stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState) + stateBytes, err := codec.MarshalJSONIndent(gapp.cdc, genesisState) if err != nil { return err } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 10c7e4126e..e19ee616ac 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -6,10 +6,10 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" @@ -92,7 +92,7 @@ type GaiaGenTx struct { // GaiaAppGenTx generates a Gaia genesis transaction. func GaiaAppGenTx( - cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx, + cdc *codec.Codec, pk crypto.PubKey, genTxConfig config.GenTx, ) (appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { if genTxConfig.Name == "" { return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)") @@ -136,7 +136,7 @@ func GaiaAppGenTx( } // Generate a gaia genesis transaction without flags -func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) ( +func GaiaAppGenTxNF(cdc *codec.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) ( appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { var bz []byte @@ -145,7 +145,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name Address: addr, PubKey: sdk.MustBech32ifyConsPub(pk), } - bz, err = wire.MarshalJSONIndent(cdc, gaiaGenTx) + bz, err = codec.MarshalJSONIndent(cdc, gaiaGenTx) if err != nil { return } @@ -160,7 +160,7 @@ func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name // Create the core parameters for genesis initialization for gaia // note that the pubkey input is this machines pubkey -func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) { +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") @@ -280,13 +280,13 @@ func validateGenesisStateAccounts(accs []GenesisAccount) (err error) { } // GaiaAppGenState but with JSON -func GaiaAppGenStateJSON(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func GaiaAppGenStateJSON(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { // create the final app state genesisState, err := GaiaAppGenState(cdc, appGenTxs) if err != nil { return nil, err } - appState, err = wire.MarshalJSONIndent(cdc, genesisState) + appState, err = codec.MarshalJSONIndent(cdc, genesisState) return } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 43f35925e3..3d9f86789a 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -18,10 +18,10 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "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/tests" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" @@ -549,8 +549,8 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { require.NoError(t, err, "out %v, err %v", out, err) value := initRes["value"] var acc auth.BaseAccount - cdc := wire.NewCodec() - wire.RegisterCrypto(cdc) + cdc := codec.New() + codec.RegisterCrypto(cdc) err = cdc.UnmarshalJSON(value, &acc) require.NoError(t, err, "value %v, err %v", string(value), err) return acc diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index d0a0ad9d72..3ff2730f46 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -21,7 +21,7 @@ import ( bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" @@ -127,7 +127,7 @@ var ( // Extended ABCI application type GaiaApp struct { *bam.BaseApp - cdc *wire.Codec + cdc *codec.Codec // keys to access the substores keyMain *sdk.KVStoreKey @@ -204,15 +204,15 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp } // custom tx codec -func MakeCodec() *wire.Codec { - var cdc = wire.NewCodec() - ibc.RegisterWire(cdc) - bank.RegisterWire(cdc) - stake.RegisterWire(cdc) - slashing.RegisterWire(cdc) - auth.RegisterWire(cdc) - sdk.RegisterWire(cdc) - wire.RegisterCrypto(cdc) +func MakeCodec() *codec.Codec { + var cdc = codec.New() + ibc.RegisterCodec(cdc) + bank.RegisterCodec(cdc) + stake.RegisterCodec(cdc) + slashing.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) cdc.Seal() return cdc } diff --git a/wire/wire.go b/codec/codec.go similarity index 93% rename from wire/wire.go rename to codec/codec.go index 683149c99c..175eb89c13 100644 --- a/wire/wire.go +++ b/codec/codec.go @@ -1,4 +1,4 @@ -package wire +package codec import ( "bytes" @@ -11,7 +11,7 @@ import ( // amino codec to marshal/unmarshal type Codec = amino.Codec -func NewCodec() *Codec { +func New() *Codec { cdc := amino.NewCodec() return cdc } @@ -42,7 +42,7 @@ func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) { var Cdc *Codec func init() { - cdc := NewCodec() + cdc := New() RegisterCrypto(cdc) Cdc = cdc.Seal() } diff --git a/crypto/keys/wire.go b/crypto/keys/codec.go similarity index 100% rename from crypto/keys/wire.go rename to crypto/keys/codec.go diff --git a/docs/sdk/core/app2.md b/docs/sdk/core/app2.md index 9a689cca60..6477617fb1 100644 --- a/docs/sdk/core/app2.md +++ b/docs/sdk/core/app2.md @@ -139,8 +139,8 @@ Amino can also be used for persistent storage of interfaces. To use Amino, simply create a codec, and then register types: ``` -func NewCodec() *wire.Codec { - cdc := wire.NewCodec() +func NewCodec() *codec.Codec { + cdc := codec.New() cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil) cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil) @@ -175,7 +175,7 @@ func (tx app2Tx) GetMsgs() []sdk.Msg { } // Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue -func tx2Decoder(cdc *wire.Codec) sdk.TxDecoder { +func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx err := cdc.UnmarshalBinary(txBytes, &tx) diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index 5f23abe071..837c9f44fa 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -13,8 +13,8 @@ import ( "github.com/tendermint/tendermint/libs/log" bapp "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) const ( @@ -25,8 +25,8 @@ var ( issuer = ed25519.GenPrivKey().PubKey().Address() ) -func NewCodec() *wire.Codec { - cdc := wire.NewCodec() +func NewCodec() *codec.Codec { + cdc := codec.New() cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil) cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil) @@ -196,7 +196,7 @@ func (tx app2Tx) GetSignature() []byte { } // Amino decode app2Tx. Capable of decoding both MsgSend and MsgIssue -func tx2Decoder(cdc *wire.Codec) sdk.TxDecoder { +func tx2Decoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, sdk.Error) { var tx app2Tx err := cdc.UnmarshalBinary(txBytes, &tx) diff --git a/docs/sdk/core/examples/app3.go b/docs/sdk/core/examples/app3.go index 787c75d96e..9a101c2816 100644 --- a/docs/sdk/core/examples/app3.go +++ b/docs/sdk/core/examples/app3.go @@ -7,8 +7,8 @@ import ( "github.com/tendermint/tendermint/libs/log" bapp "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -51,12 +51,12 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp { } // Update codec from app2 to register imported modules -func UpdatedCodec() *wire.Codec { - cdc := wire.NewCodec() +func UpdatedCodec() *codec.Codec { + cdc := codec.New() cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil) cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil) - auth.RegisterWire(cdc) + auth.RegisterCodec(cdc) cryptoAmino.RegisterAmino(cdc) return cdc } diff --git a/docs/sdk/core/examples/app4.go b/docs/sdk/core/examples/app4.go index 620913bb69..e4fa515ee2 100644 --- a/docs/sdk/core/examples/app4.go +++ b/docs/sdk/core/examples/app4.go @@ -7,8 +7,8 @@ import ( "github.com/tendermint/tendermint/libs/log" bapp "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -76,7 +76,7 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount, err error) { // InitChainer will set initial balances for accounts as well as initial coin metadata // MsgIssue can no longer be used to create new coin -func NewInitChainer(cdc *wire.Codec, accountMapper auth.AccountMapper) sdk.InitChainer { +func NewInitChainer(cdc *codec.Codec, accountMapper auth.AccountMapper) sdk.InitChainer { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { stateJSON := req.AppStateBytes diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-codec.md b/docs/sdk/sdk-by-examples/simple-governance/app-codec.md index 9d35b43631..d994ba19c9 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/app-codec.md +++ b/docs/sdk/sdk-by-examples/simple-governance/app-codec.md @@ -5,13 +5,13 @@ Finally, we need to define the `MakeCodec()` function and register the concrete types and interface from the various modules. ```go -func MakeCodec() *wire.Codec { - var cdc = wire.NewCodec() - wire.RegisterCrypto(cdc) // Register crypto. - sdk.RegisterWire(cdc) // Register Msgs - bank.RegisterWire(cdc) - simplestake.RegisterWire(cdc) - simpleGov.RegisterWire(cdc) +func MakeCodec() *codec.Codec { + var cdc = codec.New() + codec.RegisterCrypto(cdc) // Register crypto. + sdk.RegisterCodec(cdc) // Register Msgs + bank.RegisterCodec(cdc) + simplestake.RegisterCodec(cdc) + simpleGov.RegisterCodec(cdc) // Register AppAccount cdc.RegisterInterface((*auth.Account)(nil), nil) diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-rest.md b/docs/sdk/sdk-by-examples/simple-governance/app-rest.md index 7e8469ad6a..828d52c858 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/app-rest.md +++ b/docs/sdk/sdk-by-examples/simple-governance/app-rest.md @@ -13,7 +13,7 @@ var SimpleGovAppInit = server.AppInit{ } // SimpleGovAppGenState sets up the app_state and appends the simpleGov app state -func SimpleGovAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func SimpleGovAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { appState, err = server.SimpleAppGenState(cdc, appGenTxs) if err != nil { return diff --git a/docs/sdk/sdk-by-examples/simple-governance/app-structure.md b/docs/sdk/sdk-by-examples/simple-governance/app-structure.md index 7cec5aa5c8..ce9ba5fef3 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/app-structure.md +++ b/docs/sdk/sdk-by-examples/simple-governance/app-structure.md @@ -28,7 +28,7 @@ Then, let us define the structure of our application. // Extended ABCI application type SimpleGovApp struct { *bam.BaseApp - cdc *wire.Codec + cdc *codec.Codec // keys to access the substores capKeyMainStore *sdk.KVStoreKey diff --git a/docs/sdk/sdk-by-examples/simple-governance/intro.md b/docs/sdk/sdk-by-examples/simple-governance/intro.md index ab4b5b6a5b..fe1c7639e9 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/intro.md +++ b/docs/sdk/sdk-by-examples/simple-governance/intro.md @@ -28,7 +28,7 @@ Before getting in the bulk of the code, we will start by some introductory conte + [Types](module-types.md) + [Keeper](module-keeper.md) + [Handler](module-handler.md) - + [Wire](module-wire.md) + + [Wire](module-codec.md) + [Errors](module-errors.md) + Command-Line Interface and Rest API * [Command-Line Interface](module-cli.md) diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-cli.md b/docs/sdk/sdk-by-examples/simple-governance/module-cli.md index b7e3143e3c..4cdf331a9a 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/module-cli.md +++ b/docs/sdk/sdk-by-examples/simple-governance/module-cli.md @@ -14,7 +14,7 @@ The CLI builds on top of [Cobra](https://github.com/spf13/cobra). Here is the sc ) // Main command function. One function for each command. - func Command(codec *wire.Codec) *cobra.Command { + func Command(codec *codec.Codec) *cobra.Command { // Create the command to return command := &cobra.Command{ Use: "actual command", diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-codec.md b/docs/sdk/sdk-by-examples/simple-governance/module-codec.md new file mode 100644 index 0000000000..68b03b9365 --- /dev/null +++ b/docs/sdk/sdk-by-examples/simple-governance/module-codec.md @@ -0,0 +1,13 @@ +## Codec + +**File: [`x/simple_governance/codec.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/codec.go)** + +The `codec.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare: + +```go +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil) + cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil) +} +``` +Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more). \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-init.md b/docs/sdk/sdk-by-examples/simple-governance/module-init.md index d018b512ff..33bdfd5000 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/module-init.md +++ b/docs/sdk/sdk-by-examples/simple-governance/module-init.md @@ -7,7 +7,7 @@ cd x/ mkdir simple_governance cd simple_governance mkdir -p client/cli client/rest -touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go wire.go +touch client/cli/simple_governance.go client/rest/simple_governance.go errors.go handler.go handler_test.go keeper_keys.go keeper_test.go keeper.go test_common.go test_types.go types.go codec.go ``` Let us start by adding the files we will need. Your module's folder should look something like that: @@ -25,7 +25,7 @@ x ├─── keeper_keys.go ├─── keeper.go ├─── types.go - └─── wire.go + └─── codec.go ``` Let us go into the detail of each of these files. \ No newline at end of file diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md b/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md index 851593a78b..99c7cd566c 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md +++ b/docs/sdk/sdk-by-examples/simple-governance/module-keeper.md @@ -47,7 +47,7 @@ With all that in mind, we can define the structure of our `Keeper`: ```go type Keeper struct { SimpleGov sdk.StoreKey // Key to our module's store - cdc *wire.Codec // Codec to encore/decode structs + cdc *codec.Codec // Codec to encore/decode structs ck bank.Keeper // Needed to handle deposits. This module onlyl requires read/writes to Atom balance sm stake.Keeper // Needed to compute voting power. This module only needs read access to the staking store. codespace sdk.CodespaceType // Reserves space for error codes diff --git a/docs/sdk/sdk-by-examples/simple-governance/module-wire.md b/docs/sdk/sdk-by-examples/simple-governance/module-wire.md deleted file mode 100644 index f889db91a4..0000000000 --- a/docs/sdk/sdk-by-examples/simple-governance/module-wire.md +++ /dev/null @@ -1,13 +0,0 @@ -## Wire - -**File: [`x/simple_governance/wire.go`](https://github.com/cosmos/cosmos-sdk/blob/fedekunze/module_tutorial/examples/simpleGov/x/simple_governance/wire.go)** - -The `wire.go` file allows developers to register the concrete message types of their module into the codec. In our case, we have two messages to declare: - -```go -func RegisterWire(cdc *wire.Codec) { - cdc.RegisterConcrete(SubmitProposalMsg{}, "simple_governance/SubmitProposalMsg", nil) - cdc.RegisterConcrete(VoteMsg{}, "simple_governance/VoteMsg", nil) -} -``` -Don't forget to call this function in `app.go` (see [Application - Bridging it all together](app-structure.md)) for more). \ No newline at end of file diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 751924f469..a3d04960d3 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -8,7 +8,7 @@ The specification has focused on semantics and functionality of the IBC protocol In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: Ethereum's RLP[[6](./references.md#6)] and Google's Protobuf[[7](./references.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. -The tendermint-specific data structures are encoded with go-wire[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. +The tendermint-specific data structures are encoded with go-amino[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. For the following appendixes, the data structure specifications will be in proto3[[9](./references.md#9)] format. @@ -61,7 +61,7 @@ The IBC protocol does not handle these kinds of errors. They must be handled ind **TODO: clean this all up** -This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. +This is a mess now, we need to figure out what formats we use, define go-amino, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 0951ca6572..56ea93b5a4 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -28,7 +28,7 @@ Every transaction on the same chain already has a well-defined causality relatio For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain `B` when a particular IBC packet is committed to chain `B`, and require outgoing sends of that packet on chain `A` to escrow an equal amount of the asset on chain `A` until the vouchers are later redeemed back to chain `A` with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain `B` can later be redeemed back to chain `A`. -This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-formats). +This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement amino-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-formats). ### 3.2 Definitions diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md index f85ae85994..0c4fae18e8 100644 --- a/docs/spec/ibc/conclusion.md +++ b/docs/spec/ibc/conclusion.md @@ -6,4 +6,4 @@ We have demonstrated a secure, performant, and flexible protocol for cross-block This document defines solely a message queue protocol - not the application-level semantics which must sit on top of it to enable asset transfer between two chains. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. -There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in Golang and released under the Apache license. To facilitate implementations in other langauages which are wire-compatible with the Cosmos implementation, the following appendices define exact message and binary encoding formats. +There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in Golang and released under the Apache license. To facilitate implementations in other langauages which are amino-compatible with the Cosmos implementation, the following appendices define exact message and binary encoding formats. diff --git a/docs/spec/ibc/references.md b/docs/spec/ibc/references.md index ba9fbee697..715086e1bc 100644 --- a/docs/spec/ibc/references.md +++ b/docs/spec/ibc/references.md @@ -24,7 +24,7 @@ [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) ##### 8: -[https://github.com/tendermint/go-wire](https://github.com/tendermint/go-wire) +[https://github.com/tendermint/go-amino](https://github.com/tendermint/go-amino) ##### 9: [https://developers.google.com/protocol-buffers/docs/proto3](https://developers.google.com/protocol-buffers/docs/proto3) diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 6330e9f31e..657cbcf0fd 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -4,9 +4,9 @@ import ( "encoding/json" bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/basecoin/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" @@ -27,7 +27,7 @@ const ( // integral app types. type BasecoinApp struct { *bam.BaseApp - cdc *wire.Codec + cdc *codec.Codec // keys to access the multistore keyMain *sdk.KVStoreKey @@ -93,16 +93,16 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba return app } -// MakeCodec creates a new wire codec and registers all the necessary types +// MakeCodec creates a new codec codec and registers all the necessary types // with the codec. -func MakeCodec() *wire.Codec { - cdc := wire.NewCodec() +func MakeCodec() *codec.Codec { + cdc := codec.New() - wire.RegisterCrypto(cdc) - sdk.RegisterWire(cdc) - bank.RegisterWire(cdc) - ibc.RegisterWire(cdc) - auth.RegisterWire(cdc) + codec.RegisterCrypto(cdc) + sdk.RegisterCodec(cdc) + bank.RegisterCodec(cdc) + ibc.RegisterCodec(cdc) + auth.RegisterCodec(cdc) // register custom type cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil) @@ -173,7 +173,7 @@ func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage, app.accountMapper.IterateAccounts(ctx, appendAccountsFn) genState := types.GenesisState{Accounts: accounts} - appState, err = wire.MarshalJSONIndent(app.cdc, genState) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index dad8191b35..24237e6029 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -4,9 +4,9 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/basecoin/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -22,7 +22,7 @@ func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.Gene } genesisState := types.GenesisState{Accounts: genAccts} - stateBytes, err := wire.MarshalJSONIndent(baseApp.cdc, genesisState) + stateBytes, err := codec.MarshalJSONIndent(baseApp.cdc, genesisState) if err != nil { return types.GenesisState{}, err } @@ -67,7 +67,7 @@ func TestGenesis(t *testing.T) { // reload app and ensure the account is still there baseApp = NewBasecoinApp(logger, db) - stateBytes, err := wire.MarshalJSONIndent(baseApp.cdc, genState) + stateBytes, err := codec.MarshalJSONIndent(baseApp.cdc, genState) require.Nil(t, err) // initialize the chain with the expected genesis state diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 45774ae435..04d3e371ef 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -1,8 +1,8 @@ package types import ( + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" ) @@ -30,7 +30,7 @@ func NewAppAccount(name string, baseAcct auth.BaseAccount) *AppAccount { // GetAccountDecoder returns the AccountDecoder function for the custom // AppAccount. -func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { +func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder { return func(accBytes []byte) (auth.Account, error) { if len(accBytes) == 0 { return nil, sdk.ErrTxDecode("accBytes are empty") diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index e66dc0cb3d..0449f6251e 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -10,8 +10,8 @@ import ( tmtypes "github.com/tendermint/tendermint/types" bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" @@ -30,7 +30,7 @@ const ( // Extended ABCI application type DemocoinApp struct { *bam.BaseApp - cdc *wire.Codec + cdc *codec.Codec // keys to access the substores capKeyMainStore *sdk.KVStoreKey @@ -103,15 +103,15 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { } // custom tx codec -func MakeCodec() *wire.Codec { - var cdc = wire.NewCodec() - wire.RegisterCrypto(cdc) // Register crypto. - sdk.RegisterWire(cdc) // Register Msgs - cool.RegisterWire(cdc) - pow.RegisterWire(cdc) - bank.RegisterWire(cdc) - ibc.RegisterWire(cdc) - simplestake.RegisterWire(cdc) +func MakeCodec() *codec.Codec { + var cdc = codec.New() + codec.RegisterCrypto(cdc) // Register crypto. + sdk.RegisterCodec(cdc) // Register Msgs + cool.RegisterCodec(cdc) + pow.RegisterCodec(cdc) + bank.RegisterCodec(cdc) + ibc.RegisterCodec(cdc) + simplestake.RegisterCodec(cdc) // Register AppAccount cdc.RegisterInterface((*auth.Account)(nil), nil) @@ -182,7 +182,7 @@ func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), } - appState, err = wire.MarshalJSONIndent(app.cdc, genState) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } diff --git a/examples/democoin/app/app_test.go b/examples/democoin/app/app_test.go index e964dbad25..eeea84bd46 100644 --- a/examples/democoin/app/app_test.go +++ b/examples/democoin/app/app_test.go @@ -4,10 +4,10 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/types" "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -27,7 +27,7 @@ func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error CoolGenesis: cool.Genesis{trend}, } - stateBytes, err := wire.MarshalJSONIndent(bapp.cdc, genesisState) + stateBytes, err := codec.MarshalJSONIndent(bapp.cdc, genesisState) if err != nil { return err } diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index 528cafe1c7..e22b0fc3e0 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -13,9 +13,9 @@ import ( "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/app" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/wire" ) // init parameters @@ -25,7 +25,7 @@ var CoolAppInit = server.AppInit{ } // coolGenAppParams sets up the app_state and appends the cool app state -func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { +func CoolAppGenState(cdc *codec.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { appState, err = server.SimpleAppGenState(cdc, appGenTxs) if err != nil { return diff --git a/examples/democoin/types/account.go b/examples/democoin/types/account.go index 8eb9b0ae42..ad57c944d3 100644 --- a/examples/democoin/types/account.go +++ b/examples/democoin/types/account.go @@ -1,8 +1,8 @@ package types import ( + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool" @@ -31,7 +31,7 @@ func (acc AppAccount) GetName() string { return acc.Name } func (acc *AppAccount) SetName(name string) { acc.Name = name } // Get the AccountDecoder function for the custom AppAccount -func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { +func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder { return func(accBytes []byte) (res auth.Account, err error) { if len(accBytes) == 0 { return nil, sdk.ErrTxDecode("accBytes are empty") diff --git a/examples/democoin/x/assoc/validator_set.go b/examples/democoin/x/assoc/validator_set.go index 7515e1ad60..69b35501f3 100644 --- a/examples/democoin/x/assoc/validator_set.go +++ b/examples/democoin/x/assoc/validator_set.go @@ -3,8 +3,8 @@ package assoc import ( "bytes" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) // ValidatorSet defines @@ -12,7 +12,7 @@ type ValidatorSet struct { sdk.ValidatorSet store sdk.KVStore - cdc *wire.Codec + cdc *codec.Codec maxAssoc int addrLen int @@ -21,7 +21,7 @@ type ValidatorSet struct { var _ sdk.ValidatorSet = ValidatorSet{} // NewValidatorSet returns new ValidatorSet with underlying ValidatorSet -func NewValidatorSet(cdc *wire.Codec, store sdk.KVStore, valset sdk.ValidatorSet, maxAssoc int, addrLen int) ValidatorSet { +func NewValidatorSet(cdc *codec.Codec, store sdk.KVStore, valset sdk.ValidatorSet, maxAssoc int, addrLen int) ValidatorSet { if maxAssoc < 0 || addrLen < 0 { panic("Cannot use negative integer for NewValidatorSet") } diff --git a/examples/democoin/x/assoc/validator_set_test.go b/examples/democoin/x/assoc/validator_set_test.go index eac23b25e2..9fc6526f8d 100644 --- a/examples/democoin/x/assoc/validator_set_test.go +++ b/examples/democoin/x/assoc/validator_set_test.go @@ -9,10 +9,10 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/mock" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) func defaultContext(key sdk.StoreKey) sdk.Context { @@ -36,7 +36,7 @@ func TestValidatorSet(t *testing.T) { {addr2, sdk.NewDec(2)}, }} - valset := NewValidatorSet(wire.NewCodec(), ctx.KVStore(key).Prefix([]byte("assoc")), base, 1, 5) + valset := NewValidatorSet(codec.New(), ctx.KVStore(key).Prefix([]byte("assoc")), base, 1, 5) require.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1)) require.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2)) diff --git a/examples/democoin/x/cool/app_test.go b/examples/democoin/x/cool/app_test.go index 3349928eb2..35a656cebc 100644 --- a/examples/democoin/x/cool/app_test.go +++ b/examples/democoin/x/cool/app_test.go @@ -47,7 +47,7 @@ var ( func getMockApp(t *testing.T) *mock.App { mapp := mock.NewApp() - RegisterWire(mapp.Cdc) + RegisterCodec(mapp.Cdc) keyCool := sdk.NewKVStoreKey("cool") bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper) keeper := NewKeeper(keyCool, bankKeeper, mapp.RegisterCodespace(DefaultCodespace)) diff --git a/examples/democoin/x/cool/client/cli/tx.go b/examples/democoin/x/cool/client/cli/tx.go index 1e41ff5b25..2f78bba460 100644 --- a/examples/democoin/x/cool/client/cli/tx.go +++ b/examples/democoin/x/cool/client/cli/tx.go @@ -7,15 +7,15 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) // QuizTxCmd invokes the coolness quiz transaction. -func QuizTxCmd(cdc *wire.Codec) *cobra.Command { +func QuizTxCmd(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "cool [answer]", Short: "What's cooler than being cool?", @@ -40,7 +40,7 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command { } // SetTrendTxCmd sends a new cool trend transaction. -func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command { +func SetTrendTxCmd(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "setcool [answer]", Short: "You're so cool, tell us what is cool!", diff --git a/examples/democoin/x/cool/wire.go b/examples/democoin/x/cool/codec.go similarity index 53% rename from examples/democoin/x/cool/wire.go rename to examples/democoin/x/cool/codec.go index 63666888a9..491c006177 100644 --- a/examples/democoin/x/cool/wire.go +++ b/examples/democoin/x/cool/codec.go @@ -1,11 +1,11 @@ package cool import ( - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" ) -// Register concrete types on wire codec -func RegisterWire(cdc *wire.Codec) { +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgQuiz{}, "cool/Quiz", nil) cdc.RegisterConcrete(MsgSetTrend{}, "cool/SetTrend", nil) } diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index 50b38fc3ee..e3af7790e4 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -8,9 +8,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth" bank "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -26,7 +26,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { func TestCoolKeeper(t *testing.T) { ms, capKey := setupMultiStore() - cdc := wire.NewCodec() + cdc := codec.New() auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, auth.ProtoBaseAccount) diff --git a/examples/democoin/x/oracle/README.md b/examples/democoin/x/oracle/README.md index 0cfcb820d5..b840dc0e8b 100644 --- a/examples/democoin/x/oracle/README.md +++ b/examples/democoin/x/oracle/README.md @@ -38,7 +38,7 @@ func NewHandler(keeper Keeper) sdk.Handler { In the previous example, the keeper has an `oracle.Keeper`. `oracle.Keeper`s are generated by `NewKeeper`. ```go -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *codec.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { return Keeper { cdc: cdc, key: key, diff --git a/examples/democoin/x/oracle/keeper.go b/examples/democoin/x/oracle/keeper.go index 0406f560a0..e55cd7083c 100644 --- a/examples/democoin/x/oracle/keeper.go +++ b/examples/democoin/x/oracle/keeper.go @@ -1,7 +1,7 @@ package oracle import ( - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,7 +9,7 @@ import ( // Keeper of the oracle store type Keeper struct { key sdk.StoreKey - cdc *wire.Codec + cdc *codec.Codec valset sdk.ValidatorSet @@ -18,7 +18,7 @@ type Keeper struct { } // NewKeeper constructs a new keeper -func NewKeeper(key sdk.StoreKey, cdc *wire.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { +func NewKeeper(key sdk.StoreKey, cdc *codec.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper { if timeout < 0 { panic("Timeout should not be negative") } diff --git a/examples/democoin/x/oracle/keeper_keys.go b/examples/democoin/x/oracle/keeper_keys.go index f657e80279..9b71aeaa18 100644 --- a/examples/democoin/x/oracle/keeper_keys.go +++ b/examples/democoin/x/oracle/keeper_keys.go @@ -1,23 +1,23 @@ package oracle import ( + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) // GetInfoKey returns the key for OracleInfo -func GetInfoKey(p Payload, cdc *wire.Codec) []byte { +func GetInfoKey(p Payload, cdc *codec.Codec) []byte { bz := cdc.MustMarshalBinary(p) return append([]byte{0x00}, bz...) } // GetSignPrefix returns the prefix for signs -func GetSignPrefix(p Payload, cdc *wire.Codec) []byte { +func GetSignPrefix(p Payload, cdc *codec.Codec) []byte { bz := cdc.MustMarshalBinary(p) return append([]byte{0x01}, bz...) } // GetSignKey returns the key for sign -func GetSignKey(p Payload, signer sdk.AccAddress, cdc *wire.Codec) []byte { +func GetSignKey(p Payload, signer sdk.AccAddress, cdc *codec.Codec) []byte { return append(GetSignPrefix(p, cdc), signer...) } diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index f4971c8b14..416d933f2d 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -9,10 +9,10 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/mock" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" ) func defaultContext(keys ...sdk.StoreKey) sdk.Context { @@ -39,8 +39,8 @@ func (o seqOracle) ValidateBasic() sdk.Error { return nil } -func makeCodec() *wire.Codec { - var cdc = wire.NewCodec() +func makeCodec() *codec.Codec { + var cdc = codec.New() cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(Msg{}, "test/Oracle", nil) @@ -79,7 +79,7 @@ func getSequence(ctx sdk.Context, key sdk.StoreKey) int { if seqbz == nil { seq = 0 } else { - wire.NewCodec().MustUnmarshalBinary(seqbz, &seq) + codec.New().MustUnmarshalBinary(seqbz, &seq) } return seq @@ -93,7 +93,7 @@ func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error { return sdk.NewError(sdk.CodespaceRoot, 1, "") } - bz := wire.NewCodec().MustMarshalBinary(seq + 1) + bz := codec.New().MustMarshalBinary(seq + 1) store.Set([]byte("seq"), bz) return nil diff --git a/examples/democoin/x/pow/app_test.go b/examples/democoin/x/pow/app_test.go index 0a4f95cf15..6e6f07f770 100644 --- a/examples/democoin/x/pow/app_test.go +++ b/examples/democoin/x/pow/app_test.go @@ -23,7 +23,7 @@ var ( func getMockApp(t *testing.T) *mock.App { mapp := mock.NewApp() - RegisterWire(mapp.Cdc) + RegisterCodec(mapp.Cdc) keyPOW := sdk.NewKVStoreKey("pow") bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper) config := Config{"pow", 1} diff --git a/examples/democoin/x/pow/client/cli/tx.go b/examples/democoin/x/pow/client/cli/tx.go index 15102b128a..f1d708c897 100644 --- a/examples/democoin/x/pow/client/cli/tx.go +++ b/examples/democoin/x/pow/client/cli/tx.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" @@ -16,7 +16,7 @@ import ( ) // command to mine some pow! -func MineCmd(cdc *wire.Codec) *cobra.Command { +func MineCmd(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "mine [difficulty] [count] [nonce] [solution]", Short: "Mine some coins with proof-of-work!", diff --git a/examples/democoin/x/pow/codec.go b/examples/democoin/x/pow/codec.go new file mode 100644 index 0000000000..8f4296f17a --- /dev/null +++ b/examples/democoin/x/pow/codec.go @@ -0,0 +1,10 @@ +package pow + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgMine{}, "pow/Mine", nil) +} diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 03613f7166..8166ddfc53 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -8,15 +8,15 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - wire "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth" bank "github.com/cosmos/cosmos-sdk/x/bank" ) func TestPowHandler(t *testing.T) { ms, capKey := setupMultiStore() - cdc := wire.NewCodec() + cdc := codec.New() auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, auth.ProtoBaseAccount) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index dbab3e6178..dbd974c4d0 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -9,9 +9,9 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth" bank "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -29,7 +29,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { func TestPowKeeperGetSet(t *testing.T) { ms, capKey := setupMultiStore() - cdc := wire.NewCodec() + cdc := codec.New() auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, auth.ProtoBaseAccount) diff --git a/examples/democoin/x/pow/wire.go b/examples/democoin/x/pow/wire.go deleted file mode 100644 index 3d7f61486e..0000000000 --- a/examples/democoin/x/pow/wire.go +++ /dev/null @@ -1,10 +0,0 @@ -package pow - -import ( - "github.com/cosmos/cosmos-sdk/wire" -) - -// Register concrete types on wire codec -func RegisterWire(cdc *wire.Codec) { - cdc.RegisterConcrete(MsgMine{}, "pow/Mine", nil) -} diff --git a/examples/democoin/x/simplestake/client/cli/commands.go b/examples/democoin/x/simplestake/client/cli/commands.go index 9f6eb40f7e..d09bf64f37 100644 --- a/examples/democoin/x/simplestake/client/cli/commands.go +++ b/examples/democoin/x/simplestake/client/cli/commands.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" @@ -25,7 +25,7 @@ const ( ) // simple bond tx -func BondTxCmd(cdc *wire.Codec) *cobra.Command { +func BondTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "bond", Short: "Bond to a validator", @@ -79,7 +79,7 @@ func BondTxCmd(cdc *wire.Codec) *cobra.Command { } // simple unbond tx -func UnbondTxCmd(cdc *wire.Codec) *cobra.Command { +func UnbondTxCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "unbond", Short: "Unbond from a validator", diff --git a/examples/democoin/x/simplestake/wire.go b/examples/democoin/x/simplestake/codec.go similarity index 57% rename from examples/democoin/x/simplestake/wire.go rename to examples/democoin/x/simplestake/codec.go index 4ef971b448..7813fd6423 100644 --- a/examples/democoin/x/simplestake/wire.go +++ b/examples/democoin/x/simplestake/codec.go @@ -1,11 +1,11 @@ package simplestake import ( - "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/codec" ) -// Register concrete types on wire codec -func RegisterWire(cdc *wire.Codec) { +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgBond{}, "simplestake/BondMsg", nil) cdc.RegisterConcrete(MsgUnbond{}, "simplestake/UnbondMsg", nil) } diff --git a/examples/democoin/x/simplestake/keeper.go b/examples/democoin/x/simplestake/keeper.go index eb3f340792..7bdc17937b 100644 --- a/examples/democoin/x/simplestake/keeper.go +++ b/examples/democoin/x/simplestake/keeper.go @@ -3,8 +3,8 @@ package simplestake import ( "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -17,13 +17,13 @@ type Keeper struct { ck bank.Keeper key sdk.StoreKey - cdc *wire.Codec + cdc *codec.Codec codespace sdk.CodespaceType } func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper { - cdc := wire.NewCodec() - wire.RegisterCrypto(cdc) + cdc := codec.New() + codec.RegisterCrypto(cdc) return Keeper{ key: key, cdc: cdc, diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 417356f66c..68f28bd91b 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -12,9 +12,9 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" ) @@ -32,7 +32,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, authKey, capKey := setupMultiStore() - cdc := wire.NewCodec() + cdc := codec.New() auth.RegisterBaseAccount(cdc) accountMapper := auth.NewAccountMapper(cdc, authKey, auth.ProtoBaseAccount) @@ -60,7 +60,7 @@ func TestKeeperGetSet(t *testing.T) { func TestBonding(t *testing.T) { ms, authKey, capKey := setupMultiStore() - cdc := wire.NewCodec() + cdc := codec.New() auth.RegisterBaseAccount(cdc) ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger()) diff --git a/examples/kvstore/kvstore b/examples/kvstore/kvstore index 5dd8b5eeaf7814de34a5d4ad3ac4a03eb6e32106..6de98cc563792f9f400bb17077d5d66a2d910500 100755 GIT binary patch delta 3487 zcmZ{mdt6k<6~`TAc`xq;Tzo9@5EYh(JPfaBP^yU%QKPj15fO-0K?V>LA0*-ECTfaA z<6w%tTa6fNVzgSXLG-rq86Zte(uhhl1{#A+3!9oIjW+bmy|c1>`sp9v-<~-$bLPy< z-kE8--|u$dLBHGcKwvnG04LCaGq`{&xPd!(fG6m|3%tPxe8CU=Api^z2tg1GBf$tE zFbYP)7#IsC2!(MF2H_9^kuV;jU;<2pNe~S&V1`(TgLp`Q$&d(1kPIm>1s;J^NP~39 zfJ}H4rouFM46_c+a$z3KhXwEyEQCeyb65<&IPtV+ zv#zLod2x~K)BN<#0%0H2<;@8Z_7A4KF*U+AHs&Q}3VYsXdDW4^Zhw&1yUat4lU+_% z7gn)`g8WgI>jnA95~KiGiWDNtkRoI`QjCZ zxm?&6`zqd#5cW<3&9cw^dK|srF8uq`=y+kX_)=#W$%rl$%Ih5t(Y})D~ z9CH`ZFg7xW28nWF4jnJdf*cy?(2c#4L;W4f@~IrUOXSZmOxpkHc4iR`aO~E*h@Nv6 zj-yNHM9q|ME}>7_lZr2Sn2(xYc?q>wspY*TG-Ry06{f0Z*A$x~man1ZRpqIs8d_df zYEo;osrQx)okLKmy)5I>q}%hTSWPF1l&pj1hpTC@@T$Xll~Mcp$COc%y>o6AjTX&b z7&MO!nzyTHwrGHZ2C`|@_9V0<%c~!b!}m*;s;QSf__6iWIG95u`);u8zk$Yxs_73+ zxq1UlweO1cWg70iqIlKvIfa!gQ^mUQsrbxZQCYRHa8-4&qnF_XJq8CGB^Pg|DNe$H zv0?}7!hC0yRZUL#-w+eFQcs6wa^6;Y#6A}sIh-XlQ2Q69F6y2Js(0wXJ{VFCl?NKA zZ)CW-rRrYew`VHhb23F#nj|3xhYQQL4H#Qo@f=c}u zDudmxYpfMLk2kA#C)!)K3$sCCp_T{jE%Jl*)BBF-vec_$-*c@^ zGo5%>7N2`2llh!)bz$FZ>TqW}&b3}~sEDiR7%87V-};riyE;&;y-&9Eg>5p4kOMwy zT}U2w5ME2?l~&{oau!jLKO%oZ-b2nI=aCD@MdT848F?RRlRK}pD`i?loAp8nn>(%j zakeAkMBwf-ojTb#ts{K6c2TLh6tTjyZFy|zkEdMa!&x03;@gv3Xvap+hf&5_7T0zl zA0U55K14o3t{@*HpCF$ie?dM&uF8vRI~R-7NBntCJT~CsaLl&9Cy81oXwCz0xb_9CdEE&*){8i3?uf9Db z`b;+8v3Fl-zGFY$UpL>GuQjmP%y*ElkzV93@;Brgc7Oc%i z?`N~<{k}#1DeL|2IlZmLk`2newi6=x?#k~h-If1BzDIsQ{*C;I+(#ZD|B<^Ze|qX2 zt^JU*XK~oTItTYS_I=oZcu0wM+S#)=e4x$#D8aKQN}NOD*!eViV3mmCcIjqvjdNx*cMM zJY0x${7VCA7eQa_HW2@VqOfOcAZZq#sFKwZNbYSE!=+6LB1+nnU~<#ys{W0Jk$5R| zK@iE7th0kja(&aaV4^f#8%buxiD12MMzU2rX6tKa8VO1Wmr-0sa~VUFnwewCqv8XS zT1J@2fx#nFTIFOS;%_dU8?@_4f$R&v=C3VTaoTjHMN%>m z?-;4j97n>0|4MBviIUK#S;+Kd({NGvZU~3noI0Pm2zpLJYQVia>Cee^zt@*;{_x58Bwz$J3okuzu>k>6l h-B*V%CrN9|_r4L@fxGgBz7* z)sPGtb=D?oZIq;maSWkMo0QV%+KQ&}QDSVR7}Go?(b!fZId|>?Yd)WTKKi zd(S=h&di-TIDFH6@7{=8*;z^55XLm3%M{49)|g_02V?XJOYn`5gvnlSOkwl0XzXu!eS_dCGZsd z^!U?Whn(xmX^Esh^z@4>xIN;s^wBVG?`oDt)NvcpzBF|fw}l@st)0qk-|*5K&nng| zuULjGM^+%uAVtWt$V#LbDM3mlc|}=gjxt2pWG&0)gS)L|=5FhANI9|!S&dX6YmiE$ zO6s;&@43RKUDjChgd4Xf%r(m4 z3PXi7Q1~{N`YTs>Gh*I-&9g@8A{pmV$(8rXZ=umbqLEJG%>_mp8^N|wW7<$%qR|$| zSJNs}RhFiXR#ld1*cG1kH6slepGCs7ZG0JmlCiarhWuZ7E@H__9tn%TpyDadET_|W zaCkX=Y_!tS(IS-@ETwex0$W0NDGdvEEc}1e?NvsD$Lkid?`uk{%4nRu24M}I&O7#^ zQ&#@tJt;8J+Lb2U8@4N0lrFHmqN1d%s6t~ZD#N|g#IY+0qU6vq{h-m1B^w8&ImRXJW*w4kl0;XK#F^)!*Y4Wmwa zy`E+{-aYRY>dE^kTb<+B)Qw%3EQu{N!+ES7E!^8my~dk<3o1=1GZq(B)Rs7u!lLP^ zKaNU!w$TpzCMk1P@1%zDRKlg5)Z3xyv&-I;G`r}GsmeAe4{T`_)s&W(R1|BpYjxVS zS(>sclX8E|>rPT+En-4yk#$HNvL4xhY(##B)FTbZCZrK*LN+5mN172pT97S>8F?Pr ziflu+Bdy3UkQWdOvIE(P>_T2d+K`u!m!)+lh3C8ZvaZIAOcLbi5mzCquKlaASg_FD z((Wmwt#3a+7Mm_*ZfM`h_g#F`btVawK5J+X<+~>)yUZjBqE$7Ms0H_yc3+_Y+N+hZ zBEeufn?xlzx3o9$AnTtiShrx$Pg)#%8qF9)b|ZU`SCGBPK4d>4B39%8@=N4ZPzH<8~UZy|3Z$B}oC6NrSIMBbIGTRP{=^=6N{&R9}6 zr>eMQrSQe6&R6-4w4Ls3IvOuN#ZMy1>*z%vzJcb*%uC2+ zkL@3IW?OGR@(FSU`4qW|{2lp+Wb3{5?t6T|w&sC!?s}RBGPol!kjh;a45az8t)Ev# z*A$s%P3XVrDI91W2om%GL+<8%Fo671+6RN*9N{C{bl2@;&*-k(PwMY=*9&~cvrM?= z?Ii5@d@xeb6t}BhPP%R|_vo)9pCLDp&yjy2Um#y1Um;&hJ^GtxukuOu_zl@V@Spk( zQ65{Y90$h)w70f)H{+#XC0@png-JA=us1|@DenK_BbCo@CH z6|3^XO(4mldS5Wf6a2JqC0jx-hY&gR@&jZ+63_FTM=06G{~j%O=7$oLFea0kgfodC z@}2pSWHx{9#cXE{Ie7o!CEmbTfs#)&xQtrMZnW}lmnbrSlX7-h2AiV@$`mHiOk$X5 znM`F8%Os9TJd Date: Thu, 13 Sep 2018 23:23:44 +0200 Subject: [PATCH 02/12] Merge PR #2249: Staking Querier pt1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cherry picked commits from prev branch * Added new keepers for querier functionalities * Renaming * Fixed gov errors and messages * Added Querier to stake and app * Update delegation keepers * REST Queriers not working * Fix marshalling error * Querier tests working * Pool and params working * sdk.NewCoin for test handler * Refactor and renaming * Update LCD queries and added more tests for queriers * use sdk.NewCoin * Delegator summary query and tests * Added more tests for keeper * Update PENDING.md * Update stake rest query * Format and replaced panics for sdk.Error * Refactor and addressed comments from Sunny and Aleks * Fixed some of the errors produced by addr type change * Fixed remaining errors * Updated and fixed lite tests * JSON Header and consistency on errors * Increased cov for genesis * Added comment for maxRetrieve param in keepers * Comment on DelegationWithoutDec * Bech32Validator Keepers * Changed Bech validator * Updated remaining tests and bech32 validator * Addressed most of Rigel's comments * Updated tests and types * Make codec to be unexported from keeper * Moved logic to query_utils and updated tests * Fix linter err and PENDING * Fix err * Fix err * Fixed tests * Update PENDING description * Update UpdateBondedValidatorsFull * Update iterator * defer iterator.Close() * delete comment * Address some of Aleks comments, need to fix tests * export querier * Fixed tests * Address Rigel's comments * More tests * return error for GetDelegatorValidator * Fixed conflicts * Fix linter warnings * Address @rigelrozanski comments * Delete comments * wire ––> codec --- Gopkg.lock | 42 +-- Makefile | 4 +- PENDING.md | 3 +- client/lcd/lcd_test.go | 46 +-- client/utils/utils.go | 1 + cmd/gaia/app/app.go | 3 +- x/auth/client/rest/sign.go | 1 + x/stake/app_test.go | 4 +- x/stake/client/rest/query.go | 486 +++++++++++------------------- x/stake/client/rest/utils.go | 144 --------- x/stake/genesis_test.go | 6 + x/stake/handler_test.go | 6 +- x/stake/keeper/delegation.go | 122 +++++--- x/stake/keeper/delegation_test.go | 84 +++++- x/stake/keeper/query_utils.go | 101 +++++++ x/stake/keeper/validator.go | 62 ++-- x/stake/keeper/validator_test.go | 18 +- x/stake/querier/queryable.go | 227 ++++++++++++++ x/stake/querier/queryable_test.go | 215 +++++++++++++ x/stake/stake.go | 7 + x/stake/types/delegation.go | 7 + 21 files changed, 968 insertions(+), 621 deletions(-) create mode 100644 x/stake/keeper/query_utils.go create mode 100644 x/stake/querier/queryable.go create mode 100644 x/stake/querier/queryable_test.go diff --git a/Gopkg.lock b/Gopkg.lock index d5ccc65899..a3aab3a866 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,7 +34,7 @@ [[projects]] branch = "master" - digest = "1:6aabc1566d6351115d561d038da82a4c19b46c3b6e17f4a0a2fa60260663dc79" + digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" @@ -71,7 +71,7 @@ version = "v1.4.7" [[projects]] - digest = "1:fa30c0652956e159cdb97dcb2ef8b8db63ed668c02a5c3a40961c8f0641252fe" + digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" name = "github.com/go-kit/kit" packages = [ "log", @@ -103,7 +103,7 @@ version = "v1.7.0" [[projects]] - digest = "1:212285efb97b9ec2e20550d81f0446cb7897e57cbdfd7301b1363ab113d8be45" + digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -118,7 +118,7 @@ version = "v1.1.1" [[projects]] - digest = "1:cb22af0ed7c72d495d8be1106233ee553898950f15fd3f5404406d44c2e86888" + digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" name = "github.com/golang/protobuf" packages = [ "proto", @@ -165,7 +165,7 @@ [[projects]] branch = "master" - digest = "1:8951fe6e358876736d8fa1f3992624fdbb2dec6bc49401c1381d1ef8abbb544f" + digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -262,7 +262,7 @@ version = "v1.0.0" [[projects]] - digest = "1:98225904b7abff96c052b669b25788f18225a36673fba022fb93514bb9a2a64e" + digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", @@ -273,7 +273,7 @@ [[projects]] branch = "master" - digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "UT" @@ -281,7 +281,7 @@ [[projects]] branch = "master" - digest = "1:dad2e5a2153ee7a6c9ab8fc13673a16ee4fb64434a7da980965a3741b0c981a3" + digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" name = "github.com/prometheus/common" packages = [ "expfmt", @@ -293,7 +293,7 @@ [[projects]] branch = "master" - digest = "1:a37c98f4b7a66bb5c539c0539f0915a74ef1c8e0b3b6f45735289d94cae92bfd" + digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" name = "github.com/prometheus/procfs" packages = [ ".", @@ -312,7 +312,7 @@ revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:37ace7f35375adec11634126944bdc45a673415e2fcc07382d03b75ec76ea94c" + digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" name = "github.com/spf13/afero" packages = [ ".", @@ -331,7 +331,7 @@ version = "v1.2.0" [[projects]] - digest = "1:627ab2f549a6a55c44f46fa24a4307f4d0da81bfc7934ed0473bf38b24051d26" + digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "UT" @@ -363,7 +363,7 @@ version = "v1.0.0" [[projects]] - digest = "1:73697231b93fb74a73ebd8384b68b9a60c57ea6b13c56d2425414566a72c8e6d" + digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" name = "github.com/stretchr/testify" packages = [ "assert", @@ -375,7 +375,7 @@ [[projects]] branch = "master" - digest = "1:442d2ffa75ffae302ce8800bf4144696b92bef02917923ea132ce2d39efe7d65" + digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -396,7 +396,7 @@ [[projects]] branch = "master" - digest = "1:203b409c21115233a576f99e8f13d8e07ad82b25500491f7e1cca12588fb3232" + digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" name = "github.com/tendermint/ed25519" packages = [ ".", @@ -423,7 +423,7 @@ version = "v0.11.0" [[projects]] - digest = "1:963f6c04345ce36f900c1d6367200eebc3cc2db6ee632ff865ea8dcf64b748a0" + digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -490,7 +490,7 @@ version = "v0.23.1-rc0" [[projects]] - digest = "1:ad879bb8c71020a3f92f0c61f414d93eae1d5dc2f37023b6abaa3cc84b00165e" + digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e" name = "github.com/tendermint/tmlibs" packages = ["cli"] pruneopts = "UT" @@ -507,7 +507,7 @@ [[projects]] branch = "master" - digest = "1:2a3ce1f08dcae8bac666deb6e4c88b5d7170c510da38fd746231144cac351704" + digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -529,7 +529,7 @@ revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" [[projects]] - digest = "1:04dda8391c3e2397daf254ac68003f30141c069b228d06baec8324a5f81dc1e9" + digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -546,7 +546,7 @@ [[projects]] branch = "master" - digest = "1:9d9e5fc87553258c36ee18d38023587edd61e4b2521f4473da34b47a83a492e5" + digest = "1:ead82e3e398388679f3ad77633a087ac31a47a6be59ae20841e1d1b3a3fbbd22" name = "golang.org/x/sys" packages = [ "cpu", @@ -556,7 +556,7 @@ revision = "4ea2f632f6e912459fe60b26b1749377f0d889d5" [[projects]] - digest = "1:7509ba4347d1f8de6ae9be8818b0cd1abc3deeffe28aeaf4be6d4b6b5178d9ca" + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -587,7 +587,7 @@ revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" [[projects]] - digest = "1:4515e3030c440845b046354fd5d57671238428b820deebce2e9dabb5cd3c51ac" + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", diff --git a/Makefile b/Makefile index a015e5a6b1..a2c51b6df7 100644 --- a/Makefile +++ b/Makefile @@ -160,8 +160,8 @@ test_sim_gaia_fast: @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=400 -SimulationBlockSize=200 -SimulationCommit=true -v -timeout 24h test_sim_gaia_slow: - @echo "Running full Gaia simulation. This may take awhile!" - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -SimulationCommit=true -v -timeout 24h + @echo "Running full Gaia simulation. This may take a while!" + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -SimulationCommit=true -v -timeout 24h SIM_NUM_BLOCKS ?= 210 SIM_BLOCK_SIZE ?= 200 diff --git a/PENDING.md b/PENDING.md index 6d6754dc5a..f94d1653e8 100644 --- a/PENDING.md +++ b/PENDING.md @@ -101,7 +101,7 @@ IMPROVEMENTS * [x/stake] Improve speed of GetValidator, which was shown to be a performance bottleneck. [#2046](https://github.com/tendermint/tendermint/pull/2200) * [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state. * Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571 - + * SDK * [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present. * [spec] Added simple piggy bank distribution spec @@ -113,6 +113,7 @@ IMPROVEMENTS * [simulation] Add a concept of weighting the operations \#2303 * [simulation] Logs get written to file if large, and also get printed on panics \#2285 * [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable + * [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149) * Tendermint diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 312ac397b2..3d51ca6db2 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -30,7 +30,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) func init() { @@ -510,6 +509,7 @@ func TestBonding(t *testing.T) { cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() + amt := sdk.NewDec(60) validator1Operator := sdk.ValAddress(pks[0].Address()) validator := getValidator(t, port, validator1Operator) @@ -527,18 +527,18 @@ func TestBonding(t *testing.T) { // query validator bond := getDelegation(t, port, addr, validator1Operator) - require.Equal(t, "60.0000000000", bond.Shares) + require.Equal(t, amt, bond.Shares) summary := getDelegationSummary(t, port, addr) require.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations") - require.Equal(t, "60.0000000000", summary.Delegations[0].Shares) + require.Equal(t, amt, summary.Delegations[0].Shares) require.Len(t, summary.UnbondingDelegations, 0, "Delegation summary holds all unbonding-delegations") bondedValidators := getDelegatorValidators(t, port, addr) require.Len(t, bondedValidators, 1) require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr) - require.Equal(t, validator.DelegatorShares.Add(sdk.NewDec(60)).String(), bondedValidators[0].DelegatorShares.String()) + require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String()) bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator) require.Equal(t, validator1Operator, bondedValidator.OperatorAddr) @@ -558,9 +558,8 @@ func TestBonding(t *testing.T) { coins = acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) - unbondings := getUndelegations(t, port, addr, validator1Operator) - require.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") - require.Equal(t, "60", unbondings[0].Balance.Amount.String()) + unbonding := getUndelegation(t, port, addr, validator1Operator) + require.Equal(t, "60", unbonding.Balance.Amount.String()) summary = getDelegationSummary(t, port, addr) @@ -895,43 +894,43 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/signing_info/%s", validatorPubKey), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var signingInfo slashing.ValidatorSigningInfo err := cdc.UnmarshalJSON([]byte(body), &signingInfo) require.Nil(t, err) + return signingInfo } // ============= Stake Module ================ -func getDelegation(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) rest.DelegationWithoutRat { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delAddr, valAddr), nil) +func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Delegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var bond rest.DelegationWithoutRat - + var bond stake.Delegation err := cdc.UnmarshalJSON([]byte(body), &bond) require.Nil(t, err) return bond } -func getUndelegations(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) []stake.UnbondingDelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delAddr, valAddr), nil) +func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.UnbondingDelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var unbondings []stake.UnbondingDelegation - + var unbondings stake.UnbondingDelegation err := cdc.UnmarshalJSON([]byte(body), &unbondings) require.Nil(t, err) return unbondings } -func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary { +func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) stake.DelegationSummary { res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var summary rest.DelegationSummary + var summary stake.DelegationSummary err := cdc.UnmarshalJSON([]byte(body), &summary) require.Nil(t, err) @@ -970,8 +969,8 @@ func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddr return bondedValidators } -func getDelegatorValidator(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) stake.Validator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delAddr, valAddr), nil) +func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.Validator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var bondedValidator stake.Validator @@ -1097,18 +1096,22 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, func getValidators(t *testing.T, port string) []stake.Validator { res, body := Request(t, port, "GET", "/stake/validators", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var validators []stake.Validator err := cdc.UnmarshalJSON([]byte(body), &validators) require.Nil(t, err) + return validators } -func getValidator(t *testing.T, port string, valAddr sdk.ValAddress) stake.Validator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", valAddr.String()), nil) +func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake.Validator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var validator stake.Validator err := cdc.UnmarshalJSON([]byte(body), &validator) require.Nil(t, err) + return validator } @@ -1284,7 +1287,6 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac } }`, proposerAddr, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr) - fmt.Println(res) require.Equal(t, http.StatusOK, res.StatusCode, body) var results ctypes.ResultBroadcastTxCommit diff --git a/client/utils/utils.go b/client/utils/utils.go index 52472ba1eb..81fd138595 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -133,6 +133,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, return txBldr.SignStdTx(name, passphrase, stdTx, appendSig) } +// nolint // SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted int64, err error) { txBytes, err := txBldr.BuildWithPubKey(name, msgs) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 627fb5987e..da79a055a4 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -109,7 +109,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio AddRoute("gov", gov.NewHandler(app.govKeeper)) app.QueryRouter(). - AddRoute("gov", gov.NewQuerier(app.govKeeper)) + AddRoute("gov", gov.NewQuerier(app.govKeeper)). + AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc)) // initialize BaseApp app.SetInitChainer(app.initChainer) diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 7ded69dc58..6894d1c5db 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -22,6 +22,7 @@ type SignBody struct { AppendSig bool `json:"append_sig"` } +// nolint: unparam // sign tx REST handler func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 6542fb1969..922ce8ac3a 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -20,9 +20,9 @@ var ( addr3 = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) priv4 = ed25519.GenPrivKey() addr4 = sdk.AccAddress(priv4.PubKey().Address()) - coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}} + coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))} fee = auth.StdFee{ - sdk.Coins{{"foocoin", sdk.NewInt(0)}}, + sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}, 100000, } ) diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index ca1d71b9ae..c6e39ebd27 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -11,7 +11,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" - "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/gorilla/mux" ) @@ -50,16 +49,16 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co delegationHandlerFn(cliCtx, cdc), ).Methods("GET") - // Query all unbonding_delegations between a delegator and a validator + // Query all unbonding delegations between a delegator and a validator r.HandleFunc( "/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", - unbondingDelegationsHandlerFn(cliCtx, cdc), + unbondingDelegationHandlerFn(cliCtx, cdc), ).Methods("GET") // Get all validators r.HandleFunc( "/stake/validators", - validatorsHandlerFn(cliCtx, cdc), + validatorsHandlerFn(cliCtx), ).Methods("GET") // Get a single validator info @@ -71,107 +70,53 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co // Get the current state of the staking pool r.HandleFunc( "/stake/pool", - poolHandlerFn(cliCtx, cdc), + poolHandlerFn(cliCtx), ).Methods("GET") // Get the current staking parameter values r.HandleFunc( "/stake/parameters", - paramsHandlerFn(cliCtx, cdc), + paramsHandlerFn(cliCtx), ).Methods("GET") } -// already resolve the rational shares to not handle this in the client - -// defines a delegation without type Rat for shares -type DelegationWithoutRat struct { - DelegatorAddr sdk.AccAddress `json:"delegator_addr"` - ValidatorAddr sdk.ValAddress `json:"validator_addr"` - Shares string `json:"shares"` - Height int64 `json:"height"` -} - -// aggregation of all delegations, unbondings and redelegations -type DelegationSummary struct { - Delegations []DelegationWithoutRat `json:"delegations"` - UnbondingDelegations []stake.UnbondingDelegation `json:"unbonding_delegations"` - Redelegations []stake.Redelegation `json:"redelegations"` -} - // HTTP request handler to query a delegator delegations func delegatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var valAddr sdk.ValAddress - var delegationSummary = DelegationSummary{} - - // read parameters vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - // Get all validators using key - validators, statusCode, errMsg, err := getBech32Validators(storeName, cliCtx, cdc) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return + params := stake.QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, } - for _, validator := range validators { - valAddr = validator.OperatorAddr - - // Delegations - delegations, statusCode, errMsg, err := getDelegatorDelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.Delegations = append(delegationSummary.Delegations, delegations) - } - - // Undelegations - unbondingDelegation, statusCode, errMsg, err := getDelegatorUndelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.UnbondingDelegations = append(delegationSummary.UnbondingDelegations, unbondingDelegation) - } - - // Redelegations - // only querying redelegations to a validator as this should give us already all relegations - // if we also would put in redelegations from, we would have every redelegation double - redelegations, statusCode, errMsg, err := getDelegatorRedelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.Redelegations = append(delegationSummary.Redelegations, redelegations) - } - } - - output, err := cdc.MarshalJSON(delegationSummary) + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - w.Write(output) + res, err := cliCtx.QueryWithData("custom/stake/delegator", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + + return + } + + w.Write(res) } } @@ -184,6 +129,8 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han vars := mux.Vars(r) delegatorAddr := vars["delegatorAddr"] + w.Header().Set("Content-Type", "application/json") + _, err := sdk.AccAddressFromBech32(delegatorAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -237,7 +184,7 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han foundTxs, errQuery := queryTxs(node, cdc, action, delegatorAddr) if errQuery != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("error querying transactions. Error: %s", errQuery.Error()))) + w.Write([]byte(errQuery.Error())) } txs = append(txs, foundTxs...) } @@ -253,59 +200,49 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han } // HTTP request handler to query an unbonding-delegation -func unbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { +func unbondingDelegationHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - valAddr, err := sdk.ValAddressFromBech32(bech32validator) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - key := stake.GetUBDKey(delAddr, valAddr) + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } - res, err := cliCtx.QueryStore(key, storeName) + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/stake/unbondingDelegation", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) + w.Write([]byte(err.Error())) + return } - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - ubd, err := types.UnmarshalUBD(cdc, key, res) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't unmarshall unbonding-delegation. Error: %s", err.Error()))) - return - } - - // unbondings will be a list in the future but is not yet, but we want to keep the API consistent - ubdArray := []stake.UnbondingDelegation{ubd} - - output, err := cdc.MarshalJSON(ubdArray) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't marshall unbonding-delegation. Error: %s", err.Error()))) - return - } - - w.Write(output) + w.Write(res) } } @@ -317,70 +254,7 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handl bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - valAddr, err := sdk.ValAddressFromBech32(bech32validator) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - key := stake.GetDelegationKey(delAddr, valAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error()))) - return - } - - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - delegation, err := types.UnmarshalDelegation(cdc, key, res) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - outputDelegation := DelegationWithoutRat{ - DelegatorAddr: delegation.DelegatorAddr, - ValidatorAddr: delegation.ValidatorAddr, - Height: delegation.Height, - Shares: delegation.Shares.String(), - } - - output, err := cdc.MarshalJSON(outputDelegation) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) - } -} - -// HTTP request handler to query all delegator bonded validators -func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - var valAddr sdk.ValAddress - var bondedValidators []types.Validator - - // read parameters - vars := mux.Vars(r) - bech32delegator := vars["delegatorAddr"] + w.Header().Set("Content-Type", "application/json") delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { @@ -389,120 +263,134 @@ func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) h return } - // Get all validators using key - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error()))) - return - } else if len(kvs) == 0 { - // the query will return empty if there are no validators - w.WriteHeader(http.StatusNoContent) - return - } - - validators, err := getValidators(kvs, cdc) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) - return - } - - for _, validator := range validators { - // get all transactions from the delegator to val and append - valAddr = validator.OperatorAddr - - validator, statusCode, errMsg, errRes := getDelegatorValidator(cliCtx, cdc, delegatorAddr, valAddr) - if errRes != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, errRes.Error()))) - return - } else if statusCode == http.StatusNoContent { - continue - } - - bondedValidators = append(bondedValidators, validator) - } - output, err := cdc.MarshalJSON(bondedValidators) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - w.Write(output) + + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/stake/delegation", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + + return + } + + w.Write(res) + } +} + +// HTTP request handler to query all delegator bonded validators +func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // read parameters + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + params := stake.QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/stake/delegatorValidators", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + + return + } + + w.Write(res) } } // HTTP request handler to get information from a currently bonded validator func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // read parameters - var output []byte + vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) - valAddr, err := sdk.ValAddressFromBech32(bech32validator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) - return - } - - // Check if there if the delegator is bonded or redelegated to the validator - - validator, statusCode, errMsg, err := getDelegatorValidator(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } else if statusCode == http.StatusNoContent { - w.WriteHeader(statusCode) - return - } - output, err = cdc.MarshalJSON(validator) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/stake/delegatorValidator", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + + return + } + + w.Write(res) } } -// TODO bech32 -// http request handler to query list of validators -func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { +// HTTP request handler to query list of validators +func validatorsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error()))) - return - } - // the query will return empty if there are no validators - if len(kvs) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } + w.Header().Set("Content-Type", "application/json") - validators, err := getValidators(kvs, cdc) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) - return - } - - output, err := cdc.MarshalJSON(validators) + res, err := cliCtx.QueryWithData("custom/stake/validators", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + w.Header().Set("Content-Type", "application/json") + w.Write(res) } } @@ -510,111 +398,73 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handl func validatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var output []byte - // read parameters vars := mux.Vars(r) bech32validatorAddr := vars["addr"] - valAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - key := stake.GetValidatorKey(valAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validator, error: %s", err.Error()))) - return - } - - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - validator, err := types.UnmarshalValidator(cdc, valAddr, res) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - output, err = cdc.MarshalJSON(validator) + params := stake.QueryValidatorParams{ + ValidatorAddr: validatorAddr, + } + + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - if output == nil { - w.WriteHeader(http.StatusNoContent) + res, err := cliCtx.QueryWithData("custom/stake/validator", bz) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return } - w.Write(output) + + w.Write(res) } } // HTTP request handler to query the pool information -func poolHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { +func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - key := stake.PoolKey - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query pool. Error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - pool, err := types.UnmarshalPool(cdc, res) + res, err := cliCtx.QueryWithData("custom/stake/pool", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - output, err := cdc.MarshalJSON(pool) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) + w.Write(res) } } // HTTP request handler to query the staking params values -func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { +func paramsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - key := stake.ParamKey - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query parameters. Error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - params, err := types.UnmarshalParams(cdc, res) + res, err := cliCtx.QueryWithData("custom/stake/parameters", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - output, err := cdc.MarshalJSON(params) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) + w.Write(res) } } diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index 6bd91e7401..afe672840c 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -2,15 +2,10 @@ package rest import ( "fmt" - "net/http" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" - "github.com/cosmos/cosmos-sdk/x/stake/types" rpcclient "github.com/tendermint/tendermint/rpc/client" ) @@ -24,106 +19,6 @@ func contains(stringSlice []string, txType string) bool { return false } -func getDelegatorValidator(cliCtx context.CLIContext, cdc *codec.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - validator types.Validator, httpStatusCode int, errMsg string, err error) { - - key := stake.GetDelegationKey(delAddr, valAddr) - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - return types.Validator{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err - } - if len(res) == 0 { - return types.Validator{}, http.StatusNoContent, "", nil - } - - key = stake.GetValidatorKey(valAddr) - res, err = cliCtx.QueryStore(key, storeName) - if err != nil { - return types.Validator{}, http.StatusInternalServerError, "couldn't query validator. Error: ", err - } - if len(res) == 0 { - return types.Validator{}, http.StatusNoContent, "", nil - } - validator, err = types.UnmarshalValidator(cdc, valAddr, res) - if err != nil { - return types.Validator{}, http.StatusBadRequest, "", err - } - - return validator, http.StatusOK, "", nil -} - -func getDelegatorDelegations( - cliCtx context.CLIContext, cdc *codec.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - outputDelegation DelegationWithoutRat, httpStatusCode int, errMsg string, err error) { - - delegationKey := stake.GetDelegationKey(delAddr, valAddr) - marshalledDelegation, err := cliCtx.QueryStore(delegationKey, storeName) - if err != nil { - return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err - } - - if len(marshalledDelegation) == 0 { - return DelegationWithoutRat{}, http.StatusNoContent, "", nil - } - - delegation, err := types.UnmarshalDelegation(cdc, delegationKey, marshalledDelegation) - if err != nil { - return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't unmarshall delegation. Error: ", err - } - - outputDelegation = DelegationWithoutRat{ - DelegatorAddr: delegation.DelegatorAddr, - ValidatorAddr: delegation.ValidatorAddr, - Height: delegation.Height, - Shares: delegation.Shares.String(), - } - - return outputDelegation, http.StatusOK, "", nil -} - -func getDelegatorUndelegations( - cliCtx context.CLIContext, cdc *codec.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - unbonds types.UnbondingDelegation, httpStatusCode int, errMsg string, err error) { - - undelegationKey := stake.GetUBDKey(delAddr, valAddr) - marshalledUnbondingDelegation, err := cliCtx.QueryStore(undelegationKey, storeName) - if err != nil { - return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't query unbonding-delegation. Error: ", err - } - - if len(marshalledUnbondingDelegation) == 0 { - return types.UnbondingDelegation{}, http.StatusNoContent, "", nil - } - - unbondingDelegation, err := types.UnmarshalUBD(cdc, undelegationKey, marshalledUnbondingDelegation) - if err != nil { - return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't unmarshall unbonding-delegation. Error: ", err - } - return unbondingDelegation, http.StatusOK, "", nil -} - -func getDelegatorRedelegations( - cliCtx context.CLIContext, cdc *codec.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - regelegations types.Redelegation, httpStatusCode int, errMsg string, err error) { - - key := stake.GetREDsByDelToValDstIndexKey(delAddr, valAddr) - marshalledRedelegations, err := cliCtx.QueryStore(key, storeName) - if err != nil { - return types.Redelegation{}, http.StatusInternalServerError, "couldn't query redelegation. Error: ", err - } - - if len(marshalledRedelegations) == 0 { - return types.Redelegation{}, http.StatusNoContent, "", nil - } - - redelegations, err := types.UnmarshalRED(cdc, key, marshalledRedelegations) - if err != nil { - return types.Redelegation{}, http.StatusInternalServerError, "couldn't unmarshall redelegations. Error: ", err - } - - return redelegations, http.StatusOK, "", nil -} - // queries staking txs func queryTxs(node rpcclient.Client, cdc *codec.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { page := 0 @@ -137,42 +32,3 @@ func queryTxs(node rpcclient.Client, cdc *codec.Codec, tag string, delegatorAddr return tx.FormatTxResults(cdc, res.Txs) } - -// gets all validators -func getValidators(validatorKVs []sdk.KVPair, cdc *codec.Codec) ([]types.Validator, error) { - validators := make([]types.Validator, len(validatorKVs)) - for i, kv := range validatorKVs { - - addr := kv.Key[1:] - validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) - if err != nil { - return nil, err - } - - validators[i] = validator - } - return validators, nil -} - -// gets all Bech32 validators from a key -// nolint: unparam -func getBech32Validators(storeName string, cliCtx context.CLIContext, cdc *codec.Codec) ( - validators []types.Validator, httpStatusCode int, errMsg string, err error) { - - // Get all validators using key - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) - if err != nil { - return nil, http.StatusInternalServerError, "couldn't query validators. Error: ", err - } - - // the query will return empty if there are no validators - if len(kvs) == 0 { - return nil, http.StatusNoContent, "", nil - } - - validators, err = getValidators(kvs, cdc) - if err != nil { - return nil, http.StatusInternalServerError, "Error: ", err - } - return validators, http.StatusOK, "", nil -} diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index ddd29f6f84..947bb2e185 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -42,6 +42,12 @@ func TestInitGenesis(t *testing.T) { vals, err := InitGenesis(ctx, keeper, genesisState) require.NoError(t, err) + actualGenesis := WriteGenesis(ctx, keeper) + require.Equal(t, genesisState.Pool, actualGenesis.Pool) + require.Equal(t, genesisState.Params, actualGenesis.Params) + require.Equal(t, genesisState.Bonds, actualGenesis.Bonds) + require.EqualValues(t, keeper.GetAllValidators(ctx), actualGenesis.Validators) + // now make sure the validators are bonded and intra-tx counters are correct resVal, found := keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[0])) require.True(t, found) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 4862b14c33..70a21c171c 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -17,14 +17,14 @@ import ( //______________________________________________________________________ func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator { - return types.NewMsgCreateValidator(address, pubKey, sdk.Coin{"steak", sdk.NewInt(amt)}, Description{}) + return types.NewMsgCreateValidator(address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}) } func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate { return MsgDelegate{ DelegatorAddr: delAddr, ValidatorAddr: valAddr, - Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, + Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), } } @@ -34,7 +34,7 @@ func newTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.Val DelegatorAddr: delAddr, ValidatorAddr: valAddr, PubKey: valPubKey, - Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, + Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), } } diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 0ef3eb9cfb..ef09a307e1 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake/types" ) -// load a delegation +// return a specific delegation func (k Keeper) GetDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( delegation types.Delegation, found bool) { @@ -24,44 +24,37 @@ func (k Keeper) GetDelegation(ctx sdk.Context, return delegation, true } -// load all delegations used during genesis dump +// return all delegations used during genesis dump func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, DelegationKey) + defer iterator.Close() - i := 0 - for ; ; i++ { - if !iterator.Valid() { - break - } + for ; iterator.Valid(); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) delegations = append(delegations, delegation) - iterator.Next() } - iterator.Close() return delegations } -// load all delegations for a delegator -func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.AccAddress, - maxRetrieve int16) (delegations []types.Delegation) { +// return a given amount of all the delegations from a delegator +func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (delegations []types.Delegation) { + + delegations = make([]types.Delegation, maxRetrieve) store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegationsKey(delegator) - iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() - delegations = make([]types.Delegation, maxRetrieve) i := 0 - for ; ; i++ { - if !iterator.Valid() || i > int(maxRetrieve-1) { - break - } + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) delegations[i] = delegation - iterator.Next() + i++ } - iterator.Close() - return delegations[:i] // trim + return delegations[:i] // trim if the array length < maxRetrieve } // set the delegation @@ -71,7 +64,7 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr), b) } -// remove the delegation +// remove a delegation from store func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { store := ctx.KVStore(k.storeKey) store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr)) @@ -79,7 +72,27 @@ func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { //_____________________________________________________________________________________ -// load a unbonding delegation +// return a given amount of all the delegator unbonding-delegations +func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (unbondingDelegations []types.UnbondingDelegation) { + + unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Key(), iterator.Value()) + unbondingDelegations[i] = unbondingDelegation + i++ + } + return unbondingDelegations[:i] // trim if the array length < maxRetrieve +} + +// return a unbonding delegation func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) { @@ -94,21 +107,18 @@ func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, return ubd, true } -// load all unbonding delegations from a particular validator +// return all unbonding delegations from a particular validator func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, GetUBDsByValIndexKey(valAddr)) - for { - if !iterator.Valid() { - break - } + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { key := GetUBDKeyFromValIndexKey(iterator.Key()) value := store.Get(key) ubd := types.MustUnmarshalUBD(k.cdc, key, value) ubds = append(ubds, ubd) - iterator.Next() } - iterator.Close() return ubds } @@ -116,16 +126,15 @@ func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sd func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, UnbondingDelegationKey) - i := int64(0) - for ; iterator.Valid(); iterator.Next() { + defer iterator.Close() + + for i := int64(0); iterator.Valid(); iterator.Next() { ubd := types.MustUnmarshalUBD(k.cdc, iterator.Key(), iterator.Value()) - stop := fn(i, ubd) - if stop { + if stop := fn(i, ubd); stop { break } i++ } - iterator.Close() } // set the unbonding delegation and associated index @@ -147,7 +156,26 @@ func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDe //_____________________________________________________________________________________ -// load a redelegation +// return a given amount of all the delegator redelegations +func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (redelegations []types.Redelegation) { + redelegations = make([]types.Redelegation, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + redelegations[i] = redelegation + i++ + } + return redelegations[:i] // trim if the array length < maxRetrieve +} + +// return a redelegation func (k Keeper) GetRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) { @@ -162,38 +190,34 @@ func (k Keeper) GetRedelegation(ctx sdk.Context, return red, true } -// load all redelegations from a particular validator +// return all redelegations from a particular validator func (k Keeper) GetRedelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, GetREDsFromValSrcIndexKey(valAddr)) - for { - if !iterator.Valid() { - break - } + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { key := GetREDKeyFromValSrcIndexKey(iterator.Key()) value := store.Get(key) red := types.MustUnmarshalRED(k.cdc, key, value) reds = append(reds, red) - iterator.Next() } - iterator.Close() return reds } -// has a redelegation +// check if validator is receiving a redelegation func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool { store := ctx.KVStore(k.storeKey) prefix := GetREDsByDelToValDstIndexKey(delAddr, valDstAddr) - iterator := sdk.KVStorePrefixIterator(store, prefix) //smallest to largest + iterator := sdk.KVStorePrefixIterator(store, prefix) + defer iterator.Close() found := false if iterator.Valid() { - //record found found = true } - iterator.Close() return found } @@ -358,7 +382,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, // create the unbonding delegation params := k.GetParams(ctx) minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr) - balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} + balance := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) // no need to create the ubd object just complete now if completeNow { @@ -418,7 +442,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } params := k.GetParams(ctx) - returnCoin := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} + returnCoin := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) dstValidator, found := k.GetValidator(ctx, valDstAddr) if !found { return types.ErrBadRedelegationDst(k.Codespace()) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 023642bb22..ed65f1f404 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -// tests GetDelegation, GetDelegations, SetDelegation, RemoveDelegation, GetDelegations +// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations func TestDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 10) pool := keeper.GetPool(ctx) @@ -30,6 +30,7 @@ func TestDelegation(t *testing.T) { validators[2] = keeper.UpdateValidator(ctx, validators[2]) // first add a validators[0] to delegate too + bond1to1 := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[0], @@ -66,16 +67,16 @@ func TestDelegation(t *testing.T) { keeper.SetDelegation(ctx, bond2to3) // test all bond retrieve capabilities - resBonds := keeper.GetDelegations(ctx, addrDels[0], 5) + resBonds := keeper.GetDelegatorDelegations(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) require.True(t, bond1to1.Equal(resBonds[0])) require.True(t, bond1to2.Equal(resBonds[1])) require.True(t, bond1to3.Equal(resBonds[2])) - resBonds = keeper.GetDelegations(ctx, addrDels[0], 3) + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[0]) require.Equal(t, 3, len(resBonds)) - resBonds = keeper.GetDelegations(ctx, addrDels[0], 2) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) require.True(t, bond2to1.Equal(resBonds[0])) require.True(t, bond2to2.Equal(resBonds[1])) @@ -89,15 +90,34 @@ func TestDelegation(t *testing.T) { require.True(t, bond2to2.Equal(allBonds[4])) require.True(t, bond2to3.Equal(allBonds[5])) + resVals := keeper.GetDelegatorValidators(ctx, addrDels[0], 3) + require.Equal(t, 3, len(resVals)) + resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4) + require.Equal(t, 3, len(resVals)) + + for i := 0; i < 3; i++ { + + resVal, err := keeper.GetDelegatorValidator(ctx, addrDels[0], addrVals[i]) + require.Nil(t, err) + require.Equal(t, addrVals[i], resVal.GetOperator()) + + resVal, err = keeper.GetDelegatorValidator(ctx, addrDels[1], addrVals[i]) + require.Nil(t, err) + require.Equal(t, addrVals[i], resVal.GetOperator()) + } + // delete a record keeper.RemoveDelegation(ctx, bond2to3) _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2]) require.False(t, found) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 2, len(resBonds)) require.True(t, bond2to1.Equal(resBonds[0])) require.True(t, bond2to2.Equal(resBonds[1])) + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[1]) + require.Equal(t, 2, len(resBonds)) + // delete all the records from delegator 2 keeper.RemoveDelegation(ctx, bond2to1) keeper.RemoveDelegation(ctx, bond2to2) @@ -105,7 +125,7 @@ func TestDelegation(t *testing.T) { require.False(t, found) _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1]) require.False(t, found) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 0, len(resBonds)) } @@ -123,21 +143,35 @@ func TestUnbondingDelegation(t *testing.T) { // set and retrieve a record keeper.SetUnbondingDelegation(ctx, ubd) - resBond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + resUnbond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) - require.True(t, ubd.Equal(resBond)) + require.True(t, ubd.Equal(resUnbond)) // modify a records, save, and retrieve ubd.Balance = sdk.NewInt64Coin("steak", 21) keeper.SetUnbondingDelegation(ctx, ubd) - resBond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + + resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(resUnbonds)) + + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + require.Equal(t, 1, len(resUnbonds)) + + resUnbond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) - require.True(t, ubd.Equal(resBond)) + require.True(t, ubd.Equal(resUnbond)) // delete a record keeper.RemoveUnbondingDelegation(ctx, ubd) _, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.False(t, found) + + resUnbonds = keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(resUnbonds)) + + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + require.Equal(t, 0, len(resUnbonds)) + } func TestUnbondDelegation(t *testing.T) { @@ -413,12 +447,20 @@ func TestRedelegation(t *testing.T) { // set and retrieve a record keeper.SetRedelegation(ctx, rd) - resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) redelegations := keeper.GetRedelegationsFromValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) // check if has the redelegation has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) @@ -429,18 +471,28 @@ func TestRedelegation(t *testing.T) { rd.SharesDst = sdk.NewDec(21) keeper.SetRedelegation(ctx, rd) - resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) - require.True(t, rd.Equal(resBond)) + require.True(t, rd.Equal(resRed)) redelegations = keeper.GetRedelegationsFromValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) // delete a record keeper.RemoveRedelegation(ctx, rd) _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.False(t, found) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(redelegations)) + + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0]) + require.Equal(t, 0, len(redelegations)) } func TestRedelegateSelfDelegation(t *testing.T) { diff --git a/x/stake/keeper/query_utils.go b/x/stake/keeper/query_utils.go new file mode 100644 index 0000000000..c1575cd421 --- /dev/null +++ b/x/stake/keeper/query_utils.go @@ -0,0 +1,101 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" +) + +// Return all validators that a delegator is bonded to. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorValidators(ctx sdk.Context, delegatorAddr sdk.AccAddress, + maxRetrieve uint16) (validators []types.Validator) { + validators = make([]types.Validator, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetDelegationsKey(delegatorAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + addr := iterator.Key() + delegation := types.MustUnmarshalDelegation(k.cdc, addr, iterator.Value()) + + validator, found := k.GetValidator(ctx, delegation.ValidatorAddr) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + validators[i] = validator + i++ + } + return validators[:i] // trim +} + +// return a validator that a delegator is bonded to +func (k Keeper) GetDelegatorValidator(ctx sdk.Context, delegatorAddr sdk.AccAddress, + validatorAddr sdk.ValAddress) (validator types.Validator, err sdk.Error) { + + delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) + if !found { + return validator, types.ErrNoDelegation(types.DefaultCodespace) + } + + validator, found = k.GetValidator(ctx, delegation.ValidatorAddr) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + return +} + +//_____________________________________________________________________________________ + +// return all delegations for a delegator +func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) ( + delegations []types.Delegation) { + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) + delegations = append(delegations, delegation) + i++ + } + return delegations +} + +// return all unbonding-delegations for a delegator +func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress) ( + unbondingDelegations []types.UnbondingDelegation) { + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid(); iterator.Next() { + unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Key(), iterator.Value()) + unbondingDelegations = append(unbondingDelegations, unbondingDelegation) + i++ + } + return unbondingDelegations +} + +// return all redelegations for a delegator +func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress) (redelegations []types.Redelegation) { + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid(); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + redelegations = append(redelegations, redelegation) + i++ + } + return redelegations +} diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 1f65aaa957..996bf06bf5 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -103,39 +103,32 @@ func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) + defer iterator.Close() - i := 0 - for ; ; i++ { - if !iterator.Valid() { - break - } + for ; iterator.Valid(); iterator.Next() { addr := iterator.Key()[1:] validator := types.MustUnmarshalValidator(k.cdc, addr, iterator.Value()) validators = append(validators, validator) - iterator.Next() } - iterator.Close() return validators } -// Get the set of all validators, retrieve a maxRetrieve number of records -func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators []types.Validator) { +// return a given amount of all the validators +func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) - validators = make([]types.Validator, maxRetrieve) + + iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) + defer iterator.Close() + i := 0 - for ; ; i++ { - if !iterator.Valid() || i > int(maxRetrieve-1) { - break - } + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { addr := iterator.Key()[1:] validator := types.MustUnmarshalValidator(k.cdc, addr, iterator.Value()) validators[i] = validator - iterator.Next() + i++ } - iterator.Close() - return validators[:i] // trim + return validators[:i] // trim if the array length < maxRetrieve } //___________________________________________________________________________ @@ -149,6 +142,8 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat validators = make([]types.Validator, maxValidators) iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey) + defer iterator.Close() + i := 0 for ; iterator.Valid(); iterator.Next() { @@ -163,7 +158,6 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat validators[i] = validator i++ } - iterator.Close() return validators[:i] // trim } @@ -174,12 +168,12 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) maxValidators := k.GetParams(ctx).MaxValidators validators := make([]types.Validator, maxValidators) - iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest + + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + defer iterator.Close() + i := 0 - for { - if !iterator.Valid() || i > int(maxValidators-1) { - break - } + for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { address := iterator.Value() validator, found := k.GetValidator(ctx, address) ensureValidatorFound(found, address) @@ -188,9 +182,7 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { validators[i] = validator i++ } - iterator.Next() } - iterator.Close() return validators[:i] // trim } @@ -206,6 +198,8 @@ func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Valid tstore := ctx.TransientStore(k.storeTKey) iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { var abciVal abci.Validator @@ -228,8 +222,6 @@ func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Valid updates = append(updates, abciVal) } } - - iterator.Close() return } @@ -447,10 +439,7 @@ func (k Keeper) UpdateBondedValidators( // create a validator iterator ranging from largest to smallest by power iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) - for { - if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { - break - } + for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() { // either retrieve the original validator from the store, or under the // situation that this is the "affected validator" just use the @@ -484,7 +473,6 @@ func (k Keeper) UpdateBondedValidators( } bondedValidatorsCount++ - iterator.Next() } iterator.Close() @@ -551,11 +539,7 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { bondedValidatorsCount := 0 iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) - for { - if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { - break - } - + for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() { var found bool ownerAddr := iterator.Value() @@ -578,12 +562,10 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { if validator.Status == sdk.Bonded { panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr)) } - break } bondedValidatorsCount++ - iterator.Next() } iterator.Close() diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 89dd40677b..e23d3b0aad 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -59,11 +59,22 @@ func TestSetValidator(t *testing.T) { resVals = keeper.GetValidatorsByPower(ctx) require.Equal(t, 1, len(resVals)) - assert.True(ValEq(t, validator, resVals[0])) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx, 1) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx, 10) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) updates := keeper.GetValidTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) require.Equal(t, validator.ABCIValidator(), updates[0]) + + allVals := keeper.GetAllValidators(ctx) + require.Equal(t, 1, len(allVals)) } func TestUpdateValidatorByPowerIndex(t *testing.T) { @@ -283,7 +294,10 @@ func TestValidatorBasics(t *testing.T) { _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) resVals := keeper.GetValidatorsBonded(ctx) - assert.Zero(t, len(resVals)) + require.Zero(t, len(resVals)) + + resVals = keeper.GetValidators(ctx, 2) + require.Zero(t, len(resVals)) pool = keeper.GetPool(ctx) assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens)) diff --git a/x/stake/querier/queryable.go b/x/stake/querier/queryable.go new file mode 100644 index 0000000000..8537f2bd49 --- /dev/null +++ b/x/stake/querier/queryable.go @@ -0,0 +1,227 @@ +package querier + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" + "github.com/cosmos/cosmos-sdk/x/stake/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// query endpoints supported by the staking Querier +const ( + QueryValidators = "validators" + QueryValidator = "validator" + QueryDelegator = "delegator" + QueryDelegation = "delegation" + QueryUnbondingDelegation = "unbondingDelegation" + QueryDelegatorValidators = "delegatorValidators" + QueryDelegatorValidator = "delegatorValidator" + QueryPool = "pool" + QueryParameters = "parameters" +) + +// creates a querier for staking REST endpoints +func NewQuerier(k keep.Keeper, cdc *codec.Codec) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case QueryValidators: + return queryValidators(ctx, cdc, k) + case QueryValidator: + return queryValidator(ctx, cdc, req, k) + case QueryDelegator: + return queryDelegator(ctx, cdc, req, k) + case QueryDelegation: + return queryDelegation(ctx, cdc, req, k) + case QueryUnbondingDelegation: + return queryUnbondingDelegation(ctx, cdc, req, k) + case QueryDelegatorValidators: + return queryDelegatorValidators(ctx, cdc, req, k) + case QueryDelegatorValidator: + return queryDelegatorValidator(ctx, cdc, req, k) + case QueryPool: + return queryPool(ctx, cdc, k) + case QueryParameters: + return queryParameters(ctx, cdc, k) + default: + return nil, sdk.ErrUnknownRequest("unknown stake query endpoint") + } + } +} + +// defines the params for the following queries: +// - 'custom/stake/delegator' +// - 'custom/stake/delegatorValidators' +type QueryDelegatorParams struct { + DelegatorAddr sdk.AccAddress +} + +// defines the params for the following queries: +// - 'custom/stake/validator' +type QueryValidatorParams struct { + ValidatorAddr sdk.ValAddress +} + +// defines the params for the following queries: +// - 'custom/stake/delegation' +// - 'custom/stake/unbondingDelegation' +// - 'custom/stake/delegatorValidator' +type QueryBondsParams struct { + DelegatorAddr sdk.AccAddress + ValidatorAddr sdk.ValAddress +} + +func queryValidators(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []byte, err sdk.Error) { + stakeParams := k.GetParams(ctx) + validators := k.GetValidators(ctx, stakeParams.MaxValidators) + + res, errRes := codec.MarshalJSONIndent(cdc, validators) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryValidator(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryValidatorParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", err.Error())) + } + + validator, found := k.GetValidator(ctx, params.ValidatorAddr) + if !found { + return []byte{}, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + res, errRes = codec.MarshalJSONIndent(cdc, validator) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegator(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryDelegatorParams + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + delegations := k.GetAllDelegatorDelegations(ctx, params.DelegatorAddr) + unbondingDelegations := k.GetAllUnbondingDelegations(ctx, params.DelegatorAddr) + redelegations := k.GetAllRedelegations(ctx, params.DelegatorAddr) + + summary := types.DelegationSummary{ + Delegations: delegations, + UnbondingDelegations: unbondingDelegations, + Redelegations: redelegations, + } + + res, errRes = codec.MarshalJSONIndent(cdc, summary) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegatorValidators(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryDelegatorParams + + stakeParams := k.GetParams(ctx) + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + validators := k.GetDelegatorValidators(ctx, params.DelegatorAddr, stakeParams.MaxValidators) + + res, errRes = codec.MarshalJSONIndent(cdc, validators) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegatorValidator(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + validator, err := k.GetDelegatorValidator(ctx, params.DelegatorAddr, params.ValidatorAddr) + if err != nil { + return + } + + res, errRes = codec.MarshalJSONIndent(cdc, validator) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegation(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + delegation, found := k.GetDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return []byte{}, types.ErrNoDelegation(types.DefaultCodespace) + } + + res, errRes = codec.MarshalJSONIndent(cdc, delegation) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryUnbondingDelegation(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + unbond, found := k.GetUnbondingDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return []byte{}, types.ErrNoUnbondingDelegation(types.DefaultCodespace) + } + + res, errRes = codec.MarshalJSONIndent(cdc, unbond) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryPool(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []byte, err sdk.Error) { + pool := k.GetPool(ctx) + + res, errRes := codec.MarshalJSONIndent(cdc, pool) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryParameters(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []byte, err sdk.Error) { + params := k.GetParams(ctx) + + res, errRes := codec.MarshalJSONIndent(cdc, params) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/queryable_test.go new file mode 100644 index 0000000000..ee52773c6e --- /dev/null +++ b/x/stake/querier/queryable_test.go @@ -0,0 +1,215 @@ +package querier + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" + "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" +) + +var ( + addrAcc1, addrAcc2 = keep.Addrs[0], keep.Addrs[1] + addrVal1, addrVal2 = sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) + pk1, pk2 = keep.PKs[0], keep.PKs[1] +) + +func newTestDelegatorQuery(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +func newTestValidatorQuery(validatorAddr sdk.ValAddress) QueryValidatorParams { + return QueryValidatorParams{ + ValidatorAddr: validatorAddr, + } +} + +func newTestBondQuery(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryBondsParams { + return QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } +} + +func TestQueryParametersPool(t *testing.T) { + cdc := codec.New() + ctx, _, keeper := keep.CreateTestInput(t, false, 1000) + + res, err := queryParameters(ctx, cdc, keeper) + require.Nil(t, err) + + var params types.Params + errRes := cdc.UnmarshalJSON(res, ¶ms) + require.Nil(t, errRes) + require.Equal(t, keeper.GetParams(ctx), params) + + res, err = queryPool(ctx, cdc, keeper) + require.Nil(t, err) + + var pool types.Pool + errRes = cdc.UnmarshalJSON(res, &pool) + require.Nil(t, errRes) + require.Equal(t, keeper.GetPool(ctx), pool) +} + +func TestQueryValidators(t *testing.T) { + cdc := codec.New() + ctx, _, keeper := keep.CreateTestInput(t, false, 10000) + pool := keeper.GetPool(ctx) + params := keeper.GetParams(ctx) + + // Create Validators + amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8)} + var validators [2]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(sdk.ValAddress(keep.Addrs[i]), keep.PKs[i], types.Description{}) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt) + } + keeper.SetPool(ctx, pool) + validators[0] = keeper.UpdateValidator(ctx, validators[0]) + validators[1] = keeper.UpdateValidator(ctx, validators[1]) + + // Query Validators + queriedValidators := keeper.GetValidators(ctx, params.MaxValidators) + + res, err := queryValidators(ctx, cdc, keeper) + require.Nil(t, err) + + var validatorsResp []types.Validator + errRes := cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, errRes) + + require.Equal(t, len(queriedValidators), len(validatorsResp)) + require.ElementsMatch(t, queriedValidators, validatorsResp) + + // Query each validator + queryParams := newTestValidatorQuery(addrVal1) + bz, errRes := cdc.MarshalJSON(queryParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/stake/validator", + Data: bz, + } + res, err = queryValidator(ctx, cdc, query, keeper) + require.Nil(t, err) + + var validator types.Validator + errRes = cdc.UnmarshalJSON(res, &validator) + require.Nil(t, errRes) + + require.Equal(t, queriedValidators[0], validator) +} + +func TestQueryDelegation(t *testing.T) { + cdc := codec.New() + ctx, _, keeper := keep.CreateTestInput(t, false, 10000) + params := keeper.GetParams(ctx) + + // Create Validators and Delegation + val1 := types.NewValidator(addrVal1, pk1, types.Description{}) + keeper.SetValidator(ctx, val1) + + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true) + + // Query Delegator bonded validators + queryParams := newTestDelegatorQuery(addrAcc2) + bz, errRes := cdc.MarshalJSON(queryParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/stake/delegatorValidators", + Data: bz, + } + + delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators) + + res, err := queryDelegatorValidators(ctx, cdc, query, keeper) + require.Nil(t, err) + + var validatorsResp []types.Validator + errRes = cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, errRes) + + require.Equal(t, len(delValidators), len(validatorsResp)) + require.ElementsMatch(t, delValidators, validatorsResp) + + // Query bonded validator + queryBondParams := newTestBondQuery(addrAcc2, addrVal1) + bz, errRes = cdc.MarshalJSON(queryBondParams) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/stake/delegatorValidator", + Data: bz, + } + + res, err = queryDelegatorValidator(ctx, cdc, query, keeper) + require.Nil(t, err) + + var validator types.Validator + errRes = cdc.UnmarshalJSON(res, &validator) + require.Nil(t, errRes) + + require.Equal(t, delValidators[0], validator) + + // Query delegation + + query = abci.RequestQuery{ + Path: "/custom/stake/delegation", + Data: bz, + } + + delegation, found := keeper.GetDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + res, err = queryDelegation(ctx, cdc, query, keeper) + require.Nil(t, err) + + var delegationRes types.Delegation + errRes = cdc.UnmarshalJSON(res, &delegationRes) + require.Nil(t, errRes) + + require.Equal(t, delegation, delegationRes) + + // Query unbonging delegation + keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10)) + + query = abci.RequestQuery{ + Path: "/custom/stake/unbondingDelegation", + Data: bz, + } + + unbond, found := keeper.GetUnbondingDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + res, err = queryUnbondingDelegation(ctx, cdc, query, keeper) + require.Nil(t, err) + + var unbondRes types.UnbondingDelegation + errRes = cdc.UnmarshalJSON(res, &unbondRes) + require.Nil(t, errRes) + + require.Equal(t, unbond, unbondRes) + + // Query Delegator Summary + + query = abci.RequestQuery{ + Path: "/custom/stake/delegator", + Data: bz, + } + + res, err = queryDelegator(ctx, cdc, query, keeper) + require.Nil(t, err) + + var summary types.DelegationSummary + errRes = cdc.UnmarshalJSON(res, &summary) + require.Nil(t, errRes) + + require.Equal(t, unbond, summary.UnbondingDelegations[0]) +} diff --git a/x/stake/stake.go b/x/stake/stake.go index 74be3a702e..18b99fd4a0 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -3,6 +3,7 @@ package stake import ( "github.com/cosmos/cosmos-sdk/x/stake/keeper" + "github.com/cosmos/cosmos-sdk/x/stake/querier" "github.com/cosmos/cosmos-sdk/x/stake/tags" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -12,6 +13,7 @@ type ( Validator = types.Validator Description = types.Description Delegation = types.Delegation + DelegationSummary = types.DelegationSummary UnbondingDelegation = types.UnbondingDelegation Redelegation = types.Redelegation Params = types.Params @@ -24,6 +26,9 @@ type ( MsgBeginRedelegate = types.MsgBeginRedelegate MsgCompleteRedelegate = types.MsgCompleteRedelegate GenesisState = types.GenesisState + QueryDelegatorParams = querier.QueryDelegatorParams + QueryValidatorParams = querier.QueryValidatorParams + QueryBondsParams = querier.QueryBondsParams ) var ( @@ -75,6 +80,8 @@ var ( NewMsgCompleteUnbonding = types.NewMsgCompleteUnbonding NewMsgBeginRedelegate = types.NewMsgBeginRedelegate NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate + + NewQuerier = querier.NewQuerier ) const ( diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index e6bf11e732..38a94c3695 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -24,6 +24,13 @@ type delegationValue struct { Height int64 } +// aggregates of all delegations, unbondings and redelegations +type DelegationSummary struct { + Delegations []Delegation `json:"delegations"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations"` +} + // return the delegation without fields contained within the key for the store func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { val := delegationValue{ From f19fbe3fb837c7b7c8bc704fcd479891fe77dec2 Mon Sep 17 00:00:00 2001 From: Joon Date: Fri, 14 Sep 2018 10:53:21 +0900 Subject: [PATCH 03/12] Merge PR #2330: iterate over storesParams --- store/rootmultistore.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 8aa2da0ba3..7de465f770 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -119,28 +119,28 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { return err } + // Convert StoreInfos slice to map + infos := make(map[StoreKey]storeInfo) + for _, storeInfo := range cInfo.StoreInfos { + infos[rs.nameToKey(storeInfo.Name)] = storeInfo + } + // Load each Store var newStores = make(map[StoreKey]CommitStore) - for _, storeInfo := range cInfo.StoreInfos { - key, commitID := rs.nameToKey(storeInfo.Name), storeInfo.Core.CommitID - storeParams := rs.storesParams[key] - store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) + for key, storeParams := range rs.storesParams { + var id CommitID + info, ok := infos[key] + if ok { + id = info.Core.CommitID + } + + store, err := rs.loadCommitStoreFromParams(key, id, storeParams) if err != nil { return fmt.Errorf("failed to load rootMultiStore: %v", err) } newStores[key] = store } - // TODO: detecting transient is quite adhoc - // If any nontransient CommitStoreLoaders were not used, return error. - for key, param := range rs.storesParams { - if param.typ != sdk.StoreTypeTransient { - if _, ok := newStores[key]; !ok { - return fmt.Errorf("unused CommitStoreLoader: %v", key) - } - } - } - // Success. rs.lastCommitID = cInfo.CommitID() rs.stores = newStores From b5f8350dffe9d5ce2c44d1ffa9707e84ecff6f4b Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Thu, 13 Sep 2018 19:01:30 -0700 Subject: [PATCH 04/12] Merge PR #2333: Copy ADR template from Tendermint * Copy ADR template from Tendermint * Modify @alexanderbez 's ADR template --- docs/architecture/README.md | 22 +++++++++++++++++++++ docs/architecture/adr-template.md | 32 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 docs/architecture/README.md create mode 100644 docs/architecture/adr-template.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000000..7c669566df --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,22 @@ +# Architecture Decision Records (ADR) + +This is a location to record all high-level architecture decisions in the cosmos-sdk project. + +You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t). + +An ADR should provide: + +- Context on the relevant goals and the current state +- Proposed changes to achieve the goals +- Summary of pros and cons +- References +- Changelog + +Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and +justification for a change in architecture, or for the architecture of something +new. The spec is much more compressed and streamlined summary of everything as +it stands today. + +If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. + +Note the context/background should be written in the present tense. diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md new file mode 100644 index 0000000000..4ff7ad9465 --- /dev/null +++ b/docs/architecture/adr-template.md @@ -0,0 +1,32 @@ +# ADR {ADR-NUMBER}: {TITLE} + +## Changelog +* {date}: {changelog} + +## Context +> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution. + +## Decision +> This section explains all of the details of the proposed solution, including implementation details. +It should also describe affects / corollary items that may need to be changed as a part of this. +If the proposed change will be large, please also indicate a way to do the change to maximize ease of review. +(e.g. the optimal split of things to do between separate PR's) + +## Status +> A decision may be "proposed" if it hasn't been agreed upon yet, or "accepted" once it is agreed upon. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement. + +{Deprecated|Proposed|Accepted} + +## Consequences +> This section describes the consequences, after applying the decision. All consequences should be summarized here, not just the "positive" ones. + +### Positive + +### Negative + +### Neutral + +## References +> Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here! + +* {reference link} \ No newline at end of file From 7dc09d0fc2dc1af4a6c0698293bf9e1aa6657357 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Sat, 15 Sep 2018 02:41:21 +0800 Subject: [PATCH 05/12] Merge PR 2210: Judge if trust-node predefined before create certifier add verification for getBlock, queryTx and getValidators (#2210) Replace trust-node option with distrust-node option, and add varification in getting blocks, transactions and validator sets. --- Gopkg.lock | 1 + client/context/context.go | 19 +++++++++++---- client/context/errors.go | 8 +++++++ client/context/query.go | 28 +++++++++++++++------- client/flags.go | 5 ++-- client/lcd/lcd_test.go | 4 ++-- client/lcd/root.go | 4 ++-- client/lcd/test_helpers.go | 4 ++++ client/rpc/block.go | 22 +++++++++++++++-- client/rpc/validators.go | 17 +++++++++++-- client/tx/query.go | 46 ++++++++++++++++++++++-------------- client/tx/search.go | 20 +++++++++++----- x/stake/client/rest/query.go | 2 +- x/stake/client/rest/utils.go | 16 ++++++++++--- 14 files changed, 143 insertions(+), 53 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index a3aab3a866..05019f84a1 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -665,6 +665,7 @@ "github.com/tendermint/tendermint/libs/db", "github.com/tendermint/tendermint/libs/log", "github.com/tendermint/tendermint/lite", + "github.com/tendermint/tendermint/lite/errors", "github.com/tendermint/tendermint/lite/proxy", "github.com/tendermint/tendermint/node", "github.com/tendermint/tendermint/p2p", diff --git a/client/context/context.go b/client/context/context.go index 0983c970ff..db6fbef7d3 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -15,6 +15,7 @@ import ( tmlite "github.com/tendermint/tendermint/lite" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" + "os" ) const ctxAccStoreName = "acc" @@ -68,32 +69,40 @@ func NewCLIContext() CLIContext { } func createCertifier() tmlite.Certifier { + trustNodeDefined := viper.IsSet(client.FlagTrustNode) + if !trustNodeDefined { + return nil + } + trustNode := viper.GetBool(client.FlagTrustNode) if trustNode { return nil } + chainID := viper.GetString(client.FlagChainID) home := viper.GetString(cli.HomeFlag) nodeURI := viper.GetString(client.FlagNode) var errMsg bytes.Buffer if chainID == "" { - errMsg.WriteString("chain-id ") + errMsg.WriteString("--chain-id ") } if home == "" { - errMsg.WriteString("home ") + errMsg.WriteString("--home ") } if nodeURI == "" { - errMsg.WriteString("node ") + errMsg.WriteString("--node ") } - // errMsg is not empty if errMsg.Len() != 0 { - panic(fmt.Errorf("can't create certifier for distrust mode, empty values from these options: %s", errMsg.String())) + fmt.Printf("must specify these options: %s when --trust-node is false\n", errMsg.String()) + os.Exit(1) } + certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI) if err != nil { panic(err) } + return certifier } diff --git a/client/context/errors.go b/client/context/errors.go index 9c611494a5..de96aaa18c 100644 --- a/client/context/errors.go +++ b/client/context/errors.go @@ -11,3 +11,11 @@ func ErrInvalidAccount(addr sdk.AccAddress) error { return errors.Errorf(`No account with address %s was found in the state. Are you sure there has been a transaction involving it?`, addr) } + +// ErrVerifyCommit returns a common error reflecting that the blockchain commit at a given +// height can't be verified. The reason is that the base checkpoint of the certifier is +// newer than the given height +func ErrVerifyCommit(height int64) error { + return errors.Errorf(`The height of base truststore in gaia-lite is higher than height %d. +Can't verify blockchain proof at this height. Please set --trust-node to true and try again`, height) +} diff --git a/client/context/query.go b/client/context/query.go index 30cd2db300..9eb70fd8cc 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -16,6 +16,8 @@ import ( "github.com/cosmos/cosmos-sdk/store" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/lite" + tmliteErr "github.com/tendermint/tendermint/lite/errors" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -310,7 +312,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log) } - // Data from trusted node or subspace query doesn't need verification + // Data from trusted node or subspace query doesn't need verification. if ctx.TrustNode || !isQueryStoreWithProof(path) { return resp.Value, nil } @@ -323,6 +325,17 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro return resp.Value, nil } +// Certify verifies the consensus proof at given height +func (ctx CLIContext) Certify(height int64) (lite.Commit, error) { + check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Certifier) + if tmliteErr.IsCommitNotFoundErr(err) { + return lite.Commit{}, ErrVerifyCommit(height) + } else if err != nil { + return lite.Commit{}, err + } + return check, nil +} + // verifyProof perform response proof verification // nolint: unparam func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error { @@ -331,13 +344,8 @@ func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error { return fmt.Errorf("missing valid certifier to verify data from untrusted node") } - node, err := ctx.GetNode() - if err != nil { - return err - } - // AppHash for height H is in header H+1 - commit, err := tmliteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier) + commit, err := ctx.Certify(resp.Height + 1) if err != nil { return err } @@ -350,15 +358,17 @@ func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error { } // Verify the substore commit hash against trusted appHash - substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName, - multiStoreProof.StoreInfos, commit.Header.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") } + return nil } diff --git a/client/flags.go b/client/flags.go index c988980291..55b29b53ca 100644 --- a/client/flags.go +++ b/client/flags.go @@ -46,8 +46,7 @@ var ( // GetCommands adds common flags to query commands func GetCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { - // TODO: make this default false when we support proofs - c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses") + c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") @@ -71,7 +70,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") - c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses") + c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)") c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it") c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT") // --gas can accept integers and "simulate" diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 3d51ca6db2..7bd629cb18 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -177,7 +177,7 @@ func TestBlock(t *testing.T) { // -- - res, body = Request(t, port, "GET", "/blocks/1", nil) + res, body = Request(t, port, "GET", "/blocks/2", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) err = codec.Cdc.UnmarshalJSON([]byte(body), &resultBlock) @@ -210,7 +210,7 @@ func TestValidators(t *testing.T) { // -- - res, body = Request(t, port, "GET", "/validatorsets/1", nil) + res, body = Request(t, port, "GET", "/validatorsets/2", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) err = cdc.UnmarshalJSON([]byte(body), &resultVals) diff --git a/client/lcd/root.go b/client/lcd/root.go index 088344b846..ed5baa688e 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -63,10 +63,10 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") - cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") - cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node") + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") return cmd } diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 1075c078b9..3f60138224 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -186,6 +186,10 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress // XXX: Need to set this so LCD knows the tendermint node address! viper.Set(client.FlagNode, config.RPC.ListenAddress) viper.Set(client.FlagChainID, genDoc.ChainID) + viper.Set(client.FlagTrustNode, false) + dir, err := ioutil.TempDir("", "lcd_test") + require.NoError(t, err) + viper.Set(cli.HomeFlag, dir) node, err := startTM(config, logger, genDoc, privVal, app) require.NoError(t, err) diff --git a/client/rpc/block.go b/client/rpc/block.go index 4e6ed0c5fe..3b1545fc7a 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -10,6 +10,7 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" + tmliteProxy "github.com/tendermint/tendermint/lite/proxy" ) //BlockCommand returns the verified block data for a given heights @@ -21,8 +22,8 @@ func BlockCommand() *cobra.Command { RunE: printBlock, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - // TODO: change this to false when we can - cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") return cmd } @@ -41,6 +42,23 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { return nil, err } + if !cliCtx.TrustNode { + check, err := cliCtx.Certify(*height) + if err != nil { + return nil, err + } + + err = tmliteProxy.ValidateBlockMeta(res.BlockMeta, check) + if err != nil { + return nil, err + } + + err = tmliteProxy.ValidateBlock(res.Block, check) + if err != nil { + return nil, err + } + } + // TODO move maarshalling into cmd/rest functions // output, err := tmcodec.MarshalJSON(res) output, err := cdc.MarshalJSON(res) diff --git a/client/rpc/validators.go b/client/rpc/validators.go index cb002d3ea0..b034704d48 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -8,9 +8,11 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" + "bytes" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" + tmTypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" ) @@ -25,8 +27,8 @@ func ValidatorCommand() *cobra.Command { RunE: printValidators, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - // TODO: change this to false when we can - cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") return cmd } @@ -70,6 +72,17 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { return nil, err } + if !cliCtx.TrustNode { + check, err := cliCtx.Certify(*height) + if err != nil { + return nil, err + } + + if !bytes.Equal(check.ValidatorsHash(), tmTypes.NewValidatorSet(validatorsRes.Validators).Hash()) { + return nil, fmt.Errorf("got invalid validatorset") + } + } + outputValidatorsRes := ResultValidatorsOutput{ BlockHeight: validatorsRes.BlockHeight, Validators: make([]ValidatorOutput, len(validatorsRes.Validators)), diff --git a/client/tx/query.go b/client/tx/query.go index 20a455d79b..732c11b659 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -3,14 +3,11 @@ package tx import ( "encoding/hex" "fmt" - "net/http" - "strconv" - "github.com/tendermint/tendermint/libs/common" + "net/http" "github.com/gorilla/mux" "github.com/spf13/cobra" - "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -30,11 +27,10 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { // find the key to look up the account hashHexStr := args[0] - trustNode := viper.GetBool(client.FlagTrustNode) cliCtx := context.NewCLIContext().WithCodec(cdc) - output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode) + output, err := queryTx(cdc, cliCtx, hashHexStr) if err != nil { return err } @@ -45,13 +41,12 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - - // TODO: change this to false when we can - cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") return cmd } -func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) { +func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([]byte, error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return nil, err @@ -62,11 +57,18 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, tru return nil, err } - res, err := node.Tx(hash, !trustNode) + res, err := node.Tx(hash, !cliCtx.TrustNode) if err != nil { return nil, err } + if !cliCtx.TrustNode { + err := ValidateTxResult(cliCtx, res) + if err != nil { + return nil, err + } + } + info, err := formatTxResult(cdc, res) if err != nil { return nil, err @@ -75,8 +77,21 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, tru return codec.MarshalJSONIndent(cdc, info) } +// ValidateTxResult performs transaction verification +func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error { + check, err := cliCtx.Certify(res.Height) + if err != nil { + return err + } + + err = res.Proof.Validate(check.Header.DataHash) + if err != nil { + return err + } + return nil +} + func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (Info, error) { - // TODO: verify the proof if requested tx, err := parseTx(cdc, res.Tx) if err != nil { return Info{}, err @@ -116,13 +131,8 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) hashHexStr := vars["hash"] - trustNode, err := strconv.ParseBool(r.FormValue("trust_node")) - // trustNode defaults to true - if err != nil { - trustNode = true - } - output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode) + output, err := queryTx(cdc, cliCtx, hashHexStr) if err != nil { w.WriteHeader(500) w.Write([]byte(err.Error())) diff --git a/client/tx/search.go b/client/tx/search.go index 000dc57ebb..90c0ca168a 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -62,9 +62,8 @@ $ gaiacli tendermint txs --tag test1,test2 --any } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - - // TODO: change this to false once proofs built in - cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd @@ -84,7 +83,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]In return nil, err } - prove := !viper.GetBool(client.FlagTrustNode) + prove := !cliCtx.TrustNode // TODO: take these as args page := 0 @@ -94,7 +93,16 @@ func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]In return nil, err } - info, err := FormatTxResults(cdc, res.Txs) + if prove { + for _, tx := range res.Txs { + err := ValidateTxResult(cliCtx, tx) + if err != nil { + return nil, err + } + } + } + + info, err := FormatTxResults(cdc, cliCtx, res.Txs) if err != nil { return nil, err } @@ -103,7 +111,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]In } // parse the indexed txs into an array of Info -func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) { +func FormatTxResults(cdc *codec.Codec, cliCtx context.CLIContext, res []*ctypes.ResultTx) ([]Info, error) { var err error out := make([]Info, len(res)) for i := range res { diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index c6e39ebd27..8d55bed8cf 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -181,7 +181,7 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han } for _, action := range actions { - foundTxs, errQuery := queryTxs(node, cdc, action, delegatorAddr) + foundTxs, errQuery := queryTxs(node, cliCtx, cdc, action, delegatorAddr) if errQuery != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errQuery.Error())) diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index afe672840c..a2266c36c5 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/stake/tags" rpcclient "github.com/tendermint/tendermint/rpc/client" + "github.com/cosmos/cosmos-sdk/client/context" ) // contains checks if the a given query contains one of the tx types @@ -20,15 +21,24 @@ func contains(stringSlice []string, txType string) bool { } // queries staking txs -func queryTxs(node rpcclient.Client, cdc *codec.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { +func queryTxs(node rpcclient.Client, cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { page := 0 perPage := 100 - prove := false + prove := !cliCtx.TrustNode query := fmt.Sprintf("%s='%s' AND %s='%s'", tags.Action, tag, tags.Delegator, delegatorAddr) res, err := node.TxSearch(query, prove, page, perPage) if err != nil { return nil, err } - return tx.FormatTxResults(cdc, res.Txs) + if prove { + for _, txData := range res.Txs { + err := tx.ValidateTxResult(cliCtx, txData) + if err != nil { + return nil, err + } + } + } + + return tx.FormatTxResults(cdc, cliCtx, res.Txs) } From 9cf40e2df5d60e0afa5be3cf1aa2195e0c7ed84e Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Fri, 14 Sep 2018 11:52:23 -0700 Subject: [PATCH 06/12] Merge PR #2337: Update revoked -> jailed in the docs, and in minor places within cmd --- cmd/gaia/app/genesis.go | 4 ++-- cmd/gaia/app/genesis_test.go | 2 +- cmd/gaia/cmd/gaiadebug/hack.go | 2 +- cmd/gaia/testnets/STATUS.md | 2 +- docs/resources/whitepaper.md | 2 +- docs/spec/staking/transactions.md | 10 +++++----- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index e19ee616ac..84f0ea7fa7 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -234,7 +234,7 @@ func genesisAccountFromGenTx(genTx GaiaGenTx) GenesisAccount { } // GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants -// TODO: No validators are both bonded and revoked (#2088) +// TODO: No validators are both bonded and jailed (#2088) // TODO: Error if there is a duplicate validator (#1708) // TODO: Ensure all state machine parameters are in genesis (#1704) func GaiaValidateGenesisState(genesisState GenesisState) (err error) { @@ -258,7 +258,7 @@ func validateGenesisStateValidators(validators []stakeTypes.Validator) (err erro return fmt.Errorf("Duplicate validator in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress()) } if val.Jailed && val.Status == sdk.Bonded { - return fmt.Errorf("Validator is bonded and revoked in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress()) + return fmt.Errorf("Validator is bonded and jailed in genesis state: moniker %v, Address %v", val.Description.Moniker, val.ConsAddress()) } addrMap[strKey] = true } diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index f671bebb41..9cbd1f49fc 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -84,7 +84,7 @@ func TestGaiaGenesisValidation(t *testing.T) { genesisState := makeGenesisState(genTxs) err := GaiaValidateGenesisState(genesisState) require.NotNil(t, err) - // Test bonded + revoked validator fails + // Test bonded + jailed validator fails genesisState = makeGenesisState(genTxs[:1]) val1 := stakeTypes.NewValidator(addr1, pk1, stakeTypes.Description{Moniker: "test #2"}) val1.Jailed = true diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 3ff2730f46..7200ef0b65 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -65,7 +65,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error { // The following powerKey was there, but the corresponding "trouble" validator did not exist. // So here we do a binary search on the past states to find when the powerKey first showed up ... - // operator of the validator the bonds, gets revoked, later unbonds, and then later is still found in the bypower store + // operator of the validator the bonds, gets jailed, later unbonds, and then later is still found in the bypower store trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") // this is his "bypower" key powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8") diff --git a/cmd/gaia/testnets/STATUS.md b/cmd/gaia/testnets/STATUS.md index b972eb8022..4db4297845 100644 --- a/cmd/gaia/testnets/STATUS.md +++ b/cmd/gaia/testnets/STATUS.md @@ -34,7 +34,7 @@ See [testnets repo](https://github.com/cosmos/testnets). ## *June 13, 2018, 17:00 EST* - Gaia-6002 is making blocks! - Gaia-6002 is live and making blocks -- Absent validators have been slashed and revoked +- Absent validators have been slashed and jailed - Currently live with 17 validators ## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002 diff --git a/docs/resources/whitepaper.md b/docs/resources/whitepaper.md index 62707c0804..f77608072d 100644 --- a/docs/resources/whitepaper.md +++ b/docs/resources/whitepaper.md @@ -1003,7 +1003,7 @@ where the processes that caused the consensus to fail (ie. caused clients of the protocol to accept different values - a fork) can be identified and punished according to the rules of the protocol, or, possibly, the legal system. When the legal system is unreliable or excessively expensive to invoke, validators can be forced to make security -deposits in order to participate, and those deposits can be revoked, or slashed, +deposits in order to participate, and those deposits can be jailed, or slashed, when malicious behaviour is detected [\[10\]][10]. Note this is unlike Bitcoin, where forking is a regular occurence due to diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index ee2b976aec..148c1c415f 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -100,7 +100,7 @@ type TxDelegate struct { delegate(tx TxDelegate): pool = getPool() - if validator.Status == Revoked return + if validator.Status == Jailed return delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr) if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr) @@ -141,7 +141,7 @@ startUnbonding(tx TxStartUnbonding): revokeCandidacy = false if bond.Shares.IsZero() { - if bond.DelegatorAddr == validator.Operator && validator.Revoked == false + if bond.DelegatorAddr == validator.Operator && validator.Jailed == false revokeCandidacy = true removeDelegation( bond) @@ -157,7 +157,7 @@ startUnbonding(tx TxStartUnbonding): setUnbondingDelegation(unbondingDelegation) if revokeCandidacy - validator.Revoked = true + validator.Jailed = true validator = updateValidator(validator) @@ -279,9 +279,9 @@ updateBondedValidators(newValidator Validator) (updatedVal Validator) else validator = getValidator(operatorAddr) - // if not previously a validator (and unrevoked), + // if not previously a validator (and unjailed), // kick the cliff validator / bond this new validator - if validator.Status() != Bonded && !validator.Revoked { + if validator.Status() != Bonded && !validator.Jailed { kickCliffValidator = true validator = bondValidator(ctx, store, validator) From 98005b03c4518b1452446845c9c48fe04e5c5a26 Mon Sep 17 00:00:00 2001 From: Peng Zhong <172531+nylira@users.noreply.github.com> Date: Sat, 15 Sep 2018 02:55:04 +0800 Subject: [PATCH 07/12] Merge PR #2338: Update chinese whitepaper --- docs/resources/whitepaper-zh-CN.md | 1113 +++++++++++++++++----------- 1 file changed, 700 insertions(+), 413 deletions(-) diff --git a/docs/resources/whitepaper-zh-CN.md b/docs/resources/whitepaper-zh-CN.md index 540ab66476..126deabd48 100644 --- a/docs/resources/whitepaper-zh-CN.md +++ b/docs/resources/whitepaper-zh-CN.md @@ -1,726 +1,1013 @@ # Cosmos +**A Network of Distributed Ledgers** -分布式账本网络 +**分布式账本网络** -Jae Kwon
-Ethan Buchman +Jae Kwon jae@tendermint.com
+Ethan Buchman ethan@tendermint.com -加入我们的 [Matrix](https://riot.im/app/#/room/#cosmos:matrix.org)一起讨论吧! +讨论[请加入Telegram](https://t.me/cosmosproject)! -_注意:我们会对内容进行定期更新,您可以随时进行查阅,谢谢!_ +_注意:如果你能在github上阅读,我们仍然定时更新这个文档,请定期检查更新!_ -\[[toc]] +## Table of Contents ########################################################### + * [介绍](#介绍) + * [Tendermint](#tendermint) + * [验证人](#验证人) + * [共识](#共识) + * [轻客户端](#轻客户端) + * [防止攻击](#防止攻击) + * [ABCI](#abci) + * [Cosmos 概述](#Cosmos-概述 ) + * [Tendermint-拜占庭容错](#Tendermint-拜占庭容错) + * [治理](#治理) + * [枢纽与分区](#枢纽与分区) + * [枢纽](#枢纽) + * [分区](#分区) + * [跨链通信 IBC](#跨链通信-IBC) + * [用例](#用例) + * [分布式交易所](#分布式交易所) + * [作为其他加密货币的纽带](#作为其他加密货币的纽带) + * [以太坊的扩展](#以太坊的扩展) + * [多用一体化](#多用一体化) + * [缓解网络分区问题](#缓解网络分区问题) + * [联邦式名称解析系统](#联邦式名称解析系统) + * [发行与激励](#发行与激励) + * [Atom 代币](#Atom-代币) + * [众筹](#众筹) + * [验证人的数量限制](#验证人的数量限制) + * [成为创世日后的验证人](#成为创世日后的验证人) + * [对验证人的惩罚](#对验证人的惩罚) + * [交易费用](#交易费用) + * [激励黑客](#激励黑客) + * [治理规范](#治理规范) + * [参数变更提案](#参数变更提案) + * [文本提案](#文本提案) + * [路线图](#路线图) + * [相关工作 ](#相关工作 ) + * [共识系统](#共识系统) + * [经典拜占庭容错](#经典拜占庭容错) + * [BitShare委托权益](#BitShare委托权益) + * [Stellar](#stellar) + * [BitcoinNG](#bitcoinng) + * [Casper](#casper) + * [水平扩展](#水平扩展) + * [Interledger协议](#Interledger协议) + * [侧链](#侧链) + * [以太坊扩展性的努力](#以太坊扩展性的努力) + * [普遍扩展](#普遍扩展) + * [闪电网络](#闪电网络) + * [隔离验证人](#隔离验证人) + * [附录](#附录) + * [分叉问责制](#分叉问责制) + * [Tendermint共识](#Tendermint共识) + * [Tendermint轻客户端](#Tendermint轻客户端) + * [远程攻击的防御](#远程攻击的防御) + * [克服分叉与审查攻击](#克服分叉与审查攻击) + * [ABCI说明](#ABCI说明) + * [IBC数据包交付确认](#IBC数据包交付确认) + * [默克尔树及默克尔证明的说明](#默克尔树及默克尔证明的说明) + * [交易类型](#交易类型) + * [IBCBlockCommitTx](#ibcblockcommittx) + * [IBCPacketTx](#ibcpackettx) + * [鸣谢](#鸣谢) + * [引用](#引用) -## 介绍 +## 介绍 ################################################################ -开源的生态系统、去中心化的文件共享、以及公共的加密货币,这一系列技术的成功让人们开始了解到,去中心化互联网协议是可以用来彻底改善社会经济基础架构的。我们见证了专业区块链应用的诞生,比如比特币 [\[1\]](1)(加密货币),Zerocash [\[2\]](2)(私有加密货币),也看到了大众化智能合约平台,比如以太坊 [\[3\]](3),此外还有其他无数针对 EVM(以太坊虚拟机)的分布式应用,如 Augur(预测市场)以及 The DAO [\[4\]](4)(投资俱乐部)。 -但是,到目前为止,这些区块链已经暴露了各种缺陷,包括总能量低效、功能不佳或受限、并且缺乏成熟的管理机制。为了扩大比特币交易吞吐量,已经研发了许多诸如隔离见证(Segregated-Witness) [\[5\]](5)和 BitcoinNG [\[6\]](6)这样的解决方案,但是这些垂直扩展方案都因单一物理机容量而受到限制,不然就得损害其可审核性这一特性。闪电网络 [\[7\]](7)可以通过让部分交易完全记录在账本外,来帮助扩大比特币交易额,这个方法非常适合微支付以及隐私保护支付轨道,但是可能无法满足更广泛的扩展需求。 +开源的生态系统、去中心化的文件共享、以及公共的加密货币,这一系列技术的成功使人们启发和理解,去中心化的互联网协议是可以从根本上改善社会经济基础架构的。我们已经见识过个有专长的区块链应用,诸如比特币[\[1\]][1](加密货币),ZCASH [\[2\]][2] (隐私加密货币),也看到了例如以太坊 [\[3\]][3] 的大众智能合约平台,还有无数基于 EVM (以太坊虚拟机)开发的分布式应用,例如 Augur(预测市场)和 TheDAO [\[4\]][4] (投资俱乐部) -理想的解决方案是在允许多个平行区块链互相操作的同时,保留安全特性。不过事实证明,采用工作量证明很难做到这一点,但也并非不可能。例如合并挖矿可以在完成工作的同时,让母链得以在子链上重复使用。不过这样还是需要通过每个节点,依次对交易进行验证,而且如果母链上大多数哈希力没有积极地对子链进行合并挖矿,那么就很容易遭到攻击。关于 [可替代区块链网络架构的学术回顾](http://vukolic.com/iNetSec_2015.pdf)将在辅助材料中呈现,我们会在 [相关作品](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#related-work)中对更多提议及其缺点进行概括。 -这里我们要介绍的是 Cosmos,这是一个全新区块链网络架构,能够解决所有问题。Cosmos 是一个涵盖众多独立区块链的网络,叫做"空间"。空间在 Tendermint Core [\[8\]](8)支持下运行,是一个类似实用拜占庭容错的安全共识引擎,兼具高性能、一致性等特点,而且在其严格的分叉责任制保证下,能够防止怀有恶意的参与者做出不当操作。Tendermint Core 的拜占庭容错共识算法,非常适合用来扩展权益证明机制下的公共区块链。 +然而,迄今为止,这些区块链已经暴露了各种缺陷,包括总体能效低下,性能不佳或受到限制和缺乏成熟的治理机制。为了扩大比特币交易吞吐量,已经研发了许多诸如隔离见证 [\[5\]][5](Segregated-Witness)和BitcoinNG [\[6\]][6](一种新的可扩展协议)这样的解决方案,但这些垂直扩展解决方案仍然受到单一物理机容量的限制,以确保完整的可审计性。闪电网络 [\[7\]][7] 可以通过部分交易完全记录在主链账本外来扩展比特币的交易容量,这种方法十分适用于微支付和隐私保护支付通道,但是无法适用于更通用的扩展需求。 -Cosmos 上的第一个空间叫做"Cosmos Hub"(Cosmos 中心)。Cosmos 中心是一种多资产权益证明加密货币网络,它通过简单的管理机制来实现网络的改动与更新。此外,Cosmos 中心还可以通过连接其他空间来实现扩展。 -Cosmos 网络的中心及各个空间可以通过区块链间通信(IBC)协议进行沟通,这种协议就是针对区块链的虚拟用户数据报协议(UDP)或者传输控制协议(TCP)。代币可以安全快速地从一个空间传递到另一个空间,两者之间无需体现汇兑流动性。相反,空间内部所有代币的转移都会通过 Cosmos 中心,它会记录每个空间所持有的代币总量。这个中心会将每个空间与其他故障空间隔离开。因为每个人都将新空间连接到 Cosmos 中心,所以空间今后也可以兼容新的区块链技术。 +理想的解决方案是允许多个并行的区块链交互操作的同时保持其安全特性。事实证明,采用工作量证明很难做到这一点,但也并非不可能。例如合并挖矿,允许在工作完成的同时,确保母链在子链上被重复使用,但交易必须通过每个节点依次进行验证,而且如果母链上的大多数哈希算力没有积极地对子链进行合并挖矿,那么就容易遭受到攻击。关于[可替代区块链网络架构的学术回顾](http://vukolic.com/iNetSec_2015.pdf) 将在附件中展示,我们也会在[相关工作](#related-work)中对其他(技术)方案和缺陷进行概括。 + + +这里我们要介绍的 Cosmos,一个全新的区块链网络架构,能够解决所有这些问题。Cosmos 是由许多被称之为“分区”的独立区块链组成的网络。分区在 Tendermint Core [\[8\]][8]的支持下运行,Tendermint Core 是一个[类似拜占庭容错](http://tendermint.com/blog/tendermint-vs-pbft/)安全共识引擎,具有高性能、一致性的特性,并且在严格的[分叉追责](#fork-accountability) 机制下能够制止恶意破坏者的行为。Tendermint Core 的拜占庭容错共识算法十分适合用于扩展权益证明(PoS)机制下的公共区块链。使用其他共识模型的区块链, 包括类似基于权益证明(PoS)的以太坊,以及比特币也能够通过使用适配分区被 Cosmos 网络连接。 + + +Cosmos 的第一个分区称之为 Cosmos 枢纽。Cosmos 枢纽是一种多资产权益证明加密货币网络,它通过简单的治理机制能够对网络进行适配和升级。此外,Cosmos 枢纽可以通过链接其他分区来实现扩展。 + + +Cosmos 网络的枢纽及各个分区可以通过区块链间通信(IBC)协议进行通信,这种协议就是针对区块链的虚拟用户数据报协议(UDP)或者传输控制协议(TCP)。代币可以安全、快速地从一个分区转到其他分区,而无需在两个分区之间拥具有汇兑流动性。相反,所有跨分区的代币转移都会通过 Cosmos 枢纽,以此来追踪记录每个分区持有代币的总量。这个枢纽会将每个分区与其他故障分区隔离开。因为每个人都可以将新的分区连接到 Cosmos 枢纽,所以分区将可以向后兼容新的区块链技术。 + + +利用 Cosmos 可以实现区块链间的互操作。这是一个具有潜力的有价值的互联网络,其中的资产由不同的验证人发布和控制,并可以在不依靠需要信任的第三方的情况下实现跨链资产无缝的转移和交易。 ## Tendermint -这一部分将对 Tendermint 共识协议及其用来创建应用程序的界面进行介绍。更多细节,详见 [附录](#appendix)。 + +在这一部分我们将阐述Tendermint共识协议和用于建立其应用程序的接口。 更多信息,请参见[附录](#附录) ### 验证人 -在经典拜占庭容错(BFT)算法中,每个节点都同样重要。在 Tendermint 网络里,节点的投票权不能为负,而拥有投票权的节点被称作"验证人"。验证人通过传播加密签名或选票,来参与共识协议并商定下一区块。 -验证人的投票权是一开始就确定好的,或者根据应用程序由区块链来决定是否有改变。比如,在 Cosmos 中心这种权益证明类应用程序中,投票权可能就是通过绑定为保证金的代币数量来确定的。 -_注意:像 ⅔ 和 ⅓ 这样的分数指的是占总投票权的分数,而不是总验证人,除非所有验证人拥有相同币种。而+⅔ 的意思是"超过 ⅔ ",⅓+则是"⅓ 或者更多"的意思。_ +在经典的拜占庭容错算法中,每个节点有相同的权重。在 Tendermint,节点有着不同数量(非负)的 _投票权_,而那些拥有相当数量投票权的节点称之为 _验证人_。验证人通过广播加密签名、投票或者对下一个区块表决同意来参与共识协议。 + +验证者的投票权是一开始就确定好了,或者根据应用程序由区块链来决定修改投票权。例如,在像Cosmos 枢纽的权益证明应用里,投票权可由绑定为押金的代币数量来决定。 + + +注意:像⅔和⅓这样的分数指的是占总投票权的分数,而不是总验证人,除非所有验证人拥有相同权重。而>⅔ 的意思是"超过⅔ ",≥⅓则是"⅓或者更多"的意思。 ### 共识 -Tendermint 是部分同步运作的拜占庭容错共识协议,这种协议源自 DLS 共识算法 [\[20\]](20)。Tendermint 的特点就在于其简易性、高性能以及分叉责任制。协议要求有固定且熟知的一组验证人,其中每个验证人通过公钥进行身份验证。这些验证人会尝试在某个区块上同时达成共识(这里的区块是指一份交易列表)。每个区块的共识轮流进行,每一轮都会有个领头人,或者提议人,由他们来发起区块。之后验证人分阶段对是否接受该区块,或者是否进入下一轮做出投票。每轮的提议人会从验证人顺序列表中按照其选票比例来选择确定。 -该协议全部细节请参考 [此处](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm)。 +Tendermint 是部分同步运作的拜占庭容错共识协议,这种协议源自DLS共识算法 [\[20\]][20]。Tendermint以简易性、高性能以及[分叉问责制](#fork-accountability)而著称。协议要求这组验证人固定且被熟知,并且每个验证人都有其公钥验证身份。这些验证人试图同时在一个区块上达成共识,这些区块是一系列的交易记录。每个区块的共识轮流进行,每一轮都会有个领头人,或者提议人,由他们来发起区块。之后验证人分阶段对是否接受该区块,或者是否进入下一轮做出投票。每轮的提议人会从验证人顺序列表中按照其投票权比例来选择确定。 -Tendermint 采用由绝对多数的选票(+⅔)选定的最优拜占庭容错算法,以及一套锁定机制来确保安全性。对此他们保证: -- 想要违背安全必须有超过 ⅓ 的选票出现拜占庭问题,并且提交超过两个值。 -- 如果有任何验证组引起了安全问题,或者说是企图这么做,那么就会被协议发现,一方面针对有冲突的区块进行投票,同时广播那些有问题的选票。 +更多协议的全部细节,请点击[这里](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm). -除了其超强安全保障外,Tendermint 还具备其他功效。以商品型云平台为例,Tendermint 共识以分布在五大洲七个数据中心的 64 位节点为基准,其每秒可以处理成千上万笔交易,提交顺序延迟时间为 1-2 秒。而值得关注的是,即使是在极其恶劣的敌对环境中,比如验证人崩溃了或者是遇到蓄谋已久的恶意选票,也能维持这种每秒千笔交易的高绩效。详见下图。 + +Tendermint 采用了使用大多数投票(超过三分之二)和锁定机制的最优拜占庭容错,来确保其安全性。这些能够保证: + +* 蓄意破坏者想要造成安全性问题,必须有三分之一以上的投票权,并且要提交超过两份以上的值。 +* 如果有一组验证人成功破坏了安全性,或者曾试图这么做,他们会被协议识别。协议包括对有冲突的区块进行投票和广播那些有问题的投票。 + + +除了其超强的安全性外,Tendermint还具备杰出的性能。以商用型云平台为例,Tendermint共识以分布在五大洲七个数据中心的64位节点为基准,其每秒可以处理成千上万笔交易,订单提交延迟时间为1-2秒。而值得关注的是,即使是在极其恶劣的敌对环境中,比如验证人崩溃了或者是广播恶意破坏的投票,也能维持这种每秒超过千笔交易的较高性能。详见下图。 ![Figure of Tendermint throughput performance](https://raw.githubusercontent.com/gnuclear/atom-whitepaper/master/images/tendermint_throughput_blocksize.png) ### 轻客户端 -Tendermint 共识算法的主要好处就是它具有安全简易的轻客戸端,这一点使其成为手机和物联网用例的理想工具。比特币轻客户端必须同步运行区块头组成的链,并且找到工作量证明最多的那一条,而 Tendermint 轻客戸端只需和验证组的变化保持一致,然后简单地验证最新区块中预先提交的+⅔,来确定最新情况。 -这种简单的轻客戸端证明机制也可以实现 [区块链之间的通信](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc)。 +Tendermint 共识算法的主要好处是具有安全简易的客户端,使其成为手机和物联网用例的理想选择。比特币轻客户端必须同步运行区块头组成的链,并且找到工作量证明最多的那一条链,而Tendermint轻客戸端只需和验证组的变化保持一致,然后简单地验证最新区块中预先提交的>⅔,来确定最新情况。 + +这种简单的轻客戸端证明机制也可以实现[区块链之间的通信](#inter-blockchain-communication-ibc)。 ### 防止攻击 -Tendermint 有各种各样的防御措施来防止攻击,比如 [远程无利害关系双重花费](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#preventing-long-range-attacks)及 [审查制度](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#overcoming-forks-and-censorship-attacks)。这个在 [附录](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#appendix)中会进行完整讨论。 -### TMSP +Tendermint 有各种各样的防御措施来防止一些明显的攻击,比如[远程无利害关系双花攻击](#preventing-long-range-attacks) 及[审查制度](#overcoming-forks-and-censorship-attacks)。 这些在[附录](#appendix)中有更详细的讨论。 -Tendermint 共识算法是在叫做 Tendermint Core 的程序中实现的。这个程序是一种与应用程序无关的"共识引擎",可以让任何命中注定的黑匣子软件变为分散复制的区块链。就像 Apache 网页服务器或者 Nginx 是通过通用网关接口(CGI)或快速通用网关接口(FastCGI)来连接 Wordpress(一款博客系统)应用程序一样,Tendermint Core 通过 Tendermint Socket 协议(TMSP)来连接区块链应用程序。因此,TMSP 允许区块链应用程序用任何语言进行编程,而不仅仅是共识引擎写入的程序语言。此外,TMSP 也让交换任何现有区块链堆栈的共识层成为可能。 +### ABCI -我们将其与知名加密货币比特币进行了类比。在比特币这种加密币区块链中,每个节点都维持着完整的审核过的 UTXO(未使用交易输出)数据库。如果您想要在 TMSP 基础上,创建出类似比特币的系统,那么 Tendermint Core 可以做到: -- 在节点间共享区块及交易 -- 创建规范或不可改变的交易顺序(区块链) +Tendermint共识算法是在叫做 Tendermint Core 的程序中实现的。这个程序是独立于应用的“共识引擎”,可以将任何已经确定的黑盒应用转变为分布式、可复制的区块链。Tendermint Core 可以通过应用区块链接口(ABCI) [\[17\]][17]与其他区块链应用连接。而且,应用区块链接口(ABCI) 接口允许区块链应用以任何语言编程实现,而不仅仅是写这个共识引擎所使用的语言。此外,应用区块链接口(ABCI) 也让交换任何现有区块链栈的共识层成为可能。 -同时,TMSP 应用程序会负责: -- 维护 UTXO 数据库 -- 验证交易的加密签名 -- 防止出现不存在的交易花费 -- 允许客户访问 UTXO 数据库 +我们将其与知名加密货币比特币进行了类比。在比特币这种加密币区块链中,每个节点都维持着完整的审核过的 UTXO(未使用交易输出)数据库。如果您想要在应用区块链接口(ABCI)基础上,创建出类似比特币的系统,那么 Tendermint Core 可以做到: + +* 在节点间共享区块及交易 +* 创建规范或不可改变的交易顺序(区块链) + + +同时,ABCI应用也可以做到: + +* 维护 UTXO 数据库 +* 验证交易的加密签名 +* 防止出现不存在的余额被交易 +* 允许客户访问UTXO数据库 + -Tendermint 能够通过为应用程序与共识的形成过程,提供简单的应用程序界面(API),来分解区块设计。 ## Cosmos 概述 -Cosmos 是一种独立平行的区块链网络,其中每条区块链通过 Tendermint [1](https://github.com/tendermint/tendermint)这样的经典拜占庭容错共识算法来运行。 -网络中第一条区块链将会是 Cosmos 中心。Cosmos 中心通过全新区块链间通信协议来连接其他众多区块链(或将其称之为空间)。中心可以追踪无数代币种类,并且在各个连接的空间里记录代币总数。代币可以安全快速地从一个空间传递到另一个空间,两者之间无需体现汇兑流动性,因为所有空间之间的代币传输都会经过 Cosmos 中心。 +Cosmos是一个独立平行的区块链网络,其中每条区块链通过 Tendermint[1](http://github.com/tendermint/tendermint)这样的经典拜占庭容错共识算法来运行。 -这一架构解决了当今区块链领域面临的许多问题,包括应用程序互操作性、可扩展性、以及无缝更新性。比如,从 Bitcoind、Go-Ethereum、CryptoNote、ZCash 或其他区块链系统中衍生出来的空间,都可以接入 Cosmos 中心。这些空间允许 Cosmos 实现无限扩展,从而满足全球交易的需求。此外,空间也完全适用于分布式交易所,反之交易所也支持空间运行。 -Cosmos 不仅仅是单一的分布式账本,而 Cosmos 中心也不是封闭式花园或宇宙中心。我们正在为分布式账本的开放网络设计一套协议,这套协议会按照加密学、稳健经济学、共识理论、透明性及可追究制的原则,成为未来金融系统的全新基础。 +网络中第一条区块链将会是 Cosmos 枢纽。Cosmos 枢纽通过全新的区块链间通信协议来连接其他众多区块链(或将其称之为 _分区_)。Cosmos 枢纽可以追踪无数代币的种类,并且在各个连接的分区里记录各种代币总数。代币可以安全快速地从一个分区转移到另一个分区,两者之间无需体现汇兑流动性,因为所有分区之间的代币传输都会经过 Cosmos 枢纽。 -### Tendermint 拜占庭容错股份授权证明机制(Tendermint-BFT DPoS) -Cosmos 中心是 Cosmos 网络中第一个公共区块链,通过 Tendermint 拜占庭共识算法运行。这个 Tendermint 开源项目于 2014 年开始,旨在解决比特币工作量证明算法的速度、可扩展性以及环境问题。通过采用并提高已经过验证的拜占庭算法(1988 年在麻省理工学院开发),Tendermint 成为了首个在概念上演示加密货币权益证明的团队,这种机制可以解决 NXT 和 BitShares 这些第一代权益证明加密币面临的"无利害关系"(nothing-at-stake)的问题。 +这一架构解决了当今区块链领域面临的许多问题,包括应用程序互操作性、可扩展性、以及可无缝升级的能力。比如,从Bitcoind、Go-Ethereum、CryptoNote、ZCash或其他区块链系统中衍生出来的分区,都能被锚定接入 Cosmos 枢纽。这些分区允许 Cosmos 实现无限扩展,从而满足全球交易的需求。此外,分区也完全适用于分布式交易所,反之交易所也支持分区运行。 -如今,实际上所有比特币移动钱包都要使用可靠的服务器来进行交易验证。这是因为工作量证明机制需要在交易被认定为无法逆转前进行多次确认。而在 CoinBase 之类的服务中也已经出现重复花费攻击。 -和其他区块链共识系统不同,Tendermint 提供的是即时、可证明安全的移动客户端支付验证方式。因为 Tendermint 的设计完全不支持分叉,所以移动钱包就可以实时接收交易确认,从而在智能手机上真正实现去信任的支付方式。这一点也大大影响了物联网应用程序。 -Cosmos 中的验证人(其扮演的角色类似比特币矿工,但是与之不同的是,他们采用加密签名来进行投票)必须是专门用来提交区块的安全机器。非验证人可以将权益代币(也叫做"atom")委托给任何验证人来赚取一定的区块费用以及 atom 奖励,但是如果验证人被黑客攻击或者违反协议规定,那么就会面临被惩罚(削减)的风险。Tendermint 拜占庭共识的可证明安全机制,以及利益相关方(验证人和委托人)的抵押品保证,为节点甚至是轻客户端提供了可证明、可计量的安全性。 +Cosmos 不仅仅是单一的分布式账本,而 Cosmos 枢纽也不是封闭式庭院或宇宙的中心。我们正在为分布式账本的开放网络设计一套协议,这套协议将基于密码学、稳健经济学、共识理论、透明性及可追责制的原则,成为未来金融系统的全新基础。 -### 管理 +### Tendermint-拜占庭容错 -分布式公共账本应该要有一套章程与管理体系。比特币依靠比特币基金会(在一定程度上)及挖矿来协调更新,但是这个过程很缓慢。以太坊在采用硬分叉措施解决 The DAO 黑客事件后,分裂成了 ETH 和 ETC,这主要是因为之前设定社会契约或机制来进行这类决定。 -Cosmos 中心的验证人与委托人可以对提案进行投票,从而自动改变预先设置好的系统参数(比如区块容量限制),协调更新,并对人们看得懂的章程进行修订投票,从而管理 Cosmos 中心。这个章程允许权益相关者聚集到一起,来解决盗窃及漏洞等相关问题(比如 The DAO 事件),并快速得出明确的解决方案。 +Cosmos 枢纽是 Cosmos 网络中第一条公共区块链,通过 Tendermint 的拜占庭共识算法运行。Tendermint 开源项目创立于2014年,旨在解决比特币工作量证明共识算法的速度、可扩展性以及造成的环境问题。通过采用并提高已经过验证的拜占庭算法(1988年在麻省理工学院开发)[\[20\]][20],Tendermint 成为了首个在概念论证了权益证明加密货币的团队,这种机制可以解决 NXT 和 BitShares 这些第一代权益证明加密币面临的"无利害关系"的问题。 -每个空间也具备自己的一套章程及管理机制。比如,Cosmos 中心的章程会强制实现中心的不可改变性(不能重新执行,除了 Cosmos 中心节点实现的漏洞),而每个空间则可自行设置与盗窃及漏洞相关的重新执行政策。 -Cosmos 网络能够在政策不同的区块间实现互操作性,这一点可以让客户在无需许可的环境下进行实验,为客户带去了终极自由及潜力。 +如今,实际上所有比特币移动钱包都要使用可靠的服务器来进行交易验证。这是因为工作量证明机制需要在交易被认定为无法逆转前进行多次确认。而在 Coinbase 之类的服务中也已经出现双重支付攻击。 -## 中心与空间 -这里我们将描述一个全新的去中心化与可扩展性模型。Cosmos 网络通过 Tendermint 机制来运行众多区块链。虽然现存提案的目标是创建一个包含全球所有交易顺序的"单一区块链",Cosmos 允许众多区块链在相互运行的同时,维持互操作性。 +和其他区块链共识系统不同,Tendermint 提供的是即时、可证明安全的移动客户端支付验证方式。因为 Tendermint 被设计为完全不分叉,所以移动钱包就可以实时接收交易确认,从而在智能手机上真正实现去信任的支付方式。这一点也大大影响了物联网应用程序。 -在这个基础上,Cosmos 中心负责管理众多独立区块链(称之为"空间",有时也叫做"碎片",根据数据库扩展技术"分片"得出)。中心上的空间会源源不断地提交最新区块,这一点可以让中心跟上每个空间状态的变化。同样地,每个空间也会和中心的状态保持一致(不过空间之间不会同彼此的步伐保持一致,除非间接通过中心来实现)。之后信息包就会从一个空间传递到另一个空间,并通过发布梅克尔证明(Merkle-proof)来说明信息已经被传送或接收。这种机制叫做"区块链间通信",或者简称为"IBC"机制。 + +Cosmos 中的验证人角色类似比特币矿工,但是他们采用加密签名来进行投票。验证人是专门用来提交区块的安全机器。非验证人可以将权益代币(也叫做"atom"币)委托给任何验证人来赚取一定的区块费用以及atom奖励,但是如果验证人被黑客攻击或者违反协议规定,那么代币就会面临被惩罚(削减)的风险。Tendermint 拜占庭共识的可证明安全机制,以及利益相关方(验证人和委托人)的抵押品保证,为节点甚至是轻客户端提供了可证明、可量化的安全性。 + +### 治理 + +分布式公共账本应该要有一套章程与治理体系。比特币依靠比特币基金会以及挖矿来协作更新,但是这是一个反应缓慢的治理制度。以太坊在采用硬分叉成 ETH 和 ETC 来解决 The DAO 黑客,这主要是因为之前没有设定社会契约或机制来进行这类决定。 + + +Cosmos 枢纽的验证人与委托人可以对提案进行投票,从而改变预先默认设置好的系统参数(比如区块转账费用限制),协作更新,并对可读性的章程进行修订投票,从而治理 Cosmos 枢纽制度。这个章程允许权益相关者聚集到一起,来解决盗窃及漏洞等相关问题(比如The DAO事件),并得出更快更明确的解决方案。 + + + +每个分区也可以制定自己的一套章程及治理机制。比如,Cosmos 枢纽的章程可以设置为强制实现枢纽的不可改变性(不能回滚,除了 Cosmos 枢纽节点产生的漏洞),而每个分区则可设置自己的回滚政策。 + + +Cosmos 网络能够在制度不同的分区间实现互操作性,这一点给客户极高的自由度和潜力而无需许可即可实验(新技术)。 + +## 枢纽与分区 + + +这里我们将描述一个全新的去中心化与可扩展性模型。Cosmos 网络通过 Tendermint 机制来运行众多的区块链。虽然现存提案的目标是创建一个包含全球所有交易订单的"单一区块链",但是 Cosmos 允许众多区块链在并行运行的同时,保持可互操作性。 + + +在这个基础上,Cosmos枢纽负责管理称之为“分区”的众多独立区块链(有时也叫做"分片",参考自众所周知的数据库扩展技术"分片")。枢纽上的分片会源源不断地提交最新区块,这一点可以让枢纽同步每一个分区的状态。同样地,每个分区也会和枢纽的状态保持一致(不过分区之间不会同彼此的同步,除非间接通过枢纽来实现)。通过发布默克尔证明来证明消息被接受和发送,来让消息从一个分区传递到另一个分区。这种机制叫做"区块链间通信",或者简称为"IBC"机制。 ![Figure of hub and zones acknowledgement](https://raw.githubusercontent.com/gnuclear/atom-whitepaper/master/images/hub_and_zones.png) -任何区块都可以自行成为中心,从而形成非循环图,但是有一点需要阐明,那就是我们只会对简单配置(只有一个中心)以及许多没有中心的空间进行描述。 -### 中心(Hub) +任何分区都可以自行成为枢纽来建立非循环图表,但为了清楚起见,我们只描述这种只有一个枢纽和许多非枢纽的分区这样简单的配置 -Cosmos 中心区块链承载的是多资产分布式账本,其中代币可以由个体用户或空间本身持有。这些代币能够通过特殊的 IBC 包裹,即"代币包"(coin packet)从一个空间转移到另一个空间。中心负责保持空间中各类代币全球总量不变。IBC 代币宝交易必须由发送人、中心及接收人的区块链执行。 -因为 Cosmos 中心在整个系统中扮演着中央代币账本的角色,其安全性极其重要。虽然每个空间可能都是一个 Tendermint 区块链——只需通过 4 个,或者在无需拜占庭容错共识的情况下更少的验证人来保证安全),但是 Cosmos 中心必须通过全球去中心化验证组来保证安全,而且这个验证组要能够承受最严重的攻击,比如大陆网络分割或者由国家发起的攻击。 +### 枢纽 -### 空间(Zones) -Cosmos 空间是独立的区块链,能够和 Cosmos 中心进行 IBC 信息交换。从 Cosmos 中心的角度看,空间是一种多资产、多签名的动态会员制账户,它可以通过 IBC 包裹进行代币发送与接收。就像加密币账户一样,空间不能转移超出其持有量的代币,不过可以从其他拥有代币的人那里接收代币。空间可能会被指定为一种或多种代币的"来源",从而赋予其增加代币供应量的权力。 +Cosmos枢纽是承载多种分布式账本资产的区块链,其中代币可以由个人或分区自己持有。这些代币能够通过特殊的IBC数据包,即"代币数据包"(coin packet)从一个分区转移到另一个分区。枢纽负责保持各个分区中各类代币总量不变。IBC代币数据包交易必须由发送人、枢纽及区块接受者执行。 -Cosmos 中心的 Atom 或可作为空间(连接到中心)验证人的筹码。虽然在 Tendermint 分叉责任制下,空间出现重复花费攻击会导致 atom 数量减少,但是如果空间中有超过 ⅔ 的选票都出现拜占庭问题的话,那这个空间就可以提交无效状态。Cosmos 中心不会验证或执行提交到其他空间的交易,因此将代币传送到可靠空间就是用户的责任了。未来 Cosmos 中心的管理系统可能会通过改善提案,来解决空间故障问题。比如,在检测到袭击时,可以将有些空间(或全部空间)发起的代币转移输出压制下来,实现紧急断路(即暂时中止代币转移)。 -## 区块链间通信(IBC) -现在我们来介绍下中心与空间之前通信的方法。假如现在有三个区块链,分别是"空间 1"、"空间 2"以及"中心",我们想要"空间 1"生成一个包裹,通过"中心"发送给"空间 2"。为了让包裹从一个区块链转移到另一个区块链,需要在接收方区块链上发布一个证明,来明确发送方已经发起了一个包裹到指定地点。接收方要验证的这个证明,必须和发送方区块头保持一致。这种机制就类似与侧链采用的机制,它需要两个相互作用的链,通过双向传送存在证明数据元(交易),来"知晓"另一方的情况。 +因为Cosmos枢纽在整个系统中扮演着中央代币账本的角色,其安全性极其重要。虽然每个分区可能都是一个Tendermint区块链——只需通过4个,(或者在无需拜占庭容错共识的情况下更少的验证人来保证安全),但是Cosmos枢纽必须通过全球去中心化验证组来保证安全,而且这个验证组要能够承受最严重的攻击,比如区域网络分裂或者由国家发起的攻击。 -IBC 协议可以自然定义为两种交易的使用:一种是 IBCBlockCommitTx 交易,这种交易可以让区块链向任何观察员证明其最新区块哈希值;另一种是 IBCPacketTx 交易,这种交易则可以证明某个包裹确实由发送者的应用程序,通过梅克尔证明机制(Merkle-proof)传送到了最新区块的哈希值上。 +### 分区 -通过将 IBC 机制分裂成两个单独的交易,即 IBCBlockCommitTx 交易与 IBCPacketTx 交易,我们可以让接收链的本地费用市场机制,来决定承认哪个包裹,与此同时还能确保发送方的完全自由,让其自行决定能够传出的包裹数量。 + +Cosmos分区是独立的区块链,能够和Cosmos枢纽进行IBC消息交换。从枢纽的角度上看,分区是一种多重资产、动态会员制的多重签名账户,可以通过IBC数据包用来发送和接受代币。就像加密币账户一样,分区不能转移超出其持有量的代币,不过可以从其他拥有代币的人那里接收代币。分区可能会被指定为一种或多种代币的"来源",从而赋予其增加代币供应量的权力。 + + +Cosmos 枢纽的 Atom 或可作为分区验证人连接到枢纽的筹码。虽然在Tendermint分叉责任制下,分区出现双重支付攻击会导致atom数量减少,但是如果分区中有超过⅔的选票都出现拜占庭问题的话,那这个分区就可以提交无效状态。Cosmos 枢纽不会验证或执行提交到其他分区的交易,因此将代币发送到可靠的分区间就是用户的责任了。未来 Cosmos 枢纽的管理系统可能会通过改善提案,来解决分区故障问题。比如,在检测到袭击时,可以将有些分区(或全部分区)发起的代币转账将被暂停,实现紧急断路(即暂时中止代币转账)。 + +## 跨链通信-IBC + +现在我们来介绍下枢纽与分区之间通信的方法。假如现在有三个区块链,分别是"分区1"、“分区2"以及"枢纽”,我们想要"分区1"生成一个数据包,通过"枢纽"发送给"分区2"。为了让数据包从一个区块链转移到另一个区块链,需要在接收方区块链上发布一个证明,来明确发送方已经发起了一个数据包到指定目的地。接收方要验证的这个证明,必须和发送方区块头保持一致。这种机制就类似与侧链采用的机制,它需要两个相互作用的链,通过双向传送存在证明数据元(交易),来"知晓"另一方的情况。 + +IBC协议可以自然定义为两种交易的使用:一种是IBCBlockCommitTx 交易,这种交易可以让区块链向任何观察员证明其最新区块哈希值;另一种是IBCPacketTx 交易,这种交易则可以证明某个数据包确实由发送者的应用程序,通过默克尔证明机制(Merkle-proof)传送到了最新区块的哈希值上。 + +通过将IBC机制分离成两个单独的交易,即IBCBlockCommitTx 交易与 IBCPacketTx 交易,我们可以让接收方链的本地费用市场机制,来决定承认哪个数据包,与此同时还能确保发送方的完全自由,让其自行决定能够传出的数据包数量。 ![Figure of Zone1, Zone2, and Hub IBC without acknowledgement](https://raw.githubusercontent.com/gnuclear/atom-whitepaper/master/msc/ibc_without_ack.png) -在上述案例中,为了更新"中心"上"空间 1"的区块哈希(或者说"空间 2"上"中心"的区块哈希),必须将 IBCBlockCommitTx 交易的"空间 1"区块哈希值发布到"中心"上(或者将该交易的"中心"区块哈希值发布到"空间 2"中)。 -_更多关于两种 IBC 交易的信息,请参考_ [_IBCBlockCommitTx_](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#ibcblockcommittx)_ 以及 _ [_IBCPacketTx_](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#ibcpacketcommit)_。_ -## 用例 + +在上述案例中,为了更新"枢纽"上"分区1"的区块哈希(或者说"分区2"上"枢纽"的区块哈希),必须将IBCBlockCommitTx交易的"分区1"区块哈希值发布到"枢纽"上(或者将该交易的"枢纽"区块哈希值发布到"分区2"中)。 + +_关于两种IBC交易类型,详细请参见 [IBCBlockCommitTx](#ibcblockcommittx) 和 [IBCPacketTx](#ibcpacketcommit) + +## 用例 ### 分布式交易所 -比特币借助批量复制的分布式账本技术来保证安全,同样的,我们也可以用这种方式,在区块链上运行,从而降低交易所受内外部攻击的可能性。我们称之为分布式交易所。 +比特币借助大量复制来增加分布式账本的安全性。用类似的方式,我们可以在区块链上运行交易所,来降低其受内部及外部攻击的可能性。我们称之为去中心化交易所。 -如今,加密币社区认为去中心化交易所是基于"原子交叉链"交易(AXC 交易)的交易所。通过这类交易,不同链上的两位用户可以发起两笔传输交易,要么在两个账本上一起执行,要么两个账本都不执行(即原子级)。比如,两位用户可以通过 AXC 交易来进行比特币和以太币之间的交易(或不同账本上的任意两种代币),即使比特币和以太坊之间并没有相互连接。在 AXC 交易模式下的交易所,其好处在于用户双方都不需要相信彼此,也不用相信交易匹配服务。其坏处就是,双方都得在线才能进行交易。 +现今,加密货币社区认为的去中心化交易所基于"跨链原子事务"交易( AXC 交易)。通过AXC交易,两条不同链上的两个用户可以发起两笔转账交易,交易在两个账本上要么一起提交执行,或者两个账本都不执行(即交易的原子性)。比如,两位用户可以通过AXC交易来实现比特币和以太币之间的交易(或是在不同账本上的任意两种代币),即使比特币和以太坊的区块链之间并没有彼此连接。AXC 交易模式下的交易所用户双方不需要彼此信任,也不用依赖交易匹配服务。其弊端是,交易双方必须同时在线才能进行交易。 -另一种去中心化交易所是在交易所的区块链上运行批量复制的分布式账本。这种交易所的用户可以提交一份限价订单,在关机状态下执行交易。区块链会代表交易者匹配并完成交易。 +另一种去中心化交易所是进行大量复制的具有独立区块链的分布式交易所。该种交易所的用户可以提交限价订单并关闭他们的计算机,交易可以在用户离线状态下执行。区块链将会代表交易者去完成匹配和交易。 -去中心化交易所可以创建一份大范围限价订单簿,以此来吸引其他交易者。在交易所界,流动性需求越来越高,因此交易所业务界的网络效应也愈发强烈(或者说至少产生了"胜者得益"效应)。目前加密币交易所排名第一的是 Poloniex,其 24 小时交易额为 2000 万美元,而 Bitfinex 以 24 小时 500 万位列第二。在这种强大的网络效应背景下,基于 AXC 的去中心化交易所的交易额是不可能超过中心化交易所的。去中心化交易所要想和中心化交易所一争高下,那么就需要支持大范围限价订单簿的运行。而只有基于区块链的去中心化交易所可以实现这一点。 +一个中心化的交易所可以构建一个有大交易量的限价交易的买卖盘账目,以此来吸引更多的交易者。在交易所领域,流动性会引发更多流动性,因此在交易所业务中,其具有的网络效应也愈发明显(或者说至少产生了"赢家通吃"效应)。目前加密货币交易所 Poloniex 以每24小时2,000万美元的交易量排名第一, Bitfinex 则每24小时500万美元的交易额位列第二。在这种强大的网络效应之下,基于AXC的去中心化交易所的成交量是不太可能超过中心化交易所。去中心化交易所要想和中心化交易所一争高下,就需要支持以限价订单构成的具有深度的交易买卖盘账目的运行。而只有基于区块链的去中心化交易所可以实现这一点。 -Tendermint 的快速交易执行是另一大优势。Cosmos 的空间可以在不牺牲一致性的前提下,通过优先完善快速交易,来实现交易的快速完成——针对双向订单交易,及 IBC(跨区块链通信)代币与其他空间的交易。 +Tendermint提供的快速交易执行是另一大优势。Cosmos的内部网络可以在不牺牲一致性的前提下优先快速的确定最终性,来实现交易的快速完成 —— 同时针对交易订单交易,以及IBC(跨区块链通信)代币与其他网络的交易。 -根据如今加密币交易所的情况,Cosmos 的一项重大应用就是分布式交易所(也就是 Cosmos DEX)。其交易吞吐能力及提交延时情况可以和那些中心化交易所媲美。交易者可以在离线的状态下提交限价订单。并且,在 Tendermint,Cosmos 中心以及 IBC 的应用下,交易者可以快速地完成资金在交易所及其他空间的转出转入。 +综上,根据现有加密货币交易所的情况,Cosmos的一项重大应用就是去中心化交易所(称为 Cosmos DEX)。其交易吞吐能量和委托延时可以与那些中心化交易所媲美。交易者可以在各方离线的状态下提交限价订单。并且,基于Tendermint,Cosmos枢纽以及IBC的情况下,交易者可以快速地完成在交易所及其他网络的资金进出。 -### 和其他加密货币挂钩 +### 作为其他加密货币的纽带 -享有特权的空间可以作为和其他加密货币挂钩的代币来源。这种挂钩类似 Cosmos 中心与空间之间的关系,两者都必须及时更新彼此最新的区块链,从而验证代币已经从一方转移到另一方的证明。Cosmos 网络上挂钩的空间要和中心以及其他加密货币保持一致。这种间接挂钩的空间可以维持简单的中心逻辑,并且不用了解其他区块链共识战略(比如比特币工作量证明挖矿机制)。 +特权分区可以作为和其他加密货币挂钩的代币来源。这种挂钩类似Cosmos枢纽与分区之间的关系,两者都必须及时更新彼此最新的区块链,从而验证代币已经从一方转移到另一方。Cosmos网络上挂钩的”桥接分区“要和中心以及其他加密币保持同步。这种间接通过”桥接分区“可以保持枢纽逻辑的简洁。并且不必要了解其他的链上共识战略,如比特币工作量证明挖矿机制。 -比如,设置有特定验证组的 Cosmos 空间(可能和中心里的验证组一样)可以作为与以太币挂钩的空间,其中基于 Tendermint Socket 协议(TMSP)的应用(即"挂钩空间"里的)有办法和外部以太坊区块链上的(即"起始点")挂钩合约交换 IBC 信息。通过这一合约,持币人可以先将以太币发送到以太坊的挂钩合约中,然后再将以太币传送到挂钩空间。挂钩合约接收到以太币后,除非同时从挂钩空间处接收到正确的 IBC 包裹,否则这些以太币是无法提取的。而当挂钩空间接收到 IBC 包裹,并证明以太币已被特定以太坊账户的挂钩合约接收后,挂钩空间就会生成存有余额的相关账户。之后,挂钩空间上的以太币(即"已挂钩的以太币")就可以转进或转出中心了,完成传送到特定以太坊提取地址的交易后,再彻底删除。IBC 包裹可以证明挂钩空间上的交易,这个包裹可以公布到以太坊挂钩合约中,来开放以太币的提取权。 +#### 向Cosmos枢纽发送代币 -当然,这类挂钩合约也存在风险,比如会出现恶劣的验证组。如果拜占庭投票权超过 ⅓,就会造成分叉,即从以太坊挂钩合约中提取以太币的同时,还能保持挂钩空间中的挂钩以太币不变。更有甚者,如果拜占庭投票权超过 ⅔,可能会有人直接对将以太币发送到挂钩合约中(通过脱离原始挂钩空间的挂钩逻辑)的人下手,盗取他们的以太币。 +每个挂钩桥接分区的验证人都会在基于Tendermint公式的区块链之上,运行带有特殊的ABCI桥接应用程序,但同时也会运行一个原有区块链的“全节点”。 -如果将这个挂钩方法完全设计成责任制,那么就有可能解决这一问题。比如,中心及起始点的全部 IBC 包裹可能需要先通过挂钩空间的认可,即让中心或起始点中的钩挂合约对挂钩空间的所有状态转变进行有效验证。中心及起始点要允许挂钩空间的验证人提供抵押品,而挂钩合约的代币转出需要有所延迟(并且抵押品解绑时间也要足够长),从而让单独的审计人有时间发起挑战。我们会以未来 Cosmos 改善提议的形式公开这一系统的设计说明及实现方式,以待 Cosmos 中心的管理系统审批通过。 +在原有区块链挖出新区块时,桥接分区验证人员将通过签署和分享起始点区块链的提示,各自局部视角可以达成一致。当一个桥接分区收到原有区块链的支付时(如在以太坊或比特币等PoW机制的链上有足够数目的确认),则在该桥接分区上创建具有该对应账户的余额。 -虽然现在的社会政治环境还不够成熟,不过我们可以做一些延伸,比如让负责国家国币的一些机构(尤其是其银行)组成一个验证组,来实现空间同国家法定货币的挂钩。当然这必须布置好额外的预防措施,只接受法律系统下的货币,从而加强可靠的公证人或大型机构对银行活动的审计。 +就以太坊而言,桥接分区可以和Cosmos枢纽共享相同的验证人。以太坊方面(原本区块链),一个桥接合约将允许以太拥有者通过将以太币发送到以太坊的桥接分区的桥接合约上。一旦挂桥接合约接收到以太币,以太币就不能被撤回,除非从桥接分区接收到对应的IBC数据包。桥接合约跟随桥接分区的验证组,它可能与Cosmos枢纽的验证人组相同。 -这一整合或可让空间中所有拥有银行账户的人,将自己银行账户里的美元传输到空间账户中,或者完整的转入中心或其他空间里。 +就比特币而言,概念是相似,除了代替一个桥接合约,每个UTXO将由一个门限多重签名P2SH数据库限制。由于P2SH系统的限制,签名者不能与Cosmos枢纽的验证人组相同。 -这么看来,Cosmos 中心就是法定货币和加密货币无缝对接的导管,从而解决困扰交易所至今的交互局限问题。 +#### 从Cosmos枢纽提出代币 + +桥接分区上的以太币(“桥接以太币”)可以在枢纽间转进,转出,完成传送到特定以太坊提取地址后,转出的“桥接以太币”被彻底删除。一个IBC消息可以证明桥接分区上的交易,这个消息将被公布到以太坊桥接合约中,以便以太币被取出。 + +就比特币而言,严谨的交易脚本系统让IBC币的镜像转换机制很难实现。每个UTXO都有自己的特定的脚本,所以当比特币履约签名者发生变化时,每个UTXO都必须迁移到新的UTXO。一个解决方案是根据需要,压缩和解压缩UTXO-set,以保持UTXO的总数量下降。 + +#### 挂钩区完全责任制 + +这类挂钩合约存在风险的风险是,可能会出现恶意的验证人组。如果拜占庭投票权超过⅓,就会造成分叉,即从以太坊桥接合约中提取以太币的同时,还能保持桥接分区中的挂钩以太币不变。甚至,如果拜占庭投票权超过⅔,可能会有人直接通过脱离原始桥接分区的桥接逻辑,对发送以太币发到桥接合约中的帐户下手,盗取以太币。 + +如果将这个桥接方法完全设计成责任制,就有可能解决这一问题。比如,枢纽及起始点的全部IBC包裹可能需要先通过桥接分区的认可,即让枢纽或起始点中的桥接合约对桥接分区的所有状态转换进行有效验证。枢纽及起始点要允许桥接分区的验证人提供抵押物,而侨界合约的代币转出需要延时(且抵押品解绑时间也要足够长),从而让单独的审计人有时间发起任何的质询。我们会把这一系统的设计说明以及执行方式开放,作为未来Cosmos改善的提议,以待Cosmos枢纽的管理系统审批通过。 ### 以太坊的扩展 -扩展问题一直是以太坊的一个公开问题。目前以太坊节点会处理每笔交易,并且存储所有状态。 -因为 Tendermint 提交区块的速度比以太坊工作量证明要快,所以由 Tendermint 共识推动且用于挂钩以太币运行的 EVM(以太坊虚拟机)空间能够强以太坊区块链的性能。此外,虽然 Cosmos 中心及 IBC 包裹技术不能实现每秒合约逻辑的任意执行,但是它可以用来协调不同空间里以太坊合约间的代币变动,通过碎片化方式为以代币为中心的以太坊奠定基础。 +众所周知,扩展问题是一直困扰着以太坊的问题。目前以太坊节点会处理节点上每笔交易,并且存储所有的状态[参考](https://docs.google.com/presentation/d/1CjD0W4l4-CwHKUvfF5Vlps76fKLEC6pIwu1a_kC_YRQ/mobilepresent?slide=id.gd284b9333_0_28).。 -### 多应用一体化 +Tendermint提交区块的速度比以太坊工作量证明要快,所以由Tendermint共识推动且使用桥接以太币运行的以太坊虚拟机分区能够强化太坊区块链的性能。此外,虽然Cosmos枢纽及IBC包裹机制不能实现每秒合约逻辑的执行,但是它可以用来协调不同分区中以太坊合约之间的代币流通,通过分片方式为以代币为中心的以太坊扩展奠定基础。 -Cosmos 空间可以运行任意应用逻辑,这一点在空间运转初期就已经设定好,通过管理可以不断更新。这种灵活度让 Cosmos 空间得以成为其他加密货币的挂钩载体,比如以太坊或比特币,并且它还能和这些区块链的衍生品挂钩,使用同样的代码库,但是验证组及初始分配有所不同。这样一来就可以运行多种现有加密币框架,比如以太坊、Zerocash、比特币、CryptoNote 等等,将其同 Tendermint Core 结合,成为通用网络中性能更优的共识引擎,为平台提供更多的交互机遇。此外,作为多资产区块链,每笔交易都有可能包含多个输入输出项,其中每个输入项都可以是任意代币,使 Cosmos 直接成为去中心化交易所,当然这里假设的是订单通过其他平台进行匹配。还有一种替代方案,即让空间作为分布式容错交易所(包含订单簿),这可以算是对现有中心化加密币交易所的严格改进——现有交易所时不时会受到攻击。 +### 多用一体化 -空间也可以作为区块链版的企业及政府系统,其原本由一个或多个组织运行的特定服务,现在作为 TMSP 应用在某个空间上运行,从而在不放弃对底层服务控制的前提下,维持公共 Cosmos 网络的安全性及交互性。所以,Cosmos 或可为那些既想使用区块链技术,又不愿将控制权彻底放给分布式第三方的人,提供最佳的运行环境。 +Cosmos分区可以运行任意的应用逻辑,应用在分区创建时设定好,可通过管理者可以不断更新。这种灵活度使得Cosmos分区可以作为其他加密币的挂钩载体,比如以太坊或比特币,并且它还能和这些区块链的衍生品挂钩,利用同样的代码库,而在验证程序及初始分配有所区分。这样就允许多种现有加密币框架得以运行,如以太坊、Zerocash、比特币、CryptoNote等等,将其同Tendermint Core结合,成为通用网络中性能更优的共识引擎,为平台之间提供更多的交互机会。此外,作为多资产区块链,每笔交易都有可能包含多个输入输出项,其中每个输入项都可以是任意代币,使Cosmos直接成为去中心化交易所,当然这里假设交易订单通过其他平台进行匹配。替代方案是,让分区作为分布式容错交易所(包含买卖盘账目),这算是对中心化加密币交易所之上的严格改进——现行交易所在过去时常发生被攻击的事件。 + +分区也可以作为区块链版的企业及政府系统,其原本由一个或多个组织运行的特定服务,现在作为ABCI应用在某个分区上运行,从而在不放弃对底层服务控制的前提下,维持公共Cosmos网络的安全性及交互性。所以,Cosmos或可为那些既想使用区块链技术,又不愿彻底放弃控制权给分布式第三方的人,提供绝佳的运行环境。 ### 缓解网络分区问题 -有人认为像 Tendermint 这种支持一致性的共识算法有一个重大问题,那就是网络分割会导致没有一个分区拥有超过 ⅔ 的投票权(比如超过 ⅓ 在线下),而任何这类网络分割都将中止整个共识。而 Cosmos 架构可以缓解这个问题,它可以使用全球中心,但是空间实行地区自治,然后让每个空间的投票权按照正常的地理位置进行分布。比如,某个一般范例就有可能是针对个别城市或地区的,让他们在运行自己空间的同时,还能共享共同的中心(比如 Cosmos 中心),并且可以在因网络分区导致的中断期间,继续维持地区自治活动。请注意,这样一来在设计稳健的联邦式容错系统过程中,就可以真正地去考虑地理、政治及网络拓扑的特征了。 +有人认为像Tendermint这种支持一致性的共识算法有一个重大问题,就是网络分区会导致没有任何一个分区会拥有超过⅔的投票权(比如超过⅓投票权在线下),这会中断共识。而Cosmos架构可以缓解这个问题,它可以使用全球中心,同时,各分区实行地区自治,然后让每个分区的投票权按照正常的地域位置进行分配。如,一般范例就有可能是针对个别城市或地区的,让他们各自运行自己分区的同时,还能共享共同的枢纽(比如Cosmos枢纽),并且在临时的网络分区导致的中断期间,也可以继续维持地区自治活动。注意,这样一来在设计稳健的联邦式容错系统过程中,就可以去考虑真实的地理、政治及网络拓扑的特征了。 ### 联邦式名称解析系统 -NameCoin 是首批试图通过比特币区块链解决名称解析问题的区块链之一。不幸的是,这个方案存在一些不足。 +NameCoin是首批试图通过比特币技术解决名称解析问题的区块链之一。不过,这个方案存在一些不足。 -比如,我们可以通过 Namecoin 来验证*@satoshi*(中本聪)这个号是在过去某个时间点用特定公钥进行注册的。但是,该公约是否更新过我们就不得而知了,除非将该名称最后一次更新以来的全部区块都下载下来。这一点是因为比特币 UTXO 交易模式中梅克尔式模型的局限性所导致的,这类模型中只有交易(而非可变的应用程序状态)会以梅克尔形式加入到区块哈希中。它会让我们证明之后名称更新的存在,而非不存在。因此,我们必须依靠完整节点才能明确这个名称的最近价值,否则就要投入巨大成本来下载整个区块链。 +例如,我们可以通过Namecoin来验证@satoshi(聪)这个号是在过去某个时间点用特定公钥进行注册的。但是,该公钥是否更新过我们就不得而知了,除非将该名称最后一次更新之前的所有全部下载。这一点是比特币UTXO交易模式中默克尔化模型的局限性导致的,这类模型中只有交易(而非可变的应用状态)会以默克尔化加入到区块哈希中。它让我们得在之后用更新证明名称的存在,而非不存在。因此,我们必须依靠全节点才能明确这个名称的最近的值,或者花费大量资源下载整个区块链。 -即使在 NameCoin 运用了默克尔化的搜索树,其工作量证明的独立性还是会导致轻客戸端的验证出现问题。轻客戸端必须下载区块链中所有区块头的完整复件(或者至少是自其最后的名称更新后的所有区块头)。这意味着带宽需要会随着时间直线扩展。 [\[21\]](21)此外,在工作量证明制区块链上的名称更改需要等额外的工作量证明验证区块才能进行,这个在比特币上可能要花上一个小时。 +即使在NameCoin上运用默克尔化的搜索树,其工作量证明的独立性还是会导致轻客戸端的验证出现问题。轻客戸端必须下载区块链中所有区块头的完整备份(或者至少是自其最后的名称更新的所有区块头)。这意味着带宽需要随着时间做线性的扩展。 [21]此外,在工作量证明制度使区块链上的名称更改需要等额外的工作量证明验证确认才能进行,它在比特币上可能要花费一个小时。 -有了 Tendermint,我们只需用到由法定数量验证人签署(通过投票权)的区块哈希,以及与名称相关的当前价值的默克尔证明。这点让简易、快速、安全的轻客戸端名称价值验证成为可能。 -在 Cosmos 中,我们可以借助这个概念对其进行延伸。Cosmos 中的每个名称注册空间都能有一个相关的最高级别域名(TLD),比如".com"或者".org"等,每个名称注册空间都有其本身的管理和登记规则。 +有了Tendermint,我们只需用到由法定数量验证人签署(通过投票权)的区块哈希,以及与名称相关的当前值的默克尔证明。这点让简易、快速、安全的轻客戸端名称值验证成为可能。 -## 发行与激励 +在Cosmos中,我们可以利用这个概念并延伸。每一个在Cosmos上的名称注册都能有一个相关的最高级别域名(TLD),比如".com"或者".org"等,而且每个名称注册分区都有自己的管理和登记规则。 + +## 发行与激励 ### Atom 代币 -Cosmos Hub(Cosmos 中心)是多资产分布式账本,不过它也有本地代币,叫做 Atom。Atom 是 Cosmos Hub 唯一的权益代币。Atom 是持有人投票、验证或委托给其他验证人的许可证,就像以太坊的以太币以太币一样,Atom 也可以用来支付交易费以减少电子垃圾。额外的通胀 Atom 和区块交易费用就作为验证人及委托人(委托给其他验证人)的奖励。 +Cosmos 枢纽是多资产分布式账本,它有自己的代币,是 Atom 。Atom 是 Cosmos 枢纽唯一的权益代币。Atom是持有人投票、验证或委托给其他验证人的许可证明,就像以太坊上的以太币一样,Atom也可以用来支付交易费以减少电子垃圾。额外的通胀 Atom 和区块交易费用就作为激励分给验证人及委托验证人。 -BurnAtomTx 交易可以用来恢复储蓄池中任意比例的代币。 +`BurnAtomTx`交易可以用来恢复储蓄池中任意比例的代币。 #### 众筹 -创世块上的 Atom 代币及验证人的初次分布会是 Cosmos 众销资助人占 75%,预售资助人 5%,Cosmos 公司占 20%。从创世块开始,总 Atom 总量的 1/3 将作为奖励发放给每年绑定的验证人以及委托人。 +创世块上的 Atom 代币及验证人的初次分发是 Cosmos 众售参与者持有75%,预售参与者持有5%,Cosmos网络基金会持有10%,ALL IN BITS, 集团持有10%。从创世块开始,Atom 总量的1/3将作为奖励发放给每年担保持有的验证人以及委托人。 -额外细节详见 [Crowdfund Plan](https://github.com/cosmos/cosmos/blob/master/PLAN.md)。 +更多细节见 [Cosmos Plan](https://github.com/cosmos/cosmos/blob/master/PLAN.md) -#### 归属 +### 验证人的数量限制 -为了防止那些炒股诈骗的投机者借众筹来进行短期牟利,创世块的 Atom 必须有所归属才能用于转移。每个账户将在为期两年的时间里以每小时恒速授予 Atom,这个速率由创世块 Atom 总量除以(2 \* 365 \* 24)小时得出。通胀区块获得的 Atom 奖励是预先授予的,可以立即进行转移,因此第一年绑定的验证人及委托人可以挣取比其创世块 Atom 一半还多的奖励。 +与比特币或其他工作量证明区块链不同的是, 由于通信的复杂性增加, Tendermint 区块链会随着验证人的增加而变慢。幸运的是, 我们可以支持足够多的验证人来实现可靠的全球化分布式区块链, 并具有非常快的交易确认时间。 而且随着带宽、存储和并行计算容量的增加, 我们将来能够支持更多的验证人。 -### 验证人的数量上限 +在创世日, 验证人的最大数量将设置为 100, 这个数字将以13% 的速度增长10年, 最终达到300位。 -Tendermint 区块链和比特币之类的工作量证明区块链不同,由于通信复杂度提升,验证人增加,所以速度会更慢。所幸的是,我们可以支持足够多的验证人来实现全球稳健的分布式区块链,使其拥有较短交易验证时间,此外,在提升带宽、内存以及平行电脑计算能力的提升下,在未来支持更多验证人的参与。 +``` +Year 0: 100 +Year 1: 113 +Year 2: 127 +Year 3: 144 +Year 4: 163 +Year 5: 184 +Year 6: 208 +Year 7: 235 +Year 8: 265 +Year 9: 300 +Year 10: 300 +... +``` -在创世块诞生那天,验证人数量最多将设置为 100,之后十年的增长率将在 13%,最终达到 300 位验证人。 +### 成为创世日后的验证人 - Year 0: 100 - Year 1: 113 - Year 2: 127 - Year 3: 144 - Year 4: 163 - Year 5: 184 - Year 6: 208 - Year 7: 235 - Year 8: 265 - Year 9: 300 - Year 10: 300 - ... -### 成为创世日后首个验证人 +Atom 持有者可以通过签署和提交 `BondTx` 交易成为验证人。抵押的 atom 数量不能为零。任何人任何时候都成为验证人, 除非当前验证人组的数量超过了最大值。在这种情况下, 只有当持有 atom 的数量大于现有验证人中持有有效 atom 数量的最少者, 该交易才有效, 其中有效 atom 包括受委托的 atom。当一个新的验证人以这种方式替换现有的验证人时, 现有的验证人将离线, 其所有的 atom 和受委托的 atom 进入解绑状态。 -如果 Atom 持有人还没有成为验证人,那么可以通过签署提交 BondTx 交易来成为验证人,其中作为抵押品的 Atom 数量不能为零。任何人在任何时候都可以作为验证人,除非当前验证组的数量超过了最大值。这样的话,除非 Atom 数量比最小验证人持有的有效 Atom(包括受委托的 Atom)还要多,那么交易才算有效。如果新验证人通过这种方式取代了现有验证人,那么现有验证人就被中止活动,所有 Atom 和受委托的 Atom 都会进入解绑状态。 +### 对验证人的惩罚 -### 针对验证人的惩罚 -针对验证人必须有一定的惩罚机制,防止他们有意无意地偏离已批准的协议。有些证据可以立即采纳,比如在同样高度和回合的双重签名,或者违反"预投票锁定"的(这一规则在 Tendermint 共识协议中有列出)。这类证据将导致验证人损失良好信誉,而且其绑定的 Atom 还有储备池内一定比例的代币份额——合起来称作其"权益"——也会减少。 +对于任何有意或无意的偏离认可协议的验证人, 必须对其施加一定的惩罚。有些证据立即可予受理, 比如在同样高度和回合的双重签名, 或违反 "预投票锁定" (Tendermint 协商一致议定书的规则)。这样的证据将导致验证人失去其良好的声誉, 其绑定的 atom 以及在储备池中的比例份额 – 统称为 “权益” – 将被大幅削减。 -有时因为地区网络中断、电力故障或者其他原因,验证人会无法连通。如果在过去随便什么时间点的 ValidatorTimeoutWindow 区块中,验证人在区块链中提交的投票没有超过 ValidatorTimeoutMaxAbsent 次,那么验证人将会被中止活动,并且从权益中共损失一定的验证人超时罚款(ValidatorTimeoutPenalty ,默认为 1%)。有些劣行表露的没那么明显,这样的话,验证人就可以在带外协调,强制叫停这类恶意验证人,如果有绝对多数制共识的话。 +有时, 由于区域网络中断、电源故障或其他原因, 验证人将不可用。如果在过去任意时间点的 `ValidatorTimeoutWindow` 块中, 验证人的提交投票不包括在区块链中超过 `ValidatorTimeoutMaxAbsent` 次, 该验证人将离线, 并减少 `ValidatorTimeoutPenalty` (默认 1%) 的权益。 -如果 Cosmos 中心因为超过 ⅓ 的投票权在线下合并而出现了中止情况,或者说超过 ⅓ 的投票权合并来审查进入区块链的恶意行为,这时候中心就必须借助硬分叉重组协议来恢复。(详见"分叉与审查攻击") + +一些 "恶意" 行为在区块链上并没有产生明显的证据。在这些情况下, 如果存在多数的协商一致, 则验证人可以在带外协调,强制将这些恶意验证人超时。 + + +如果 Cosmos 枢纽 因为超过⅓的投票权离线而出现了中止情况,或者说超过⅓的投票权审查到进入区块链的恶意行为,这时候枢纽就必须借助硬分叉重组协议来恢复。(详见“分叉与审查攻击”) ### 交易费用 -Cosmos Hub 验证人可以接受任何中共类的代币或组合作为处理交易的费用。每个验证人可以主观设置任意兑换率,并且选择它想要进行的交易,只要没有超过区块 Gas 限制(BlockGasLimit)。收集起来的费用剪去下面列出的任意税费后,会再次根据权益相关人绑定的 Atom 比例进行分配,周期是就是每次验证人支付的时间(ValidatorPayoutPeriod,默认为 1 小时)。 -在所有交易费用中,储存税(ReserveTax,默认为 2%)将存入储备池来增加储备量,来提高 Cosmos 网络的安全性及价值。普通税(CommonsTax,默认为 3%)合并到普通商品的资金中。这些资金将进入托管人地址(CustodianAddress)根据管理熊进行分配。将投票权委托给其他验证人的 Atom 持有人会支付一定佣金给委托方,而这笔费用可以由每个验证人进行设置。 +Cosmos 枢纽验证人可以接受任何种类的代币或组合作为处理交易的费用。每个验证人可自行设置兑换率, 并选择其想要的交易, 只要不超过 `BlockGasLimit`, 每隔 `ValidatorPayoutPeriod` (默认为1小时) 时间会根据权益相关人绑定的 Atom 比例进行分配。 + + +在所收取的交易费用中, `ReserveTax` (默认 2%) 将存入储备池来增加储备量, 增加 Cosmos 枢纽的安全性和价值。这些资金也可以按照治理系统的决策进行分配。 + + +将投票权委托给其他验证人的 Atom 持有人会支付一定佣金给委托方,而这笔费用可以由每个验证人进行设置。 ### 激励黑客 -Cosmos Hub 的安全是一组函数,涉及底层验证人的安全以及委托人的委托选择。为了鼓励发现并及时报告缺陷,Cosmos Hub 允许黑客通过 ReportHackTx 交易来"邀功",主要就是说明,"这个加点已被攻击,请将奖金发到这个地址"。通过这类功绩,验证人和委托人的行为将被中止,而黑客赏金地址可以收到每个人 Atom 中攻击奖励比率(HackRewardRatio,默认为 5%)。而验证人必须通过使用备份密钥来恢复剩余的 Atom。 +Cosmos 枢纽的安全取决于底层验证人的安全性和委托人的委托选择。为了鼓励发现和早期报告发现的漏洞, Cosmos 枢纽鼓励黑客通过 `ReportHackTx` 交易发布成功的漏洞, 说, "这个验证人被入侵了,请把赏金发送到这个地址"。这种情况下, 验证人和委托人将挂起闲置, 每个人 `HackPunishmentRatio` (默认 5%) 的 atom 将被削减, `HackRewardRatio` (默认 5%) 的 atom 将发送到黑客的赏金地址作为奖励。验证人必须使用其备份密钥来恢复剩余的 atom。 -为了防止这个特征被滥用于转移未授权的 Atom,ReportHackTx(黑客报告交易)前后验证人和委托人手中的两类 Atom 的比例(授权的与未授权的)将保持不变,而黑客的赏金将包含未授权的 Atom,如果有的话。 +为了防止这一特性被滥用于转移未授权的 atom, 在 `ReportHackTx` 前后,Atom的比例(授权的与未授权的) 将保持不变, 而黑客的赏金将包括一些未授权的 atom (如果有的话)。 -## 管理 +### 治理规范 -Cosmos Hub 通过分布式组织来运行,这类组织要求有一套完备的管理机制,从而协调区块链上的各类变动,比如系统变量参数,以及软件更新、规章更改等。 +Cosmos 枢纽是由一个分布式组织管理的, 需要一个明确的治理机制, 以协调对区块链的各种变化, 如系统的参数变量, 以及软件升级和宪法修订. -所有验证人对所有提案的投票负责。如果没能及时对提案做出投票,那么验证人就会在一段时间内自动失去活动权利,这段时间叫做缺席惩罚期(AbsenteeismPenaltyPeriod,默认为一周)。 +所有验证人负责对所有提案进行表决。如果未能及时对提案进行表决, 将导致验证人被自动停用一段时间。 这段时间被称为 `AbsenteeismPenaltyPeriod` (默认1周)。 -委托人自动继承委托验证人的投票权。这一投票可能会被手动覆盖掉。而未绑定的 Atom 是没有投票权的。 -每个提案都需要一定的保证金,即最低提案保证金(MinimumProposalDeposit )代币,这个可以是代币组合也可以是更多代币包括 Atom。对每一个提案,投票人可能会投票来取走保证金呢。如果超过一半的投票人选择取走保证金(比如,由于提案是垃圾信息之类),那么保证金就会进去储备池,除非有任何 Atom 被燃烧。 +委托人自动继承其委托的验证人的投票权。这一投票可以被手动覆盖掉。而未绑定的 Atom 是没有投票权的。 -对于每一个提案,投票人可能会投以下选项: -- 同意 -- 强烈同意 -- 反对 -- 强烈反对 -- 弃权 +每个提案都需要 `MinimumProposalDeposit` 代币的保证金, 这可能是一个或多个代币 (包括atom) 的组合。对于每项提案, 投票者可以投票表决取走保证金。如果超过半数的投票者选择取走保证金 (例如, 因为提案是垃圾信息), 那么保证金就会存入储备池, 除了被燃烧的 atoms。 -决定采纳(或不采纳)提案需要严格的多数投"同意"或"强烈同意"(或者"反对"及"强烈反对"),但是超过 1/3 的人投"强烈反对"或"强烈支持"的话就可以否决大多数人的决定。如果大多数人的票都被否决,那么每个人都会得到惩罚,即损失否决惩罚费用块那一部分钱( VetoPenaltyFeeBlocks,默认是一天的区块值 ,税费除外),而否决大多数决定的那一方也会受到额外的惩罚,即损失否决惩罚 Atom(VetoPenaltyAtoms,默认为 0.1%)。 +对于每项提案, 投票人可以选择下列方案: -### 参数改变提案 +* 同意 +* 强烈同意 +* 反对 +* 强烈反对 +* 弃权 -这里定义的任何参数都可以发生改变,主要在参数改变提案(ParameterChangeProposal)的接受范围内。 +决定采纳(或不采纳)提案需要严格的多数投“同意”或“强烈同意”(或者“反对”及“强烈反对”),但是超过1/3的人投“强烈反对”或“强烈支持”的话就可以否决大多数人的决定。如果大多数人票被否决,那么他们每个人都会失去 `VetoPenaltyFeeBlocks` (默认是一天的区块值 ,税费除外) 作为惩罚,而否决大多数决定的那一方还将额外失去 `VetoPenaltyAtoms` 默认为0.1%)的 Atom 作为惩罚。 + +### 参数变更提案 + + +这里定义的任何参数都可在 `ParameterChangeProposal` 通过后改变。 + +### 赏金提案 + + +通过 `BountyProposal` 后, Atom 可以增发和预留储备池资金作为赏金。 ### 文本提案 -所有其他提案,比如用来更新协议的提案,都会通过通用的文本提案(TextProposal)来协调。 -## 路线图 +所有其他提案,比如用来更新协议的提案,都会通过通用的 TextProposal 来协调。 -详见 [计划](https://github.com/cosmos/cosmos/blob/master/PLAN.md)。 -## 相关工作 +## 路线图 -过去几年,关于区块链共识及可扩展性已经有过多次创新,这一部分将挑选一些重要的创新进行简短分析。 + +详见[计划](https://github.com/cosmos/cosmos/blob/master/PLAN.md). + +## 相关工作 + +过去几年涌现了很多区块链共识及扩展性方面的创新。在这一部分中将挑选一些重要的创新进行简单分析。 ### 共识系统 #### 经典拜占庭容错 -二十世纪八十年代早期的共识机制中就已经出现了恶意参与者,当时 Leslie Lamport 杜撰了"拜占庭容错"这个词,用来指那些图谋不轨参与者做出的恣意妄为的行径,这个词和"死机故障"相对,死机故障就只是处理过程崩溃而已。早期针对同步网络也探索出了一些解决方案,其信息滞后有一个上限,尽管实际使用是在高度受控的环境下进行的(比如飞机控制器以及原子钟同步的数据中心)。直到九十年代后期,实用拜占庭容错(PBFT)才被引用,作为有效的、部分同步的共识算法,以容忍处理过程中 ⅓ 的恣意行为。PBFT 成为标准算法,繁殖了各种衍生品,包括最近 IBM 用于超级账本中的。 +二十世纪八十年代早期就开始研究存在恶意参与者的共识机制,当时Leslie Lamport创造了”拜占庭容错”这个词,用来指那些图谋不轨参与者做出的恶意的行为,与”死机故障”不同,后者只是处理过程崩溃而已。早期针对同步网络也探索出了一些解决方案,网络信息滞后有一个上限,但实际使用是在高度受控的环境下进行,比如精密飞行仪器以及使用原子钟同步的数据中心。直到九十年代后期,实用拜占庭容错( Practical Byzantine Fault Tolerance ,PBFT)[\[11\]][11]才作为有效的、部分同步的共识算法被逐步推广。它可以容忍⅓参与者有恶意行为。PBFT成为标准算法,催生了各种版本,包括最近由IBM提出并使用于Hyperledger超级账本中的算法。 -和 PBFT 相比,Tendermint 共识的主要好处在于其有一套经过改善且简化了的底层结构,其中有些是拥抱区块链范例的结果。Tendermint 区块必须按顺序提交,这一点可以消除复杂性,节省与 PBFT 浏览变化相关的通信开支。在 Cosmos 和众多加密币中,如果区块 N 本身没有提交,那么就无需让区块 N+i(i*>=1*)来提交。如果是带宽导致了区块 N 未提交到 Cosmos 空间,那么它就不会帮助使用带宽将选票共享给区块 N+i。如果是网络分区或者线下节点导致的区块 N 未提交,那么 N+i 就无论如何也不会提交。 +和PBFT相比,Tendermint共识的主要好处在于其改善且简化了的底层结构,其中有些是遵循了区块链典范的结果。Tendermint中,区块必须按顺序提交,这就消除复杂性,节省PBFT中状态变化相关的通信开支。在Cosmos和众多加密币中,如果区块N本身没有提交,那么就不能让它之后的区块N+i(i>=1)提交。如果是通信带宽限制导致了区块N未提交到Cosmos Zone上,那么将通信带宽用于分享选票给区块N+i是一种浪费。如果由于网络分区或者节点掉线导致的区块N未提交,那么N+i就无论如何也不能提交。 -此外,区块内交易的批量处理可以对应用程序的状态进行默克尔哈希,而不是用 PBFT 检查机制进行周期消化。这可以实现轻客戸端更快的证明交易提交,以及更快的跨区块链通信。 +此外,将交易打包成块可以用默克尔哈希纪录应用程序的状态,而不是用PBFT检查机制进行定时摘要。这可以让轻客戸端更快的提交交易证明,以及更快的跨链通信。 -Tendermint Core 中有很多优化项和特点都超过了 PBFT 特定的性能。比如,验证人发起的区块被分割成部分,默克尔化然后散布开来,这种方式可以提高其广播性能(关于启发请查看 LibSwift [\[19\]](19))。而且,Tendermint Core 不会对点对点连接做任何假设,只要点对点网络仍有微小连接,那么它就能正常运行。 +Tendermint Core中也优化了很多PBFT特性以外的功能。比如,验证人提交的区块被分割多个部分,对其默克尔化后,然后在节点间广播。这种方式可以提高其广播性能(具体请查看LibSwift [19])。而且,Tendermint Core不会对点对点连接做任何假设,只要点对点间的网络不断开,那么它就能正常运行。 -#### BitShare 委托权益 +#### BitShare委托权益 -BitShares [\[12\]](12)不是第一个部署权益证明机制(PoS)的区块链,但是其对 PoS 区块链的研究与采纳做出了巨大的贡献,尤其是这些被认为是"受委托的" PoS。在 BitShares 中,股权持有者选择"见证"以及"委托",其中"见证"负责下单并提交交易,而"委托"负责协调软件更新与参数变化。尽管 BitShare 在理想环境下的性能很高(100k tx/s,1 秒的滞后),但是它也有可能受到恶意见证施加的重复使用攻击,这类攻击会导致区块链出现分叉而不用受到任何经济惩罚——它的惩罚来自于"没有任何权益"。BitShare 试图通过允许交易查看近期区块哈希来缓解这个问题。此外,权益相关者可以每天移除或替代行为不佳的见证,尽管这个对成功的重复使用攻击来说根本不算什么明确的惩罚。 +BitShares [\[12\]][12]不是第一个采用权益证明机制(proof-of-stake,PoS)的区块链,但是其对PoS在区块链上的研究与推进做出了巨大的贡献,尤其是在DPoS,即受委托权益证明方面。在BitShares中,相关方选择”见证者”负责提交交易顺序并提交;相关方选择”委托人”负责协调软件更新与参数变化。尽管BitShare在理想环境下能达到很高的性能:100k tx/s,1秒的滞后。每一块只有一个单独的签名,得到交易的最终性的时间比区块时间略长。一个标准的协议仍在开发中。利益相关者可以每天去除或者替换有恶意行为的验证人,但是不同于Tendermint PoS的保证金机制,BitShares没有要求验证人或者代理人的提交押金,如果发生双花攻击的话,押金不会被削减。 #### Stellar -Stellar [\[13\]](13)是在 Ripple 的解决方案上创建的,它精炼了联邦拜占庭协议模型,其中参与共识的进程不包括固定且举世闻名的组件。相反,每个处理节点管理一个或多个"仲裁集碎片"(每一个都组成一组可靠的进程)。Stellar 中的"仲裁集"被定义为一组节点,其中每一节点包含(是一个超集合)至少一个仲裁集碎片,这样就可以达成协议。 +Stellar [\[13\]][13]是以Ripple推行的解决方案为基础,它优化了联邦拜占庭协议模型,其中参与共识过程的并不构成一个固定的全局过程。 相反,每个进程节点组织一个或多个“仲裁片”,每个“仲裁片”构成一组可信进程。 Stellar中的“法定人数”被定义为一组包含至少个个节点的一个仲裁片。从而可达成一致。 -机制的安全性依靠假设实现,即假设任意两个仲裁集的交集并非为空,而实现节点的可用性则需要至少一个仲裁集碎片来完整组成正确节点,这个在使用或大或小的仲裁集碎片间可能会产生一组权衡,因为要在不对信任施加重要假设的情况下来进行平衡是很困难的。最终,节点必须以某种办法来选择充足的仲裁集碎片,从而保证有足够多的容错(或任何"完整无损的节点",大部分文章里得出的结果就依靠这个来决定)。此外,唯一用来确保这类配置的战略是等级制的,且和 BGP 协议相似(边界网关协议),可用于互联网高层 ISP 来创建全球路由表,或者是用在浏览器中管理 TSL(传输层安全)证书,不过这两者都因其不安全性而臭名昭著。 +恒星机制的安全性依赖于任何两个仲裁的交集都是非空的假设,同时,节点的可用性要求至少一个“仲裁片”完全由诚实节点组成。这就需要在“法定人数”的大小上作出妥协:人数过少难以获得共识,人数过多则难以信任所有人。可能难以平衡而不对信任做出重大假设。 除此之外,节点必须维护一定数量的仲裁片以获得足够的容错能力(或者任何“完整节点”,大部分结果的依赖),并且提供一种层级化的配置策略,类似于边界网关协议(Border Gateway Protocol,BGP)。BGP被互联网服务供应商(Internet Service Provider,ISP)用来建立全球路由表,也被浏览器用来管理传输层安全协议(Transport Layer Security,TLS)证书。他们都因为不安全而臭名昭着。 -Stellar 文章中对基于 Tendermint 的权益证明系统的批判可以通过以下代币战略加以缓和,其中有一种新的代币叫做"atom",可以通过发布这一代币来代表未来的费用及奖励。所以,Tendermint 权益证明机制的好处就是其简易性,在相对简易的同时,提供足够多且可证明的安全保障。 + +对于Stellar论文中基于Tendermint的PoS批评可以通过本文描述的代币策略来缓解。本文提出了名为 _atom_ 的新的代币,它代表未来交易过程中产生的费用和奖励。 基于Tendermint PoS的优势在于其原理相对简单,同时仍可充分保证和证明安全性。 #### BitcoinNG -BitcoinNG 是针对比特币提出来的一种改善,或将允许垂直扩展形式,比如增加区块容量,并且不会带来负面经济影响(一般这类变动都会带来这类影响),比如越小的矿工受到的影响越大。这一改善可以通过将是首项选择从交易广播中分离来实现,即由"微区块"中的工作量证明先选择群首,之后可以对要提交的交易进行广播,直到新的微区块被发现。这个过程会减少赢得工作量证明比赛所必须的带宽需求,让小矿工可以更公平的参与竞争,然后让最后找到微区块的矿工来定期提交交易。 +BitcoinNG是对比特币的改进,允许垂直扩展,比如增加块的大小而避免带来负面的经济后果,例如对矿工造成的影响严重不成比例。 这种改进是通过将leader选举与交易广播分开来实现的:leader首先通过“微块micro-blocks”的PoW选举,然后leader能够广播交易直至下一个新的“微块”。 这减少了赢得PoW比赛所需的带宽要求,使矿主竞争更公平,并通过允许最后一名矿工提交微块以加快交易提交频率。 #### Casper -Casper [\[16\]](16)是针对以太坊提出的一个权益证明共识算法。其最初运行模式是"赌注共识",理念就是让验证人反复对其认为会提交到区块链的区块下赌注(根据它之前打赌的经验来),最后得出结论。 [链接](https://blog.ethereum.org/2015/12/28/understanding-serenity-part-2-casper/)。这是 Casper 团队活跃中的研究领域,其挑战就是搭建一套进化稳定的打赌机制。和 Tendermint 相比,Casper 主要的优势就在于提供了"优于一致性的实用性"——其共识无需超过 ⅔ 的仲裁选票——可能其代价就是速度慢以及实现复杂。 +Casper [\[16\]][16]是以太坊提出的PoS共识算法。 它的主要操作模式是“预测押注”的一致。 通过让验证者基于他们目前看到的其他投注来迭代地下注他们认为哪个块将提交入区块链。最终性可以立即实现。 + +[链接](https://blog.ethereum.org/2015/12/28/understanding-serenity-part-2-casper/). 这是Casper团队研究的一个热点领域。 挑战在于构建一个可以证明是一个演化稳定策略的投注机制。 与Tendermint相比,Casper的主要优势可能在于提供“可用性超越一致性” — 达成共识不需要超过50%的投票权 — 可能是以提交速度或实现复杂性为代价的。 ### 水平扩展 -#### Interledger 协议 +#### Interledger协议 -Interledger 协议 [\[14\]](14)严格来说不算是可扩展解决方案。它通过一个松散耦合的双边关系网络,为不同账本系统提供了特别的互操作性。比如闪电网络,ILP 的目的就是促进支付,不过是以不同账本类型的支付为主,并且延展了原子交易机制,将哈希锁以及公证人的仲裁集(也叫做原子传输协议)都包括进去。用于维持账本间交易原子数的后面这种机制和 Tendermint 的轻客戸端 SPV 机制相似,因此就可以对 ILP 和 Cosmos/IBC 之间的区别加以解释了,具体如下: +Interledger协议(The Interledger Protocol,ILP)[\[14\]][14]不是一种严格的扩展方案。 它通过一个松散耦合的双边关系网络,提供一种指定的跨不同账本系统交互操作。像闪电网络一样,ILP的目的是实现支付,但是它特别关注跨账本类型的支付,并扩展原子事务的处理机制,使得事务的处理不仅支持哈希锁,而且还包括法定人数的公证人(称为原子运输协议)。 后者在账本间交易中实施原子性的机制与Tendermint的轻客户SPV机制类似,因此比较ILP和Cosmos / IBC之间的区别是有必要的,具体见下文。 -1. 在 ILP 中连接器的公证人不支持成员变动,并且不允许在公证人间进行灵活的权重。而 IBC 是专门为区块链设计的,其验证人可以有不同的权重,而且成员也可以根据区块链进程进行变动。 -2. 在闪电网络中,ILP 付款接收人必须在线来发送确认函给发送者。而在 IBC 代币传输过程中,接收人区块链的验证组会负责提供确认,而非接收人。 -3. 两者最大的不同就是 ILP 的连接器不充当支付状态的权威方,而在 Cosmos 中,一个中心的验证人就是 IBC 代币传输状态以及每个空间所持代币总量(不是空间每个账户所持代币总量)的权威见证人。这是一个根本性创新,允许代币在空间里进行安全且非对称的传输,而在 Cosmos 中,充当 ILP 连接器的角色是一个持久且安全的区块链账本——Cosmos 中心(Cosmos Hub)。 -4. ILP 账本间支付需要由一份交易订单簿做支持,因为其不包括代币从一个账本到另一个账本的非对称传输,而只有价值或市场等值在传输。 +1.ILP不支持连接器公证员的变更,也不允许公证员之间有灵活的权重。 另一方面,IBC是专门为区块链设计的,验证人可以拥有不同的权重,并且随着区块链的发展,成员可以随时更改。 + +2.与闪电网络一样,ILP中的接收人必须在线才能向发起人发送确认。 在IBC代币传输中,接收者所在区块链的验证人集合负责提供确认,而不是接收用户本人。 + +3.最大的不同在于ILP连接器不需要负责对支付状态保持权威性,然而在Cosmos中,hub的验证人负责IBC代币传输状态和每个zone内持有代币数量的权威性。允许从zone之间的安全地不对称地交换代币是本质的创新。在cosmos中的ILP连接器可以看作是一个持久和最安全的区块链分类账:cosmos hub。 + +4.ILP内的跨账本支付需要一个交易所的指令集的支持。因为不存在从一个分类账到另一个分类账不对称的代币转移,只能做到市场等价物的转移。 #### 侧链 -侧链 [\[15\]](15)是针对比特币网络扩展提出的一个机制,通过与比特币区块链"挂钩"的可替代区块链实现。侧链可以让比特币有效从区块链转移到侧链及后台,还可以在侧链上进行新功能测试。在 Cosmos Hub 中,侧链和比特币是彼此的轻客戸端,使用 SPV(安全协议验证工具)证明来决定什么时候转移代币到侧链及后台。当然,因为比特币采用工作量证明机制,所以以比特币为中心的侧链也遇到很多工作量证明作为共识算法而带来的问题与风险。此外,这个是比特币多数派解决方案,它并不支持本地代币以及空间内网络拓扑学,而 Cosmos 可以。也就是说,这种双向挂钩的核心机制在原则上是和 Cosmos 网络运用的机制一样的。 +Sidechains [\[15\]][15]是一种通过使用与比特币区块链“双向挂钩”替代区块链来扩展比特币网络性能的机制。(双向挂钩相当于桥接,在cosmos中被称为“桥接”与市场挂钩区分)。侧链使得比特币可以方便的在比特币区块链和侧链间移动,并允许在侧链上实验新功能。在Cosmos Hub中,侧链和比特币是彼此的轻客户端,在比特币区块链和侧链间移动时使用SPV证明。当然,由于比特币使用PoW,以比特币为中心的侧链遭受许多由于PoW作为共识机制的引起的问题和风险。而且,这是一个比特币利益最大化的解决方案,并不像Cosmos那样本地支持各种代币和zone间网络拓扑结构。但是,双向挂钩的核心机制原则上与Cosmos所采用的机制相同。 -#### 以太坊在可扩展方面的努力 +#### 以太坊扩展性的努力 -以太坊目前正在研究不同的方法来实现以太坊区块链状态的碎片化,从而解决可扩展问题。这些努力的目标就是在共享状态空间中维持当前以太坊虚拟机提供的抽象层。他们同时开展了多项研究。 [\[18\]](18) [\[22\]](22) -##### Cosmos vs 以太坊 2.0 Mauve +以太坊目前正在研究许多不同的战略,将以太坊区块链的状态分区化,以解决可扩展性的需求。 这些努力的目标是在共享状态空间之上,维持当前以太坊虚拟机提供抽象层。 目前,多项研究工作正在进行。[\[18\]][18] [\[22\]][22] -Cosmos 和以太坊 2.0 Mauve [\[22\]](22)有不同的设计目标。 +##### Cosmos vs Ethereum 2.0 Mauve -- Cosmos 专注代币。而 Mauve 是和扩展普通计算相关。 -- Cosmos 不一定是以太坊虚拟机,因此即使是不同的虚拟机也可以进行交互。 -- Cosmos 可以让空间创建者决定谁来验证空间。 -- 任何人都可以在 Cosmos 开创新空间(除非管理有其他决定)。 -- 中心会隔离空间故障,这样全球代币的不变性就得到了维护。 -### 普通扩展 +Cosmos 和 Ethereum 2.0 Mauve [\[22\]][[22] 有不同的设计理念。 + +* Cosmos是针对代币儿 Mauve 是关于扩大计算能力。 +* Cosmos不仅限于 EVM, 所以即使不同的VM也可以交互。 +* Cosmos 让zone的创建者决定验证人。 +* 任何人都可以在Cosmos中建立新的zone(除非管理者另做决定)。 +* hub与zone的失效隔离,所以全局的代币不变量可以保持。 + +### 普遍扩展 #### 闪电网络 -闪电网络是一种代币传输网络,在比特币区块链(及其他公共区块链)上一层运行,能够执行众多改善交易吞吐量的指令,通过将大多数交易从共式账本移出,转入所谓的"支付信道"内。这个举措通过链上的加密货币脚本或可实现,它可以让参与者进入双方有状态的合约,其中状态可以通过共享数字签名进行更新,并且最后将证据公布到区块链后可以关闭合约,这个机制最初是通过跨链原子掉期而普及的。通过开放多方支付信道,闪电网络的参与者可以成为他人支付的路由焦点,带领全面连接的支付信道网络,代价就是绑定在支付信道上的资本。 -虽然闪电网络可以轻松在多个单独区块链间延伸以通过交易市场进行价值传输,但是它无法用来进行区块链间的非对称代币传输。Cosmos 网络的主要好处就是能够进行这类直接代币传输。也就是说,我们可以期待支付信道与闪电网络在我们的代币传输机制下投入广泛应用,一方面为了节省成本,另一方面也可以保证隐私。 +闪电网络被设计成一种代币传输网络,在比特币区块链(及其他公有区块链)上一层运行,通过把大部分交易从共识分类账之外转移到所谓的“ 付款渠道“。 这通过链上加密货币脚本来实现,这些脚本使双方能够进入双方持有的状态化合同,通过共享数字签名来更新状态,并且在合同结束后最终通过在区块链上发布证据,这种机制首先受到跨链原子互换交易的欢迎。 通过与多方开通支付渠道,闪电网络的参与者可以成为集中点,为其他人的支付提供路由,从而导致支付渠道网络的完全联通,其代价是绑定在支付渠道上的资金。 -#### 隔离见证 +虽然闪电网络也可以轻松地跨越多个独立的区块链,并借助交易市场实现价值转移,但它不能实现从一个区块链到另一个区块链的非对称代币交易。 这里描述的Cosmos网络的主要优点是实现直接的代币交换。 也就是说,我们希望支付渠道和闪电网络将会与我们的代币传输机制一起被广泛采用,从而节省成本和保护隐私。 -隔离见证是一项比特币改善提议( [连接](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)),其目的是将每个区块的交易吞吐量提高 2-3 倍,并且同时保证区块快速同步新节点。这个方案的出色就在于它可以在比特币当前协议的限制下运行,并且允许进行软分叉更新(也就是其他旧版本的客户端软件在更新后可以继续运行)。Tendermint 是一种新的协议,它没有设计限制,因此可以有不同的扩展选择。首先,Tendermint 采用拜占庭容错循环制算法,这个是在加密签字而非挖矿的基础上进行,可以简单地通过多平行区块链进行水平扩展,而定期频繁的区块提交还可以进行垂直扩展。 +#### 隔离验证人 -## 附录 + +隔离见证是一个比特币改进建议BIP,[链接](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) 旨在将每块交易吞吐量提高2倍或3倍,同时使新节点能更快同步区块。 这个解决方案的亮点在于它如何在比特币当前协议的局限下,允许软分叉升级(例如,具有旧版本软件的客户端将在升级后仍可继续运行)。 Tendermint作为一个新的协议没有设计的限制,所以它具有不同的扩展优先级。 TendermintBFT的循环算法,主要基于加密签名而不是采矿,这种算法允许通过多个并行区块链进行水平扩展,而更常规、更频繁的区块提交也允许垂直扩展。 + + + +## 附录 ### 分叉问责制 -如果遇到超过容错能力以及共识出错的情况下,设计周到的共识协议应该要对此提供一定的保障。这个在经济系统中尤为必要,经济系统中的拜占庭行为可以获取大量资金奖励。而针对上述情况有一个最为重要的保证就是分叉问责制,这个形式下,导致共识出错的进程(也就是导致协议客户端开始接受不同值——即分叉出现)可以被识别出并根据协议规定进行生发,或者也有可能受到法律制裁。当法律系统变得不可靠或者调用代价过大,那么验证人可能要强制支付安全保证金来参与,而一旦检测到恶意行为,那么这些保证金就会被吊销或者减少。 [\[10\]](10) +经过精心设计的共识协议应该能够在超出容错能力或者共识出错的情况下为系统提供一定的保障。这在能够通过拜占庭行为获取实质经济回报的金融系统中显得尤为必要。_分叉问责制_ 就属于这种非常重要的保障机制,这种制度会使一个导致共识出错的进程(例如使协议客户端开始接受不同值——即分叉)被识别出来,并且根据协议规则对其进行惩罚,甚至将其移送至司法系统处置。但当司法系统不可靠或者诉讼费极其昂贵时,为了让验证人们都参与到这个机制当中,该制度会强制要求他们建立安全保证金,一旦检测到恶意行为,那么这些保证金将会被罚没或者削减[\[10\]][10]. -注意这个和比特币有很大区别,由于网络同步及其发现哈希冲突的概率特性,比特币的分叉是定期出现的。在很多案例中,很难将恶意分叉与同步导致的分叉区分开来,所以比特币无法可靠地实施分叉问责制,除了让矿工为孤行区块挖矿支付隐形机会成本。 -### Tendermint 共识 -我们将投票阶段分为预投票及预提交阶段。一个选票可以用于特定区块或*Nil。*我们把同一轮超过 ⅔ 的单个区块的预投票总和称为*Polka*,把同一轮超过 ⅔ 的单个区块的预提交总和称为*Commit*。如果同一轮针对 Nil 的预提交超过 ⅔,那么它们就进入下一轮。 +注意这与比特币有所不同,由于网络非同步与局部哈希碰撞的概率特性,比特币的分叉是定期出现的。因为在很多情况下,恶意分叉与非同步引起的分叉是无法分辨的,除非让矿工为挖到的孤立区块支付隐性的机会成本,否则比特币就很难准确地执行分叉问责制。 -注意,协议中严格的决定论会招致较弱的同步假设,因为默认的首项必须被扣除且略过。因此,验证人在对 Nil 进行预投票前会等候一段时间(即*TimeoutPropose* ,超时提议),而*TimeoutPropose* 的值会随着每一轮的进行而增加。每一轮剩下的进程是完全同步的,在过程中只有验证人收到超过 ⅔ 的网络投票才会进入下一步。在实践中,这么做将需要超级强大的对手无限阻挠较弱的同步假设(导致共识无法提交区块),而且通过使用每个验证人的*TimeoutPropose* 随机值还可加大其难度。 +### Tendermint共识 -另一套约束,或者锁定规则会确保最终在每个高度只提交一个区块,任何试图提交超过一个区块到指定高度的恶意行为都会被识别出来。首先,每个区块的预提交必须正当,并且以 Polka 的形式提交。如果验证人已经在*R_1*轮预提交了一个区块,那么我们就认为它们被锁定在了这个区块,然后用于验证*R_2*轮新预提交动作的 Polka 必须进入 R_polka 轮,其中 `R_1 < R_polka <= R_2`。第二,验证人必须提出并且/或者预投票它们被锁定的区块。这两步合起来,就可以确保验证人不会在没有充足证据证明正当性的前提下进行预提交,并且保证已经完成预提交的验证人不能再为其他东西的预提交贡献证明。这样不但可以保证安全,还能保证共识算法的活跃。 -关于这一协议的全部细节参考 [这里](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm)。 +我们把投票阶段分为 _预投票_ 和 _预提交_ 两个阶段。一个投票可以是用于特定区块,也可以用于 _Nil_。我们把同一轮超过⅔的单个区块的预投票总和称为 _Polka_ ,把同一轮超过⅔的单个区块的预提交总和称为 _Commit_。如果在同一轮中对Nil的预提交超过⅔,他们就进入到下一轮中。 -### Tendermint 轻客戸端 -本来需要同步所有区块头,现在在 Tendermint-PoS 里取消了这一需求,因为我们有替代链(分叉),也就是说可以削减超过 ⅓ 的绑定权益。当然,削减也是需要有人共享分叉证据的,所以轻客戸端就要存储任何它见证的区块哈希提交。此外,轻客戸端可以定期同步验证组的变动,以避免出现 [远距离攻击](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#preventing-long-range-attacks)(除了其他可能实现的解决方案)。 +注意到协议中的严格的确定性会引发一个弱同步假设,因为错误的发起者必须被检测到并将其略过。验证人们在为Nil预投票之前会等待一段时间,称之为超时提议,这个超时提议的hi等待时间也会随着每一轮的进行而递增。每一轮的进行都是完全不同步的,在这个过程中,只有当验证人收听到超过⅔的网络投票才能进入到下一轮中。实际上,它需要极其强大的阻碍才能阻挠这个弱同步假设(导致无达成共识,无法提交区块),而且通过每个验证人超时提议(TimeoutPropose)的随机值还可更加提高这么做的难度。 -秉着和以太坊类似的想法,Tendermint 能够让应用程序在每个区块中嵌入全球梅克尔根哈希,可以进行简单且可验证的状态查询,比如查询账户余额、合约存放价值,或者未使用交易输入的存在等,具体由应用程序的特性决定。 +另一个附加的约束条件,或者叫锁定条例,它能确保网络最终在每个高度只提交一个区块。任何试图在给定高度提交超过一个区块的恶意行为都会被识别出来。首先,一个区块的预提交必须被认为是正当的,并且以Polka的形式提交。如果验证人已经准备在R_1轮中预提交一个区块,我们称他们锁定了这个区块,并且然后用于验证R_2轮新预提交动作的Polka必须进入R_polka轮,其中 R_1 < R_polka <= R_2。其次,验证人们必须为他们锁定的区块提议并且(或者)预投票。这两个条件共同作用,确保了验证人在对其正当性没有充分论证的情况下不能进行预提交操作,并且保证已经完成预提交的验证人不能再为其他东西的预提交贡献证明投票。这样不但可以保证共识算法的安全性,还能保证它的活跃度。 -### 预防远距离攻击 -假设有一套充足的可复原的广播网络集合以及一组静态验证组,那么任何区块链分叉都可以被检测到,而且发起攻击的验证人提交的保证金会被扣除。这一创新是由 Vitalik Buterin 于 2014 年首次提出的,可以解决其他权益证明加密货币"没有任何权益"的问题(详见 [相关工作](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#related-work)部分。)但是,由于验证组必须要可以更改,所以可能在很长一段时内最初的验证人可能会解除绑定,因此就可以自由从创世区块中创建新链,并且不需要任何费用,因为他们不再有锁定的保证金。这类攻击被称为远距离攻击(LRA),和短距离攻击相反,短距离攻击是当前处于绑定中的验证人导致的分叉,并且会因此受到惩罚(假设有类似 Tendermint 共识这样的分叉问责制拜占庭容错算法)。长距离攻击经常被认为是对权益证明机制的重要打击。 +关于这一协议的全部细节参考[这里](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm). -所幸的是,LRA 可以通过以下方法得以缓解。第一,针对解绑(来恢复抵押的保证金并且不再挣取参与共识的费用)的验证人,保证金在一定时间内必须是不可转移的,可以称作"解绑期",可能长达数周或数月。第二,针对轻客戸端的安全,其首次连接到网络必须根据可信源验证最近的一个或者最好是多个区块哈希。这种情况有时也被称作"薄弱主观性"。最后,为了保证安全,必须频繁同步最新验证组(每次间隔时间和解绑期一样)。这一点可以保证轻客戸端在验证人解绑资金(从而没有任何权益)前知道验证组的变化情况,否则解绑的验证人就会通过实施远距离攻击来欺骗客户端,在其绑定的高度创建新区块起始返回点(假设它可以控制足够多的早期私钥)。 +### Tendermint轻客户端 -注意,用这种方式解决 LRA 问题需要对工作量证明模型原始的安全问题进行彻底检查。在 PoW 中,轻客戸端被认为可以在任何时候从可靠的创世区块同步到当前高度,这个过程可以简单的在每个区块头上处理工作量证明来完成。但是,为了解决 LRA,我们需要轻客戸端上线定期追踪验证组的变动,并且其首次上线必须尤其要注意根据可靠源来验证其从网络收集的情报。当然,后面这个要求和比特币的类似,在比特币中,协议和软件也必须从可靠源获得。 +因为生成侧链(一个分叉)意味着至少⅓的担保权益被罚没,Tendermint权益证明(Tendermint-PoS)取消了同步所有区块头的要求。当然,罚没保证金也是需要有人共享分叉证据的,所以轻客戸端就要存储任何它见证的区块哈希提交。另外,轻客戸端可以定期地与验证人组的改变保持同步,以避免出现远程攻击(但是其他解决方案也具有可能性)。 -上述预防 LRA 的方法正好适合 Tendermint 驱动下的区块链验证人及全部节点,因为这些节点的任务就是保持连接到网络。这个方法也适合那些想要经常进行网络同步的轻客戸端。但是,对不希望频繁接入互联网或区块链网络的轻客戸端来说,还有另一种方法可以用来解决 LRA 问题。非验证人的代币持有者额可以在很长的解绑期内(比如比验证人的解绑期久)发布代币作为抵押品,并且为轻客戸端提供第二级解决方案来证实当前及过去区块哈希的有效性。就算这些节点没有计入区块链共识的安全性时,他们还是可以为轻客戸端提供强大的保障。如果历史区块哈希查询在以太坊中得到过支持,那么任何人都可以用特定的智能合约来绑定他们的代币,并且提供付费证明服务,从而有效地针对轻客戸端 LRA 安全问题开发出一个市场。 -### 克服分叉及审查攻击 +与以太坊类似,Tendermint能够让应用程序在每个区块中嵌入一个全局默克尔根的哈希值,可以简单方便的验证的状态查询,比如查询账户余额、在智能合约中的值,或者未使用交易输出(UTXO)的存在,具体由应用程序的特性决定。 -由于区块提交的定义如此,所以任何超过 ⅓ 的投票联合都可以通过下线或者不广播选票来中止区块链运行。这样的联合也可以通过拒绝包含这类交易的区块来检查特定交易,尽管这或将导致大多数区块提案被拒绝,从而减缓区块提交速率,降低实用性及价值。恶意联合或许会陆陆续续地广播选票,以阻挠区块链的区块提交,将其逼停,或者就是参与到这些攻击的组合攻击中。最后,它会通过双重签名或者违反锁定规则来造成区块链分叉。 +### 远程攻击的防御 -如果一个全球活跃的对手参与进来,那么就会使网络分割,导致验证组子集出现拉低速率。这不单单是 Tendermint 面临的限制,也是所有共识协议(其网络可能由活跃对手控制)的限制。 -对这几类攻击,验证人子集应该在外部进行协调,签署重组提议来选择分叉(及牵扯到的任何证据)以及验证人的初始子集。签署这份重组提议的验证人会放弃他们在其他分叉上的所有抵押品。客户端要验证重组提议上的签名,验证任何证据并且做出判断或者给端用户提示来做决定。比如,一个手机钱包 APP 可能会提示用户安全警告,而电冰箱可能会接受签署超过 ½ 的初始验证人提出的重组提议。 +假设有一个足够有弹性的广播网络采集与一组静态验证人组存在,那么任何区块链分叉都可以被检测到,而且发起攻击的验证人提交的保证金会被罚没。这个由Vitalik Buterin在2014年首次提出的新方法,解决了其他权益证明加密货币的"没有任何相关权益"问题(详见 [相关工作部分](#related-work))。但由于验证人组必须是能够变化的,在较长的时间段内最初的一些验证人会解除j押金绑定,这就使他们可以自由地从创世区块中创建新链,并且因为他们不再有被锁定的保证金,他们将不需要为这个行为支付任何费用。这类攻击被称为远程攻击(LRA),与短程攻击相比,后者对处在押金绑定中的验证人发起的分叉是可以对其进行惩罚的(假设有类似Tendermint共识这样的分叉问责制拜占庭容错算法)。所以远程攻击经常被认为是对权益证明机制的一种危险打击。 -任何非同步的拜占庭容错算反都不能进入共识,如果超过 ⅓ 的投票不诚实的话,而且出现分叉就说明已经有超过 ⅓ 的投票权因为无正当理由的双重签名或改锁而失信。因此,签署重组提议是一个协调性问题,无法通过任何非同步协议(即自动的,不对底层网络可靠性做假设的)来解决。目前,我们认为重组提议的协调问题,可以通过互联网媒体的社会共识来实现人为协调。验证人必须确保在签订重组提议前不会出现网络分割,以此来避免有两个相冲突的重组提议被强叔的情况出现。 -加入外部条件媒介以及协议足够稳健的话,那么分叉的问题就没有检查攻击问题来的严重。 +幸运的是,远程攻击(LRA)可以用以下的途径来缓解。第一,对于解除绑定的验证人而言(取回抵押保证金并且不再从参与共识中获取费用),保证金在一定时间内不能转移,也可以称其为"解绑周期",这个周期可能长达数周或数月。第二,对于轻客户端的安全性而言,其首次连接到网络时必须根据可信源验证最新的一个区块哈希或者多个最好的区块哈希。这种情况有时被称为"弱主观性"。最后,为了保证安全,必须与最新的验证人组进行频繁的同步,时长与解绑周期一致。这样就确保了轻客戸端在因验证人解绑资金而失去任何权益之前,知道验证人组的变化情况,否则解绑的验证人就会在其绑定的高度后面开始创建新区块来实施远程攻击,以此来欺骗客户端(假设它可以控制足够多的早期私钥)。 -分叉和检查需要有超过 ⅓ 的拜占庭投票权,此外超过 ⅔ 的投票权联合可能会恣意提交无效状态。这个是任何拜占庭容错共识系统的特点。和双重签名不同,双重签名会利用简单的可验证的证据来创建分叉,而要检测无效状态的提交则需要非验证对等节点来验证整个区块,也就是说它们会保留一份本地状态副本,执行每笔交易,然后独立计算状态根。一旦检测出来,处理这类故障的唯一方法就是社会共识。比如,如果比特币出现问题,那么无论是软件漏洞造成的分叉,还是矿工拜占庭行为提交的无效状态(正如 2015 年 7 月),连接紧密的商户、开发者、矿工以及其他组织组成的社区按照所需分工来参与修复网络。此外,因为 Tendermint 区块链的验证人或可进行身份认证,所以如果有这个想法,那么无效状态的提交也可能会受到法律或其他外部法律体系的惩罚。 +注意到用这样的方式来对抗远程攻击(LRA)需要对工作量证明(proof-of-work)的原始安全模块进行彻底检查。在工作量证明中(PoW),一个轻客户端可以通过在每一个区块头中运行工作量证明,以此简便地在任何时候与可信任的创始区块的当前高度进行同步。但是,为了对抗远程攻击(LRA),我们需要轻客戸端定期上线追踪验证人组的变动,其中在首次上线时必须格外仔细地根据可靠源来验证从网络采集的信息。诚然,后面这个要求和比特币类似,其协议和软件也必须从可靠源获得。 -### TMSP 具体说明 -TMSP(Tendermint Socket 协议)由三个主要信息类型组成,这三类信息从核心传递到应用程序上,然后应用程序用相关回复信息做出应答。 +以上这些为了防止远程攻击的方法,比较好地适用于由Tendermint驱动下的区块链验证人节点以及全节点,因为这些节点需要保持与网络的连接。这些方法同样适用于希望频繁地与网络同步的轻客户端。但是,对于那些不希望频繁接入互联网或者区块链网络的轻客户端来说,还有另一种方法可以解决远程攻击的问题。非验证人节点可以在很长的解绑期内(比如比验证人的解绑期更久)使用代币作为保证金,并且为轻客戸端提供二级证明当前有效性以及过去区块哈希的解决方案。虽然这些代币对于区块链共识的安全性并没有价值,不过他们还是可以为轻客戸端提供强大的保障。如果在以太坊中支持历史区块哈希查询,那么任何人都可以用特定的智能合约来绑定他们的代币,并且提供付费证明服务,从而有效地针对轻客戸端LRA安全问题开发出一个市场。 -AppendTx 信息是应用程序的主力。区块链上的每笔交易都通过这个信息来传递。应用程序需要借助 AppendTx 信息在当前状态、应用程序协议以及交易加密证书中进行验证,验证每笔接收的交易。验证过的交易之后需要更新应用状态——通过绑定一个值到关键价值存储库中,或者更新 UTXO 数据库。 +### 克服分叉与审查攻击 -CheckTx 信息和 AppendTx 信息类似,但是只针对交易验证。Tendermint Core 的内存池会先用 CheckTx 检查交易有效性,并且只会将有效的交易分程传递给对等节点。应用程序可能会检查交易中的递增新鲜值,如果新鲜值过期就会借助 CheckTx 返回一个错误。 +由于提交区块流程的定义,任何联合后不少于⅓的投票权的节点都可以通过下线或者不广播选票来中止区块链运行。这样的联合也可以通过拒绝包含这些交易的区块来审查特定的交易,尽管这将导致大多数区块提案被拒绝,致使区块提交速率减缓,降低了它的实用性与价值。恶意的联合或许仍然会陆陆续续地广播选票,用阻挠区块链的区块提交来将其逼停,或者使用任何这些攻击的组合攻击。最终,它会通过双重签名或者违反锁定规则来造成区块链分叉。 -Commit 信息是用来计算当前应用状态上的加密提交项的——之后会存入下一区块头。这包含一些方便的特性。状态更新的矛盾性将以区块链分叉的形式出现,从而捕捉整个阶段的程序错误。这也简化了安全轻客戸端的开发,因为梅克尔哈希证明可以通过检查区块哈希来加以验证,而区块哈希是通过验证人仲裁集加以签署的(通过投票权)。 +如果一个全球活跃的作恶者也参与进来,就会用可能出现错误的验证组人子集导致速度降低的方法来分割网络。这不只是Tendermint面临的局限性,更确切地说是所有被活跃敌对者控制了网络的共识协议所面临的局限性1。 -额外的 TMSP 信息允许应用程序追踪改变验证组,并让应用程序接收高度、提交的选票之类的区块信息。 -TMSP 的要求/回应是简单的 Protobuf 信息。请参考这里的 [模式文件](https://github.com/tendermint/abci/blob/master/types/types.proto)。 +对于这些类型的攻击,验证人的子集应该通过外部的方式进行协调,以签署选择一个分叉(及关系到的所有证据)与带有签名的验证人的初始子集的重组提案。签署了这样一份重组提案的验证者,将放弃在所有其他分叉上属于他们的保证金。客户端应在重组提案中验证签名以及任何相关的证据,并作出判断或提示终端用户作出决定。例如,一个手机钱包app应该在可能接受任何由一半以上的初始验证人们通过投票权利签署的重组提案时,给予用户安全警告提示。 -#### AppendTx(附加交易) +当多于⅓的投票权益是不诚实的时候,一个非同步的拜占庭容错算法步能够达成共识,然而,分叉假设不少于⅓的投票权益已经因不正当的双重签名或者锁定改变而成为不诚实的。因此,签署重组提案是一个协调问题,任何非同步协议都无法解决这个问题(也就是自动的,并且不考虑底层网络的可靠性)。目前,我们通过互联网媒体的社会共识,把重组提案的协调问题留给了用户去协调。验证人必须在签署重组提案之前就确保没有出现网络分割的问题,以此来避免签署两个相冲突的重组提议的情况发生。 -- **命令行参数** : - - Data (\[]byte): 所需交易的字节 -- **返回** : - - Code (uint32): 回复代码 - - Data (\[]byte): 结果字节,如果有的话 - - Log (string): 调试或出错信息 -- **使用** : - 附加并运行一笔交易。如果交易有效,那返回 CodeType.OK -#### CheckTx(检查交易) +假设外部协调媒介和协议是可靠的,对于分叉的担心会比审查攻击要少许多。 -- **命令行参数** : - - Data (\[]byte): 所需交易的字节 -- **返回** : - - Code (uint32): 回复代码 - - Data (\[]byte): 结果字节,如果有的话 - - Log (string): 调试或出错信息 -- **使用** : -验证一笔交易。这个信息不应该改变状态。交易在广播给内存池层对等节点前,首先通过 CheckTx 运行。你可以发起半状态化 CheckTx,并在 Commit or BeginBlock 上清算状态,以允许执行同一区块中相关的交易序列。 +除了需要大于⅓的拜占庭投票权益才能启动的分叉和审查制度以外,超过⅔的联合投票权益可能会提交任意、无效的状态。这是任何拜占庭容错算法的共识系统所特有的问题。与利用简单可验证证明来创建分叉的双重签名不同,检测无效状态的提交需要非验证节点来验证整个区块,这意味着非验证节点会保留一份本地的状态副本并执行每一笔交易,然后为他们自己独立计算出状态的根源。一旦检测出来,处理这类故障的唯一方法就是社会共识。打一个比方,在比特币出现问题的情况下,无论是由于软件漏洞造成的分叉(正如2013年3月),还是由于矿工拜占庭行为提交的无效状态(正如2015年7月),由商户、开发者、矿工和其他组织组成的联系紧密的社区所建立起来的社会共识会让他们按照分工来参与到修复网络的工作当中去。此外,由于Tendermint区块链的验证人身份是可识别的,那么如果需要的话,无效状态的提交实际上是可以被法律或其他外部法律体系惩治的。 -#### Commit(提交) +### ABCI说明 -- **返回** : - - Data (\[]byte): 梅克尔根哈希 - - Log (string): 调试或出错信息 -- **使用** : - 返回应用程序状态梅克尔根哈希。 -#### Query(查询) +ABCI由3种主要的信息类型组成,这三类信息从共识引擎传递到应用程序上,然后应用程序用相应回复信息做出应答。 -- **命令行参数** : - - Data (\[]byte): 查询需要的字节 -- **返回** : - - Code (uint32): 回复代码 - - Data (\[]byte): 查询回复字节 - - Log (string): 调试或出错信息 -#### Flush(划掉) +`AppendTx` 信息是应用程序的主要传递媒介。区块链中的每一笔交易都通过这个信息来传递。应用程序需要验证每笔交易,这将通过接收针对当前状态、应用协议和交易密码证书的AppendTx信息来实现。验证过的交易将需要通过添加数值到键值存储或者更新UTXO数据库的方式来更新应用状态。 -- **使用** : -划掉回复队列。使用 types.Application 的应用程序无需实施这条信息——这个由项目进行处理。 +`CheckTx`信息与AppendTx信息类似,但它只是为了交易验证。Tendermint Core的内存池会先用CheckTx验证交易有效性,并且只会将有效的交易传递给其他的节点。应用程序会检查交易序列号,如果序列号过期就会根据CheckTx返回一个错误。 -#### Info(信息) +`Commit`信息是用来计算之后会存入到下一区块头中的当前应用状态的加密提交项。这具有便利的特性。状态的前后矛盾性将会像引起程序错误,从而导致区块链分叉。这也简化了安全轻客戸端的开发,因为默克尔哈希证明可以通过检查区块哈希来加以验证,而区块哈希是由规定人数的验证人们签署的(通过投票权益)。 -- **返回** : - - Data (\[]byte): 信息的字节 -- **使用** : -返回关于应用程序状态的信息。Application specific. -#### SetOption(设置选项) +此外,ABCI信息允许应用程序保持追踪验证人组的改变,并让应用程序接收诸如高度和提交选票之类的区块信息。 -- **命令行参数** : - - Key (string): 设置密钥 - - Value (string): 密钥设置的值 -- **返回** : - - Log (string): 调试或出错信息 -- **使用** : - 设置应用选项。比如,针对内存池的连接可以将密钥设置为"mode"(模式),价值为"mempool"(内存池)。或者针对共识连接,将密钥设置为"mode",价值设置为"consensus"(共识)。其他选项根据可具体应用进行专门设置。 -#### InitChain(初始链) +ABCI请求/响应是简单的Protobuf信息。请参考这里的[模式文件](https://github.com/tendermint/abci/blob/master/types/types.proto)。 -- **命令行参数** : - - Validators (\[]Validator): 初始创世验证人 -- **使用** : - 在创世块生成后进行调用 +##### AppendTx + * __命令行参数__: + * `Data ([]byte)`: 交易请求信息 + * __Returns__: + * `Code (uint32)`: 回复代码 + * `Data ([]byte)`: 结果字节,如果有的话 + * `Log (string)`: 错误信息 + * __使用__: + 提交并执行一笔交易,如果交易有效,那返回CodeType.OK -#### BeginBlock(起始块) +##### CheckTx + * __命令行参数__: + * `Data ([]byte)`: 交易请求信息 + * __Returns__: + * `Code (uint32)`: 回复代码 + * `Data ([]byte)`: 结果字节,如果有的话 + * `Log (string)`: 错误信息 + * __使用__: + 验证一笔交易。这个信息不应该改变应用状态。交易在广播给其他节点前,首先通过CheckTx运行。你可以发起半状态化CheckTx,并在Commit or BeginBlock上清算状态,以允许序列执行同一区块中相关的交易。 + +##### Commit + * __返回值__: + * `Data ([]byte)`: 默克尔根值 + * `Log (string)`: 调试或出错信息 + * __使用__: + 返回当前应用状态。 -- **命令行参数** : - - Height (uint64): 区块刚开始的高度 -- **使用** : - 为新区块的开始提供信号。在任何附加交易(AppendTxs)前进行调用。 +##### Query + * __命令行参数__: + * `Data ([]byte)`: 请求数据 + * __返回值__: + * `Code (uint32)`: 回复代码 + * `Data ([]byte)`: 查询回复字节 + * `Log (string)`: 调试或出错信息 -#### EndBlock(结束区块) +##### Flush + * __使用__: + 刷新回复队列。应用types.Application的应用程序无需实施这条信息——这个由项目进行处理。 -- **命令行参数** : - - Height (uint64): 结束时的区块高度 -- **返回** : - - Validators (\[]Validator): 具有新选票的变动后的验证人(归零就去除) -- **使用** : - 为区块结束提供信号。在每次提交前所有交易后调用。 +##### Info + * __返回值__: + * `Data ([]byte)`: 信息字节串 + * __使用__: + 返回关于应用程序状态的信息. 应用指定。 -更多细节请参考 [TMSP 知识库](https://github.com/tendermint/abci)。 +##### SetOption + * __参数__: + * `Key (string)`: 设置参数 + * `Value (string)`: 参数值 + * __返回值__: + * `Log (string)`: Debug or error message + * __使用__: + 比如,针对内存池的连接可以将键设置为"mode"(模式),值为"mempool"(内存池)。或者针对共识连接,将键设置为"mode",值设置为"consensus"(共识)。其他选项根据可具体应用进行专门设置。 + +##### InitChain + * __参数__: + * `Validators ([]Validator)`: 初始化创世验证人 + * __使用__: + 在创世区块创建时调用 -### IBC 包交付确认 +##### BeginBlock + * __参数__: -发送人可能会需要接收链提供包交付确认,这个原因有很多。比如,发送人可能不了解目的链的状态,如果有问题的话也不得而知。或者,发送者可能会想要向包强加一次超时(借助 MaxHeight 即最大值包域),而目的链可能会遇到拒绝服务攻击,比如接受包数量突然暴增。 -在这些案例中,发送人可以通过在 AckPending 上设置初始包状态来要求提供交付确认。接着就由接收链通过在应用程序的梅克尔哈希中包括一个缩写的 IBCPacket 来确认交付。 + * * `Height (uint64)`: 区块刚开始的高度 + * __使用__: + 为新区块的开始提供信号。在附加交易(AppendTxs)前进行调用。 + +##### EndBlock + * __参数__: + * `Height (uint64)`: 结束时的区块高度 + * __返回值__: + * `Validators ([]Validator)`: 具有新选票的变动后的验证人(归零就去除) + * __使用__: + 为区块结束提供信号。在每次提交前所有交易后调用。 + +See [the ABCI repository](https://github.com/tendermint/abci#message-types) for more details + +更多细节请参考 [ABCI知识库](https://github.com/tendermint/abci#message-types)。 + +### IBC数据包交付确认 + + +发送者有很多需要接收链提供数据包交付确认的原因。比如,如果预计目的链会出错,那发送者就可能无法了解目的链的状态。或者,当目的链可能遇到因接收数据包猛烈增多而形成的拒绝服务攻击时,发送者会想要设定数据包超时时限(借助`MaxHeight` 即最大值包域)。 + + +在这些案例中,发送人可以通过在`AckPending`上设置初始数据包状态来要求提供交付确认。然后就由接收链通过包含一个简化的`IBCPacket`的应用默克尔哈希来确认交付。 ![Figure of Zone1, Zone2, and Hub IBC with acknowledgement](https://raw.githubusercontent.com/gnuclear/atom-whitepaper/master/msc/ibc_with_ack.png) -首先,IBCBlockCommit 以及 IBCPacketTx 是公布在"Hub"(中心)上用来证明"Zone1"(空间 1)上的 IBCPacket 的存在的。假如 IBCPacketTx 的值如下: -- FromChainID: "Zone1" -- FromBlockHeight: 100 (假如) -- Packet: an IBCPacket: - - Header: an IBCPacketHeader: - - SrcChainID: "Zone1" - - DstChainID: "Zone2" - - Number: 200 (假如) - - Status: AckPending - - Type: "coin" - - MaxHeight: 350 (假如"Hub"目前的高度是 300) - - Payload: <The bytes of a "coin" payload>(一个"代币"的有效负荷字节) +首先,一个`IBCBlockCommit`和`IBCPacketTx`是被上传到“枢纽”上用来证明"分区1"上的`IBCPacket`的存在的。假设`IBCPacketTx`的值如下: -第二步,IBCBlockCommit 和 IBCPacketTx 被公布到"Zone2"(空间 2)来证明 IBCPacket 存在于"Hub"上。假如 IBCPacketTx 的值如下: +- `FromChainID`: "Zone1" +- `FromBlockHeight`: 100 (假设) +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 (say) + - `Status`: `AckPending` + - `Type`: "coin" + - `MaxHeight`: 350 (假设 "枢纽" 当前高度为 300) + - `Payload`: <一个"代币"的有效负荷字节> -- FromChainID: "Hub" -- FromBlockHeight: 300 -- Packet: an IBCPacket: - - Header: an IBCPacketHeader: - - SrcChainID: "Zone1" - - DstChainID: "Zone2" - - Number: 200 - - Status: AckPending - - Type: "coin" - - MaxHeight: 350 - - Payload: <The same bytes of a "coin" payload>(一个"代币"相同的有效负荷字节) -接下来,"Zone2"必须将缩写的包放入其应用程序哈希中来显示 AckSent 的最新状态。 +- `FromChainID`: "Zone1" +- `FromBlockHeight`: 100 (假设) +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 (假设) + - `Status`: `AckPending` + - `Type`: "coin" + - `MaxHeight`: 350 (假设"枢纽"目前的高度是300) + - `Payload`: <一个"代币"的有效负荷字节> -IBCBlockCommitand 和 IBCPacketTx 会公布到"Hub"上来证明缩写的 IBCPacket 存在于"Zone2"上。假如 IBCPacketTx 的值如下: -- FromChainID: "Zone2" -- FromBlockHeight: 400 (say) -- Packet: an IBCPacket: - - Header: an IBCPacketHeader: - - SrcChainID: "Zone1" - - DstChainID: "Zone2" - - Number: 200 - - Status: AckSent - - Type: "coin" - - MaxHeight: 350 - - PayloadHash: <The hash bytes of the same "coin" payload>(相同"代币"有效负荷的哈希字节) +其次,一个`IBCBlockCommit` 和 `IBCPacketTx`被传输都“分区2”上用来证明`IBCPacket`在“枢纽”上的存在。假设`IBCPacketTx`的值如下: -最后,"Hub"必须更新从 AckPending 到 AckReceived 的包的状态。这个最新状态的证据要回到"Zone2"。假如 IBCPacketTx 的值如下: +- `FromChainID`: "Hub" +- `FromBlockHeight`: 300 +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckPending` + - `Type`: "coin" + - `MaxHeight`: 350 + - `Payload`: <一个"代币"相同的有效负荷字节> -- FromChainID: "Hub" -- FromBlockHeight: 301 -- Packet: an IBCPacket: - - Header: an IBCPacketHeader: - - SrcChainID: "Zone1" - - DstChainID: "Zone2" - - Number: 200 - - Status: AckReceived - - Type: "coin" - - MaxHeight: 350 - - PayloadHash: <The hash bytes of the same "coin" payload>(相同"代币"有效负荷的哈希字节) -同时,"Zone1"可能会积极地假设"代币"包的交付是成功的,除非"Hub"上有证据给出相反的证明。在上述例子中,如果"Hub"没有从"Zone2"接收到区块 350 的 AckSent 状态,那么它就会自动将这个设置到 Timeout(超时)。这个超时证据可以贴回到"Zone1"上,然后就可以返回任意代币。 +- `FromChainID`: "Hub" +- `FromBlockHeight`: 300 +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckPending` + - `Type`: "coin" + - `MaxHeight`: 350 + - `Payload`: <一个"代币"相同的有效负荷字节> + + +接下来,"Zone2"必须将缩写的来显示`AckSent`的最新状态包添加到应用程序状态哈希中。 +`IBCBlockCommitand` 和`IBCPacketTx` 会传输到“枢纽"上来证明简化的`IBCPacket` 存在于"分区2"上。假设`IBCPacketTx` 的值如下: + +- `FromChainID`: "Zone2" +- `FromBlockHeight`: 400 (假设) +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckSent` + - `Type`: "coin" + - `MaxHeight`: 350 + - `PayloadHash`: <一个"代币"相同的有效负荷字节的哈希值> + + +- `FromChainID`: "Zone2" +- `FromBlockHeight`: 400 (假设) +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckSent` + - `Type`: "coin" + - `MaxHeight`: 350 + - `PayloadHash`: <一个"代币"相同的有效负荷字节的哈希值> + + + +最后,“枢纽”必须更新从`AckPending` 到`AckReceived`的数据包状态。这个新完成状态的证明应该返回到“分区2”上。假设`IBCPacketTx`的值如下: + +- `FromChainID`: "Hub" +- `FromBlockHeight`: 301 +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckReceived` + - `Type`: "coin" + - `MaxHeight`: 350 + - `PayloadHash`: <The hash bytes of the same "coin" payload> + + +- `FromChainID`: "Hub" +- `FromBlockHeight`: 301 +- `Packet`: an `IBCPacket`: + - `Header`: an `IBCPacketHeader`: + - `SrcChainID`: "Zone1" + - `DstChainID`: "Zone2" + - `Number`: 200 + - `Status`: `AckReceived` + - `Type`: "coin" + - `MaxHeight`: 350 + - `PayloadHash`: <相同"代币"有效负荷的哈希字节> + + + +与此同时,“分区1”会假设“代币”包的交付已经成功,除非"枢纽"上有证据给出相反的证明。在上述例子中,如果"枢纽"没有从"分区2"接收到第350个区块的`AckSent` 状态,那么它就会自动将其设置为`Timeout`(超时)。这个超时的证据可以贴回到"Zone1"上,然后所有代币都会被返还。 ![Figure of Zone1, Zone2, and Hub IBC with acknowledgement and timeout](https://raw.githubusercontent.com/gnuclear/atom-whitepaper/master/msc/ibc_with_ack_timeout.png) -### 梅克尔树及梅克尔证明说明 +### 默克尔树及默克尔证明的说明 -Tendermint/Cosmos 生态支持的梅克尔树有两种:简单的和 IAVL+的。 +Tendermint/Cosmos生态支持的两种默克尔树:简单树和IAVL+树。 -#### 简易版梅克尔树 +#### 简易版默克尔树 -简易版梅克尔树针对的元素的静态列表。如果项的数目不是 2 的次方,那么有些树叶就会在不同的层。简易树试图让树的两侧在同一高度,但是左边可能会稍大一点。这种梅克尔树就是用于一个区块交易的梅克尔化的,而顶层元素就是应用状态根。 - * - / \ - / \ - / \ - / \ - * * - / \ / \ - / \ / \ - / \ / \ - * * * h6 - / \ / \ / \ - h0 h1 h2 h3 h4 h5 +简易版默克尔树针对基础的静态列表。如果项目的数量不是2的次方,那么有些树叶就会在不同的层上。简易树试图让树的两侧在同一高度,但是左边可能会稍大一点。这种默克尔树就是用于一个区块交易的默克尔化的,而顶层元素就是应用状态的根。 - A SimpleTree with 7 elements +``` + * + / \ + / \ + / \ + / \ + * * + / \ / \ + / \ / \ + / \ / \ + * * * h6 + / \ / \ / \ + h0 h1 h2 h3 h4 h5 -#### IAVL+梅克尔树 + A SimpleTree with 7 elements +``` -IAVL+数据结构的目的是永久储存应用状态中的密钥-值,这样具有决定功能的梅克尔根哈希就可以有效进行运算了。这个树的平衡通过 [AVL 算法](http://en.wikipedia.org/wiki/AVL_tree)的一个变量来达到,所有运行都是 O(log(n))。 +#### IAVL+树 -在 AVL 树中,任意节点两个子树的高度至少有一处不同。无论什么时候出现更新来打破这种情况,这个树都会通过创造 O(log(n))新节点(指向旧树上未修改的节点)来再次达到平衡。在初始 AVL 算法中,内部节点也可以维持密钥-值。AVL+算法(注意这里有个"+"号)对 AVL 算法进行了修改,来维持所有树叶节点上的值,同时还只需采用分支-节点来存储密钥。这一点在简化算法的同时,还能维持较短的梅克尔哈希轨迹。 +IAVL+数据结构的目的是永久储存应用状态中的密钥对,这样就可以对确定的默克尔根哈希进行高效的运算。这个树的平衡通过 AVL算法的变体来实现,所有运行都是O(log(n))。 -AVL+树类似于以太坊的 [Patricia tries](https://en.wikipedia.org/wiki/Radix_tree)(帕氏树)。其中也有一定的折中。密钥不需要在嵌入到 IAVL+树前生成哈希,而这个就为密钥空间里提供了较快的命令迭代,或许为很多应用程序都带去了好处。逻辑实现很简单,只需要两种节点——内部节点和树叶节点。作为一个平衡的二进制树,梅克尔证明算是短的。而另一方面,IAVL+树有取决于命令的更新。 +在AVL树中,任意节点的两个子树的高度至多有一处不同。无论在什么时候这种情况都是与更新相违背的,这个树都会通过创造O(log(n))新节点(指向旧树上未修改的节点)来再次达到平衡。在初始的AVL算法中,内部节点也可以保留密钥值对。AVL+算法(注意这里有个"+"号)对AVL算法进行了修改,来维持所有数值都在树叶节点上,同时还只需采用分支-节点来存储密钥。这样在维持较短的默克尔哈希轨迹对的同时,还简化了算法。 + +AVL+树类似于以太坊的[帕氏树](http://en.wikipedia.org/wiki/Radix_tree)。其中也有一定的折中。密钥不需要在嵌入到IAVL+树之前生成哈希,所以这就为密钥空间提供了较快的命令迭代,这会为很多应用程序带来好处。逻辑实现更简单,只需要内部节点和树叶节点这两种节点类型。作为一个平衡的二叉树,其默克尔证明平均更短。而另一方面,IAVL+树的默克尔根有取决于命令的更新。 +我们将支持额外有效的默克尔树,比如当二元变量可用时的以太坊帕氏树。 -我们将支持额外有效的梅克尔树,比如以太坊的帕氏树,同时实现二进制变量的可用性。 ### 交易类型 -在标准实现中,交易通过 TMSP 界面流入 Cosmos Hub 的应用程序。 -Cosmos Hub 将接受几类主要交易,包括 SendTx,BondTx,UnbondTx,ReportHackTx,SlashTx,BurnAtomTx,ProposalCreateTx,以及 ProposalVoteTx(即发送交易、绑定交易、解绑交易、攻击报告交易、削减交易、Atom 燃烧交易,创建提议交易),这些都不需要加以寿命,会在之后文章更新中进行归档。这里我们主要列举两个主要的 IBC 交易类型:IBCBlockCommitTx 以及 IBCPacketTx(即 IBC 区块提交交易以及 IBC 包交易) +在标准的执行中,交易通过ABCI界面涌入Cosmos Hub的应用程序中。 + +Cosmos Hub将接收几类主要的交易类型,包括`SendTx`, `BondTx`, `UnbondTx`, `ReportHackTx`, `SlashTx`, `BurnAtomTx`, +`ProposalCreateTx`,以及`ProposalVoteTx`(发送交易、绑定交易、解绑交易、攻击报告交易、削减交易、Atom燃烧交易,创建提案交易、以及提案投票交易),这些都不需要加以说明,会在未来版本的文档中加以备案。这里我们主要列举两个主要的IBC交易类型: `IBCBlockCommitTx` 以及`IBCPacketTx`(即IBC区块提交交易以及IBC数据包交易) + +#### IBCBlockCommitTx -#### IBCBlockCommitTx(IBC 区块提交交易) IBCBlockCommitTx 交易主要由这些组成: -- ChainID (string): 区块链 ID -- BlockHash (\[]byte): 区块哈希字节,就是梅克尔根(包括应用程序哈希) -- BlockPartsHeader (PartSetHeader): 区块部分设置的头字节,只用于验证投票签名 -- BlockHeight (int): 提交高度 -- BlockRound (int): 提交回合 -- Commit (\[]Vote): 超过 ⅔ 的 Tendermint 预提交投票,以组成区块提交项 -- ValidatorsHash (\[]byte): 新验证组的梅克尔树根哈希 -- ValidatorsHashProof (SimpleProof): 简易版梅克尔树证明,在区块哈希中证明验证人哈希 -- AppHash (\[]byte): IAVL 树,应用程序状态的梅克尔树根哈希 -- AppHashProof (SimpleProof): 简易版梅克尔树证明,在区块哈希中验证应用程序哈希 +- `ChainID (string)`: 区块链ID +- `BlockHash ([]byte)`: 区块哈希字节,就是包括应用程序哈希默克尔根 +- `BlockPartsHeader (PartSetHeader)`: 区块部分设置的头字节,只用于验证投票签名 +- `BlockHeight (int)`: 提交高度 +- `BlockRound (int)`: 提交回合 +- `Commit ([]Vote)`: 超过⅔的包括区块提交的Tendermint预提交投票 +- `ValidatorsHash ([]byte)`: 新验证组的默克尔树根哈希 +- `ValidatorsHashProof (SimpleProof)`: 在区块哈希中证明验证人哈希的简易树默克尔证明 +- `AppHash ([]byte)`: IAVL树,应用程序状态的默克尔树根哈希 +- `AppHashProof (SimpleProof)`: 在区块哈希中验证应用程序哈希的简易版默克尔树证明`AppHash` against the `BlockHash` -#### IBCPacketTx(IBC 包交易) +#### IBCPacketTx -IBCPacket 由下列项组成: -- Header (IBCPacketHeader): 包头 -- Payload (\[]byte): 包有效负荷字节。_可选择_。 -- PayloadHash (\[]byte): 包字节哈希。_可选择。_ +`IBCPacket` 由下列项组成: -有效负荷或有效负荷哈希必须存在一个。IBCPacket 的哈希就是两个项的简易版梅克尔根,即头和有效负荷。没有完整有效负荷的 IBCPacket 被称作缩写版包。 +- `Header (IBCPacketHeader)`: 数据包头 +- `Payload ([]byte)`: 数据包有效负荷字节。可选择。 +- `PayloadHash ([]byte)`: 数据包字节哈希。可选择。 -IBCPacketHeader 由下列项组成: -- SrcChainID (string): 源区块链 ID -- DstChainID (string): 目标区块链 ID -- Number (int): 所有包特定数量 -- Status (enum): 可以是 AckPending,AckSent,AckReceived,NoAck,或 Timeout 任意一个 -- Type (string): 种类根据应用程序决定。Cosmos 保留"coin"(币)包种类。 -- MaxHeight (int): 如果状态不是这个高度给出的 NoAckWanted 或者 AckReceived ,那么状态就算超时。_可选择。_ -IBCPacketTx 交易有下列项组成: +有效负荷`Payload`或有效负荷哈希`PayloadHash`必须存在一个。`IBCPacket` 的哈希就是两个项的简易版默克尔根,即头和有效负荷。没有完整有效负荷的`IBCPacket` 被称作 _缩写版包_ 。 -- FromChainID (string): 区块链 ID,用于提供这个包,不是必要的来源 -- FromBlockHeight (int): 区块链高度,其中接下来的包会包含在源链的区块哈希中 -- Packet (IBCPacket): 数据包,其状态可以是 AckPending,AckSent,AckReceived,NoAck,或 Timeout 任意一个 -- PacketProof (IAVLProof): IAVL 树梅克尔证明,用于一定高度的源链中的应用哈希中验证包的哈希 -通过"Hub",从"Zone1"发送到"Zone2"的包的序列会用{Figure X}函数进行描述。首先一次 IBCPacketTx 会向"Hub"证明包是包含在"Zone1"的应用程序状态中。然后,另一次 IBCPacketTx 会向"Zone2"证明包包含在"Hub"的应用程序状态中。在这个过程中,IBCPacketTx 的域是一样的:SrcChainID 永远是"Zone1"而 DstChainID 永远是"Zone2"。 -PacketProof 必须有正确的梅克尔证明路径,如下: +`IBCPacketHeader`由下列项组成: - IBC/// +`SrcChainID (string)`: 源区块链 ID +DstChainID (string)`: 目标区块链ID +Number (int)`: 所有数据包的唯一数字 +Status (enum)`:可以是AckPending,AckSent,AckReceived,NoAck,或Timeout任意一个 +Type (string)`: 种类根据应用程序决定。Cosmos保留"coin"(币)包种类。 +`MaxHeight (int)`: 如果状态不是这个高度给出的`NoAckWanted` 或者`AckReceived` ,那么状态就算超时。可选择。 -当"Zone1"想要向"Zone2"通过"Hub"发送证明,那么 IBCPacket 的数据是相同的,无论这个包是在"Zone1"、 "Hub"还是"Zone2"上梅克尔化的。唯一可变的域只有追踪交付的 Status (状态),如下所示。 +An `IBCPacketTx` transaction is composed of: -## 鸣谢 +- `FromChainID (string)`: The ID of the blockchain which is providing this + packet; not necessarily the source +- `FromBlockHeight (int)`: The blockchain height in which the following packet + is included (Merkle-ized) in the block-hash of the source chain +- `Packet (IBCPacket)`: A packet of data, whose status may be one of + `AckPending`, `AckSent`, `AckReceived`, `NoAck`, or `Timeout` +- `PacketProof (IAVLProof)`: A IAVLTree Merkle-proof for proving the packet's + hash against the `AppHash` of the source chain at given height -感谢朋友及同行在概念成型与检查方面提供的帮助,以及对我们同 Tendermint 及 Cosmos 工作的支持。 +`IBCPacketTx` 交易有下列项组成: +- `FromChainID (string)`: 提供给这个数据包的区块链ID,不是源所必须的 +- `FromBlockHeight (int)`: 区块链高度,其中接下来的包会包含在(默克尔化的)源链的区块哈希中 +- `Packet (IBCPacket)`:数据包, 其状态可能是`AckPending`, `AckSent`, `AckReceived`, `NoAck`, 或者 `Timeout`其中的一个 +- `PacketProof (IAVLProof)`: IAVL树默克尔证明,用于验证在给定高度下的源链应用哈希中的数据包哈希 -- [SkuChain](http://www.skuchain.com/)的 [Zaki Manian](https://github.com/zmanian)在 格式和措辞方面提供了很多支持,尤其是 TMSP 部分。 -- Althea and Dustin Byington 的 [Jehan Trembac](https://github.com/jtremback) [k](https://github.com/jtremback) 在初始迭代方面帮助。 -- [Honey Badger](https://eprint.iacr.org/2016/199)的 [Andrew Miller](https://soc1024.com/) 对共识部分给予的反馈。 -- [Greg Slepak](https://fixingtao.com/)对共识及措辞给予的反馈。 -- 同时还要感谢 [Bill Gleim](https://github.com/gleim)和 [Seunghwan Han](http://www.seunghwanhan.com/)的各种支持与贡献。 -- \*\*此处还有您及您的组织对本文的贡献。 -## 引用 +通过"Hub",将数据包从"Zone1"发送到"Zone2"的序列,描述在{Figure X}函数中。首先,一个`IBCPacketTx`会向"Hub"证明数据包是包含在"Zone1"的应用程序状态中。然后,另一个`IBCPacketTx` 会向"Zone2"证明数据包包含在"Hub"的应用程序状态中。在这个过程中,`IBCPacketTx` 的字段是相同的:`SrcChainID`永远是"Zone1",而`DstChainID` 永远是"Zone2"。 -- [1] Bitcoin: -- [2] ZeroCash: -- [3] Ethereum: -- [4] TheDAO: -- [5] Segregated Witness: -- [6] BitcoinNG: -- [7] Lightning Network: -- [8] Tendermint: -- [9] FLP Impossibility: -- [10] Slasher: -- [11] PBFT: -- [12] BitShares: -- [13] Stellar: -- [14] Interledger: -- [15] Sidechains: -- [16] Casper: -- [17] TMSP: -- [18] Ethereum Sharding: -- [19] LibSwift: -- [20] DLS: -- [21] Thin Client Security: -- [22] Ethereum 2.0 Mauve Paper: +The `PacketProof` must have the correct Merkle-proof path, as follows: + +`PacketProof` 必须有正确的默克尔证明路径,如下: + +``` +IBC/// + +``` + + +当“Zone1”要通过“Hub”将数据包传送到“Zone2”中,无论数据包是否在“Zone1”、“Hub”、或者“Zone2”中默克尔化了,`IBCPacket`数据都是相同的。唯一易变的字段是为追踪交付的`Status`。 + +## 鸣谢 ############################################################ + +我们为所有朋友欲同行们在概念成型与检查方面给予的帮助,以及对我们在Tendermint与Cosmos工作中的大力支持,表示衷心地感谢。 + +* [Zaki Manian](https://github.com/zmanian) of + [SkuChain](https://www.skuchain.com/) provided much help in formatting and +wording, especially under the ABCI section +* [Jehan Tremback](https://github.com/jtremback) of Althea and Dustin Byington + for helping with initial iterations +* [Andrew Miller](http://soc1024.com/) of [Honey + Badger](https://eprint.iacr.org/2016/199) for feedback on consensus +* [Greg Slepak](https://fixingtao.com/) for feedback on consensus and wording +* Also thanks to [Bill Gleim](https://github.com/gleim) and [Seunghwan + Han](http://www.seunghwanhan.com) for various contributions. +* __Your name and organization here for your contribution__ + +* [SkuChain](https://www.skuchain.com/)的[Zaki Manian](https://github.com/zmanian)在格式与措辞方面提供了很多帮助,尤其在ABCI部分。 +* Althea的[Jehan Tremback](https://github.com/jtremback)和Dustin Byington在初始迭代方面的帮助。 +* [Honey + Badger](https://eprint.iacr.org/2016/199)的 [Andrew Miller](http://soc1024.com/) 在共识部分给予的反馈。 +* [Greg Slepak](https://fixingtao.com/)对共识部分的反馈以及在措辞方面的帮助。 +* 同时还要感谢 [Bill Gleim](https://github.com/gleim)和 [Seunghwan + Han](http://www.seunghwanhan.com)在多方面的支持与贡献。 +* **此处还有您及您的组织对本文的贡献。 + +## 引用 + +[1]: https://bitcoin.org/bitcoin.pdf +[2]: http://zerocash-project.org/paper +[3]: https://github.com/ethereum/wiki/wiki/White-Paper +[4]: https://download.slock.it/public/DAO/WhitePaper.pdf +[5]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki +[6]: https://arxiv.org/pdf/1510.02037v2.pdf +[7]: https://lightning.network/lightning-network-paper-DRAFT-0.5.pdf +[8]: https://github.com/tendermint/tendermint/wiki +[9]: https://groups.csail.mit.edu/tds/papers/Lynch/jacm85.pdf +[10]: https://blog.ethereum.org/2014/01/15/slasher-a-punitive-proof-of-stake-algorithm/ +[11]: http://pmg.csail.mit.edu/papers/osdi99.pdf +[12]: https://bitshares.org/technology/delegated-proof-of-stake-consensus/ +[13]: https://www.stellar.org/papers/stellar-consensus-protocol.pdf +[14]: https://interledger.org/rfcs/0001-interledger-architecture/ +[15]: https://blockstream.com/sidechains.pdf +[16]: https://blog.ethereum.org/2015/08/01/introducing-casper-friendly-ghost/ +[17]: https://github.com/tendermint/abci +[18]: https://github.com/ethereum/EIPs/issues/53 +[19]: http://www.ds.ewi.tudelft.nl/fileadmin/pds/papers/PerformanceAnalysisOfLibswift.pdf +[20]: http://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf +[21]: https://en.bitcoin.it/wiki/Thin_Client_Security +[22]: http://vitalik.ca/files/mauve_paper.html + +* [1] Bitcoin: https://bitcoin.org/bitcoin.pdf +* [2] ZeroCash: http://zerocash-project.org/paper +* [3] Ethereum: https://github.com/ethereum/wiki/wiki/White-Paper +* [4] TheDAO: https://download.slock.it/public/DAO/WhitePaper.pdf +* [5] Segregated Witness: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki +* [6] BitcoinNG: https://arxiv.org/pdf/1510.02037v2.pdf +* [7] Lightning Network: https://lightning.network/lightning-network-paper-DRAFT-0.5.pdf +* [8] Tendermint: https://github.com/tendermint/tendermint/wiki +* [9] FLP Impossibility: https://groups.csail.mit.edu/tds/papers/Lynch/jacm85.pdf +* [10] Slasher: https://blog.ethereum.org/2014/01/15/slasher-a-punitive-proof-of-stake-algorithm/ +* [11] PBFT: http://pmg.csail.mit.edu/papers/osdi99.pdf +* [12] BitShares: https://bitshares.org/technology/delegated-proof-of-stake-consensus/ +* [13] Stellar: https://www.stellar.org/papers/stellar-consensus-protocol.pdf +* [14] Interledger: https://interledger.org/rfcs/0001-interledger-architecture/ +* [15] Sidechains: https://blockstream.com/sidechains.pdf +* [16] Casper: https://blog.ethereum.org/2015/08/01/introducing-casper-friendly-ghost/ +* [17] ABCI: https://github.com/tendermint/abci +* [18] Ethereum Sharding: https://github.com/ethereum/EIPs/issues/53 +* [19] LibSwift: http://www.ds.ewi.tudelft.nl/fileadmin/pds/papers/PerformanceAnalysisOfLibswift.pdf +* [20] DLS: http://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf +* [21] Thin Client Security: https://en.bitcoin.it/wiki/Thin_Client_Security +* [22] Ethereum 2.0 Mauve Paper: http://vitalik.ca/files/mauve_paper.html #### 未分类链接 -- +* https://www.docdroid.net/ec7xGzs/314477721-ethereum-platform-review-opportunities-and-challenges-for-private-and-consortium-blockchains.pdf.html From 2263cea118d4983a30f36c962603130f69ee72b6 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Sun, 16 Sep 2018 21:15:06 -0700 Subject: [PATCH 08/12] Merge PR #2345: update doc.go for mock/simulation --- x/mock/doc.go | 13 +------------ x/mock/simulation/doc.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 x/mock/simulation/doc.go diff --git a/x/mock/doc.go b/x/mock/doc.go index d23aac3936..139cd87a5e 100644 --- a/x/mock/doc.go +++ b/x/mock/doc.go @@ -1,15 +1,4 @@ /* -Package mock provides functions for creating applications for testing. - -This module also features randomized testing, so that various modules can test -that their operations are interoperable. - -The intended method of using this randomized testing framework is that every -module provides TestAndRunTx methods for each of its desired methods of fuzzing -its own txs, and it also provides the invariants that it assumes to be true. -You then pick and choose from these tx types and invariants. To pick and choose -these, you first build a mock app with the correct keepers. Then you call the -app.RandomizedTesting method with the set of desired txs, invariants, along -with the setups each module requires. +Package mock provides utility methods to ease writing tests. */ package mock diff --git a/x/mock/simulation/doc.go b/x/mock/simulation/doc.go new file mode 100644 index 0000000000..8b9a3f6932 --- /dev/null +++ b/x/mock/simulation/doc.go @@ -0,0 +1,27 @@ +/* +Package simulation implements a simulation framework for any state machine +built on the SDK which utilizes auth. + +It is primarily intended for fuzz testing the integration of modules. +It will test that the provided operations are interoperable, +and that the desired invariants hold. +It can additionally be used to detect what the performance benchmarks in the +system are, by using benchmarking mode and cpu / mem profiling. +If it detects a failure, it provides the entire log of what was ran, + +The simulator takes as input: a random seed, the set of operations to run, +the invariants to test, and additional parameters to configure how long to run, +and misc. parameters that affect simulation speed. + +It is intended that every module provides a list of Operations which will randomly +create and run a message / tx in a manner that is interesting to fuzz, and verify that +the state transition was executed as expected. +Each module should additionally provide methods to assert that the desired invariants hold. + +Then to perform a randomized simulation, select the set of desired operations, +the weightings for each, the invariants you want to test, and how long to run it for. +Then run simulation.Simulate! +The simulator will handle things like ensuring that validators periodically double signing, +or go offline. +*/ +package simulation From a3df7af4281074c5cc410c541288ac150a913992 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 17 Sep 2018 22:13:11 +0800 Subject: [PATCH 09/12] Merge PR #2346: bugfix in validatorset query and refactor transaction search --- client/rpc/block.go | 2 +- client/rpc/validators.go | 2 +- client/tx/search.go | 4 ++-- x/stake/client/rest/utils.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/rpc/block.go b/client/rpc/block.go index 3b1545fc7a..3824bc3e5c 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -43,7 +43,7 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { } if !cliCtx.TrustNode { - check, err := cliCtx.Certify(*height) + check, err := cliCtx.Certify(res.Block.Height) if err != nil { return nil, err } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index b034704d48..f708cb1456 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -73,7 +73,7 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { } if !cliCtx.TrustNode { - check, err := cliCtx.Certify(*height) + check, err := cliCtx.Certify(validatorsRes.BlockHeight) if err != nil { return nil, err } diff --git a/client/tx/search.go b/client/tx/search.go index 90c0ca168a..5bc8726db1 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -102,7 +102,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]In } } - info, err := FormatTxResults(cdc, cliCtx, res.Txs) + info, err := FormatTxResults(cdc, res.Txs) if err != nil { return nil, err } @@ -111,7 +111,7 @@ func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]In } // parse the indexed txs into an array of Info -func FormatTxResults(cdc *codec.Codec, cliCtx context.CLIContext, res []*ctypes.ResultTx) ([]Info, error) { +func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) { var err error out := make([]Info, len(res)) for i := range res { diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index a2266c36c5..8ab5655009 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -40,5 +40,5 @@ func queryTxs(node rpcclient.Client, cliCtx context.CLIContext, cdc *codec.Codec } } - return tx.FormatTxResults(cdc, cliCtx, res.Txs) + return tx.FormatTxResults(cdc, res.Txs) } From 65137f633177f87946d03236a5eb3851fc62507e Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 17 Sep 2018 07:34:06 -0700 Subject: [PATCH 10/12] Merge PR #2343: Add a name field to the message type This is to facillitate ease of implementing #1406. (Tags for messages could then be added dynamically) Ultimately once we make the router support hiearchical routing, (#770) we can then remove the name field and just the parse info for tags from that. Until then, we can parse the tag name as `fmt.Sprintf("%s %s", msg.Type(), msg.Name())` --- PENDING.md | 1 + baseapp/baseapp_test.go | 2 ++ docs/sdk/core/examples/app1.go | 1 + docs/sdk/core/examples/app2.go | 1 + examples/democoin/x/cool/types.go | 2 ++ examples/democoin/x/oracle/oracle_test.go | 3 +++ examples/democoin/x/oracle/types.go | 1 + examples/democoin/x/pow/types.go | 1 + examples/democoin/x/simplestake/msgs.go | 2 ++ examples/kvstore/tx.go | 4 ++++ server/mock/tx.go | 4 ++++ types/tx_msg.go | 5 +++++ x/bank/msgs.go | 4 ++++ x/gov/msgs.go | 7 +++++++ x/ibc/types.go | 2 ++ x/mock/app_test.go | 1 + x/slashing/msg.go | 1 + x/stake/types/msg.go | 9 ++++++++- 18 files changed, 50 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index f94d1653e8..1344344118 100644 --- a/PENDING.md +++ b/PENDING.md @@ -46,6 +46,7 @@ BREAKING CHANGES * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) * [store] Change storeInfo within the root multistore to use tmhash instead of ripemd160 \#2308 * [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New(). + * [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging. * Tendermint diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 8ce69f0a1b..f6042bd086 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -302,6 +302,7 @@ type msgCounter struct { // Implements Msg func (msg msgCounter) Type() string { return typeMsgCounter } +func (msg msgCounter) Name() string { return "counter1" } func (msg msgCounter) GetSignBytes() []byte { return nil } func (msg msgCounter) GetSigners() []sdk.AccAddress { return nil } func (msg msgCounter) ValidateBasic() sdk.Error { @@ -340,6 +341,7 @@ type msgCounter2 struct { // Implements Msg func (msg msgCounter2) Type() string { return typeMsgCounter2 } +func (msg msgCounter2) Name() string { return "counter2" } func (msg msgCounter2) GetSignBytes() []byte { return nil } func (msg msgCounter2) GetSigners() []sdk.AccAddress { return nil } func (msg msgCounter2) ValidateBasic() sdk.Error { diff --git a/docs/sdk/core/examples/app1.go b/docs/sdk/core/examples/app1.go index ba33c61201..1379834552 100644 --- a/docs/sdk/core/examples/app1.go +++ b/docs/sdk/core/examples/app1.go @@ -57,6 +57,7 @@ func NewMsgSend(from, to sdk.AccAddress, amt sdk.Coins) MsgSend { // Implements Msg. func (msg MsgSend) Type() string { return "send" } +func (msg MsgSend) Name() string { return "send" } // Implements Msg. Ensure the addresses are good and the // amount is positive. diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index 837c9f44fa..3ff9d1dea3 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -77,6 +77,7 @@ type MsgIssue struct { // Implements Msg. func (msg MsgIssue) Type() string { return "issue" } +func (msg MsgIssue) Name() string { return "issue" } // Implements Msg. Ensures addresses are valid and Coin is positive func (msg MsgIssue) ValidateBasic() sdk.Error { diff --git a/examples/democoin/x/cool/types.go b/examples/democoin/x/cool/types.go index d335f9c913..f04811b14a 100644 --- a/examples/democoin/x/cool/types.go +++ b/examples/democoin/x/cool/types.go @@ -33,6 +33,7 @@ var _ sdk.Msg = MsgSetTrend{} // nolint func (msg MsgSetTrend) Type() string { return "cool" } +func (msg MsgSetTrend) Name() string { return "set_trend" } func (msg MsgSetTrend) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } func (msg MsgSetTrend) String() string { return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool) @@ -83,6 +84,7 @@ var _ sdk.Msg = MsgQuiz{} // nolint func (msg MsgQuiz) Type() string { return "cool" } +func (msg MsgQuiz) Name() string { return "quiz" } func (msg MsgQuiz) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } func (msg MsgQuiz) String() string { return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer) diff --git a/examples/democoin/x/oracle/oracle_test.go b/examples/democoin/x/oracle/oracle_test.go index 416d933f2d..0b921e9d95 100644 --- a/examples/democoin/x/oracle/oracle_test.go +++ b/examples/democoin/x/oracle/oracle_test.go @@ -34,6 +34,9 @@ type seqOracle struct { func (o seqOracle) Type() string { return "seq" } +func (o seqOracle) Name() string { + return "seq" +} func (o seqOracle) ValidateBasic() sdk.Error { return nil diff --git a/examples/democoin/x/oracle/types.go b/examples/democoin/x/oracle/types.go index ab4b04a425..5e47597b80 100644 --- a/examples/democoin/x/oracle/types.go +++ b/examples/democoin/x/oracle/types.go @@ -29,5 +29,6 @@ func (msg Msg) GetSigners() []sdk.AccAddress { // Payload defines inner data for actual execution type Payload interface { Type() string + Name() string ValidateBasic() sdk.Error } diff --git a/examples/democoin/x/pow/types.go b/examples/democoin/x/pow/types.go index 2247a1e88c..4f808cbedc 100644 --- a/examples/democoin/x/pow/types.go +++ b/examples/democoin/x/pow/types.go @@ -32,6 +32,7 @@ func NewMsgMine(sender sdk.AccAddress, difficulty uint64, count uint64, nonce ui // nolint func (msg MsgMine) Type() string { return "pow" } +func (msg MsgMine) Name() string { return "mine" } func (msg MsgMine) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } func (msg MsgMine) String() string { return fmt.Sprintf("MsgMine{Sender: %s, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof) diff --git a/examples/democoin/x/simplestake/msgs.go b/examples/democoin/x/simplestake/msgs.go index 9f4c4f5f67..aea984d18b 100644 --- a/examples/democoin/x/simplestake/msgs.go +++ b/examples/democoin/x/simplestake/msgs.go @@ -27,6 +27,7 @@ func NewMsgBond(addr sdk.AccAddress, stake sdk.Coin, pubKey crypto.PubKey) MsgBo //nolint func (msg MsgBond) Type() string { return moduleName } //TODO update "stake/createvalidator" +func (msg MsgBond) Name() string { return "bond" } func (msg MsgBond) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Address} } // basic validation of the bond message @@ -66,6 +67,7 @@ func NewMsgUnbond(addr sdk.AccAddress) MsgUnbond { //nolint func (msg MsgUnbond) Type() string { return moduleName } //TODO update "stake/createvalidator" +func (msg MsgUnbond) Name() string { return "unbond" } func (msg MsgUnbond) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Address} } func (msg MsgUnbond) ValidateBasic() sdk.Error { return nil } diff --git a/examples/kvstore/tx.go b/examples/kvstore/tx.go index 0d8312fab5..bb3075ae42 100644 --- a/examples/kvstore/tx.go +++ b/examples/kvstore/tx.go @@ -18,6 +18,10 @@ func (tx kvstoreTx) Type() string { return "kvstore" } +func (tx kvstoreTx) Name() string { + return "kvstore" +} + func (tx kvstoreTx) GetMsgs() []sdk.Msg { return []sdk.Msg{tx} } diff --git a/server/mock/tx.go b/server/mock/tx.go index c15e0ab2cb..3bd248c744 100644 --- a/server/mock/tx.go +++ b/server/mock/tx.go @@ -31,6 +31,10 @@ func (tx kvstoreTx) Type() string { return "kvstore" } +func (tx kvstoreTx) Name() string { + return "kvstore_tx" +} + func (tx kvstoreTx) GetMsgs() []sdk.Msg { return []sdk.Msg{tx} } diff --git a/types/tx_msg.go b/types/tx_msg.go index b0f5c78f4b..9b4aab9370 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -11,6 +11,10 @@ type Msg interface { // Must be alphanumeric or empty. Type() string + // Returns a human-readable string for the message, intended for utilization + // within tags + Name() string + // ValidateBasic does a simple validation check that // doesn't require access to any other information. ValidateBasic() Error @@ -55,6 +59,7 @@ func NewTestMsg(addrs ...AccAddress) *TestMsg { //nolint func (msg *TestMsg) Type() string { return "TestMsg" } +func (msg *TestMsg) Name() string { return "Test message" } func (msg *TestMsg) GetSignBytes() []byte { bz, err := json.Marshal(msg.signers) if err != nil { diff --git a/x/bank/msgs.go b/x/bank/msgs.go index 316d66ba13..ed2d9a780f 100644 --- a/x/bank/msgs.go +++ b/x/bank/msgs.go @@ -20,7 +20,9 @@ func NewMsgSend(in []Input, out []Output) MsgSend { } // Implements Msg. +// nolint func (msg MsgSend) Type() string { return "bank" } // TODO: "bank/send" +func (msg MsgSend) Name() string { return "send" } // Implements Msg. func (msg MsgSend) ValidateBasic() sdk.Error { @@ -101,7 +103,9 @@ func NewMsgIssue(banker sdk.AccAddress, out []Output) MsgIssue { } // Implements Msg. +// nolint func (msg MsgIssue) Type() string { return "bank" } // TODO: "bank/issue" +func (msg MsgIssue) Name() string { return "issue" } // Implements Msg. func (msg MsgIssue) ValidateBasic() sdk.Error { diff --git a/x/gov/msgs.go b/x/gov/msgs.go index dcd7112aa1..a5a68ea21d 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -9,6 +9,8 @@ import ( // name to idetify transaction types const MsgType = "gov" +var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{} + //----------------------------------------------------------- // MsgSubmitProposal type MsgSubmitProposal struct { @@ -31,6 +33,7 @@ func NewMsgSubmitProposal(title string, description string, proposalType Proposa // Implements Msg. func (msg MsgSubmitProposal) Type() string { return MsgType } +func (msg MsgSubmitProposal) Name() string { return "submit_proposal" } // Implements Msg. func (msg MsgSubmitProposal) ValidateBasic() sdk.Error { @@ -95,7 +98,9 @@ func NewMsgDeposit(depositer sdk.AccAddress, proposalID int64, amount sdk.Coins) } // Implements Msg. +// nolint func (msg MsgDeposit) Type() string { return MsgType } +func (msg MsgDeposit) Name() string { return "deposit" } // Implements Msg. func (msg MsgDeposit) ValidateBasic() sdk.Error { @@ -154,7 +159,9 @@ func NewMsgVote(voter sdk.AccAddress, proposalID int64, option VoteOption) MsgVo } // Implements Msg. +// nolint func (msg MsgVote) Type() string { return MsgType } +func (msg MsgVote) Name() string { return "vote" } // Implements Msg. func (msg MsgVote) ValidateBasic() sdk.Error { diff --git a/x/ibc/types.go b/x/ibc/types.go index 0dcc0813d8..64b7d1f4c9 100644 --- a/x/ibc/types.go +++ b/x/ibc/types.go @@ -72,6 +72,7 @@ type IBCTransferMsg struct { // nolint func (msg IBCTransferMsg) Type() string { return "ibc" } +func (msg IBCTransferMsg) Name() string { return "transfer" } // x/bank/tx.go MsgSend.GetSigners() func (msg IBCTransferMsg) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.SrcAddr} } @@ -100,6 +101,7 @@ type IBCReceiveMsg struct { // nolint func (msg IBCReceiveMsg) Type() string { return "ibc" } +func (msg IBCReceiveMsg) Name() string { return "receive" } func (msg IBCReceiveMsg) ValidateBasic() sdk.Error { return msg.IBCPacket.ValidateBasic() } // x/bank/tx.go MsgSend.GetSigners() diff --git a/x/mock/app_test.go b/x/mock/app_test.go index 1319482ca2..d48a6ba14d 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -24,6 +24,7 @@ type testMsg struct { } func (tx testMsg) Type() string { return msgType } +func (tx testMsg) Name() string { return "test" } func (tx testMsg) GetMsg() sdk.Msg { return tx } func (tx testMsg) GetMemo() string { return "" } func (tx testMsg) GetSignBytes() []byte { return nil } diff --git a/x/slashing/msg.go b/x/slashing/msg.go index 58aae2498d..3ba7f9273a 100644 --- a/x/slashing/msg.go +++ b/x/slashing/msg.go @@ -26,6 +26,7 @@ func NewMsgUnjail(validatorAddr sdk.ValAddress) MsgUnjail { //nolint func (msg MsgUnjail) Type() string { return MsgType } +func (msg MsgUnjail) Name() string { return "unjail" } func (msg MsgUnjail) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} } diff --git a/x/stake/types/msg.go b/x/stake/types/msg.go index 71a8c86314..558c913fc7 100644 --- a/x/stake/types/msg.go +++ b/x/stake/types/msg.go @@ -7,7 +7,7 @@ import ( "github.com/tendermint/tendermint/crypto" ) -// name to idetify transaction types +// name to identify transaction types const MsgType = "stake" // Verify interface at compile time @@ -49,6 +49,7 @@ func NewMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddr //nolint func (msg MsgCreateValidator) Type() string { return MsgType } +func (msg MsgCreateValidator) Name() string { return "create_validator" } // Return address(es) that must sign over msg.GetSignBytes() func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress { @@ -118,6 +119,7 @@ func NewMsgEditValidator(valAddr sdk.ValAddress, description Description) MsgEdi //nolint func (msg MsgEditValidator) Type() string { return MsgType } +func (msg MsgEditValidator) Name() string { return "edit_validator" } func (msg MsgEditValidator) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} } @@ -168,6 +170,7 @@ func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delegation s //nolint func (msg MsgDelegate) Type() string { return MsgType } +func (msg MsgDelegate) Name() string { return "delegate" } func (msg MsgDelegate) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr} } @@ -218,6 +221,7 @@ func NewMsgBeginRedelegate(delAddr sdk.AccAddress, valSrcAddr, //nolint func (msg MsgBeginRedelegate) Type() string { return MsgType } +func (msg MsgBeginRedelegate) Name() string { return "begin_redelegate" } func (msg MsgBeginRedelegate) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr} } @@ -275,6 +279,7 @@ func NewMsgCompleteRedelegate(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk //nolint func (msg MsgCompleteRedelegate) Type() string { return MsgType } +func (msg MsgCompleteRedelegate) Name() string { return "complete_redelegate" } func (msg MsgCompleteRedelegate) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr} } @@ -321,6 +326,7 @@ func NewMsgBeginUnbonding(delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares //nolint func (msg MsgBeginUnbonding) Type() string { return MsgType } +func (msg MsgBeginUnbonding) Name() string { return "begin_unbonding" } func (msg MsgBeginUnbonding) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr} } // get the bytes for the message signer to sign on @@ -369,6 +375,7 @@ func NewMsgCompleteUnbonding(delAddr sdk.AccAddress, valAddr sdk.ValAddress) Msg //nolint func (msg MsgCompleteUnbonding) Type() string { return MsgType } +func (msg MsgCompleteUnbonding) Name() string { return "complete_unbonding" } func (msg MsgCompleteUnbonding) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddr} } From 18d5b048a97df8c40cd89889a6d2088e6bfaf8ab Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 17 Sep 2018 07:36:26 -0700 Subject: [PATCH 11/12] Merge PR #2299: simulation: reduce number of failed governance msgs --- x/gov/simulation/msgs.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index 478b4be0f0..4ca1cdf445 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -51,7 +51,11 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe if err != nil { return "", nil, err } - action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) + action, ok := simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) + // don't schedule votes if proposal failed + if !ok { + return action, nil, nil + } proposalID := k.GetLastProposalID(ctx) // 2) Schedule operations for votes // 2.1) first pick a number of people to vote. @@ -85,24 +89,25 @@ func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operati if err != nil { return "", nil, err } - action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) + action, _ = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) return action, nil, nil } } -func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string) { +func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string, ok bool) { ctx, write := ctx.CacheContext() result := handler(ctx, msg) - if result.IsOK() { + ok = result.IsOK() + if ok { // Update pool to keep invariants pool := sk.GetPool(ctx) pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom))) sk.SetPool(ctx, pool) write() } - event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK())) - action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) - return action + event(fmt.Sprintf("gov/MsgSubmitProposal/%v", ok)) + action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", ok, msg.GetSignBytes()) + return } func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg gov.MsgSubmitProposal, err error) { From b09e8599d933e1071a0d35e8264ce53ae71fb2b8 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 17 Sep 2018 08:26:21 -0700 Subject: [PATCH 12/12] Merge PR #2334: ADR for global message counter --- .../{ => decision-records}/README.md | 0 .../adr-001-message-counter.md | 54 +++++++++++++++++++ .../{ => decision-records}/adr-template.md | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) rename docs/architecture/{ => decision-records}/README.md (100%) create mode 100644 docs/architecture/decision-records/adr-001-message-counter.md rename docs/architecture/{ => decision-records}/adr-template.md (98%) diff --git a/docs/architecture/README.md b/docs/architecture/decision-records/README.md similarity index 100% rename from docs/architecture/README.md rename to docs/architecture/decision-records/README.md diff --git a/docs/architecture/decision-records/adr-001-message-counter.md b/docs/architecture/decision-records/adr-001-message-counter.md new file mode 100644 index 0000000000..0e9f25990e --- /dev/null +++ b/docs/architecture/decision-records/adr-001-message-counter.md @@ -0,0 +1,54 @@ +# ADR 001: Global Message Counter + +## Context + +There is a desire for modules to have a concept of orderings between messages. + +One such example is in staking, we currently use an "intra bond tx counter" and +bond height. +The purpose these two serve is to providing an ordering for validators with equal stake, +for usage in the power-ranking of validators. +We can't use address here, as that would create a bad incentive to grind +addresses that optimized the sort function, which lowers the private key's +security. +Instead we order by whose transaction appeared first, as tracked by bondHeight +and intra bond tx counter. + +This logic however should not be unique to staking. +It is very conceivable that many modules in the future will want to be able to +know the ordering of messages / objects after they were initially created. + +## Decision + +Create a global message counter field of type int64. +Note that with int64's, there is no fear of overflow under normal use, +as it is only getting incremented by one, +and thus has a space of 9 quintillion values to go through. + +This counter must be persisted in state, but can just be read and written on +begin/end block respectively. +This field will get incremented upon every DeliverTx, +regardless if the transaction succeeds or not. +It must also be incremented within the check state for CheckTx. +The global message ordering field should be set within the context +so that modules can access it. + +## Corollary - Intra block ordering +In the event that there is desire to just have an intra block msg counter, +this can easily be derived from the global message counter. +Simply subtract current counter from first global message counter in the block. +Thus the relevant module could easily implement this. + +## Status +Proposed + +## Consequences + +### Positive +* Moves message ordering out of the set of things staking must keep track of +* Abstracts the logic well so other modules can use it + +### Negative +* Another thing to implement prelaunch. (Though this should be easy to implement) + +### Neutral diff --git a/docs/architecture/adr-template.md b/docs/architecture/decision-records/adr-template.md similarity index 98% rename from docs/architecture/adr-template.md rename to docs/architecture/decision-records/adr-template.md index 4ff7ad9465..6153a9d1ee 100644 --- a/docs/architecture/adr-template.md +++ b/docs/architecture/decision-records/adr-template.md @@ -29,4 +29,4 @@ If the proposed change will be large, please also indicate a way to do the chang ## References > Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here! -* {reference link} \ No newline at end of file +* {reference link}