diff --git a/CHANGELOG.md b/CHANGELOG.md index e051932a703c..55f99e3846db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,13 +34,27 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog -## v0.40.0-rc1 - [Unreleased] +## v0.40.0-rc1 - 2020-10-19 ### Client Breaking Changes -* __General__ +* __Modules__ * (x/staking) [\#7499](https://github.com/cosmos/cosmos-sdk/pull/7499) `BondStatus` is now a protobuf `enum` instead of an `int32`, and JSON serialized using its protobuf name, so expect names like `BOND_STATUS_UNBONDING` as opposed to `Unbonding`. +### Features + +* (cli) \#7221 Add the option of emitting amino encoded json from the CLI + +### Bug Fixes + +* (kvstore) [\#7415](https://github.com/cosmos/cosmos-sdk/pull/7415) Allow new stores to be registered during on-chain upgrades. + +### Improvements + +* (tendermint) \#7527 Update sdk to tendermint 0.34-rc5 +* (iavl) \#7549 Update sdk to IAVL 0.15.0-rc4 + + ## v0.40.0-rc0 - 2020-10-13 v0.40.0, known as the Stargate release of the Cosmos SDK, is one of the largest releases diff --git a/Makefile b/Makefile index e077ed22ac61..9331b1c664b2 100644 --- a/Makefile +++ b/Makefile @@ -387,10 +387,10 @@ proto-check-breaking-docker: @$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master .PHONY: proto-check-breaking-ci -TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/3359e0bf2f8414d9687f9eecda67b899d64a9cd1/proto/tendermint +TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc5/proto/tendermint GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master -CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.2 +CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3 TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto TM_ABCI_TYPES = third_party/proto/tendermint/abci diff --git a/baseapp/abci.go b/baseapp/abci.go index e3a07d620406..3cffb60bc50d 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -213,11 +213,6 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { defer telemetry.MeasureSince(time.Now(), "abci", "check_tx") - tx, err := app.txDecoder(req.Tx) - if err != nil { - return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) - } - var mode runTxMode switch { @@ -231,7 +226,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type)) } - gInfo, result, err := app.runTx(mode, req.Tx, tx) + gInfo, result, err := app.runTx(mode, req.Tx) if err != nil { return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } @@ -253,11 +248,6 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { defer telemetry.MeasureSince(time.Now(), "abci", "deliver_tx") - tx, err := app.txDecoder(req.Tx) - if err != nil { - return sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace) - } - gInfo := sdk.GasInfo{} resultStr := "successful" @@ -268,7 +258,7 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted") }() - gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx) + gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx) if err != nil { resultStr = "failed" return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) @@ -673,12 +663,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res case "simulate": txBytes := req.Data - tx, err := app.txDecoder(txBytes) - if err != nil { - return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx")) - } - - gInfo, res, err := app.Simulate(txBytes, tx) + gInfo, res, err := app.Simulate(txBytes) if err != nil { return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx")) } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 23a9d1dd8d56..0c878c516f1f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -550,7 +550,7 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context // Note, gas execution info is always returned. A reference to a Result is // returned if the tx does not run out of gas and if all the messages are valid // and execute successfully. An error is returned otherwise. -func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk.GasInfo, result *sdk.Result, err error) { +func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, result *sdk.Result, err error) { // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. @@ -596,6 +596,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. } }() + tx, err := app.txDecoder(txBytes) + if err != nil { + return sdk.GasInfo{}, nil, err + } + msgs := tx.GetMsgs() if err := validateBasicTxMsgs(msgs); err != nil { return sdk.GasInfo{}, nil, err diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 760e624eb99f..af82f03fc358 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -29,6 +29,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) var ( @@ -97,6 +98,14 @@ func registerTestCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&msgNoRoute{}, "cosmos-sdk/baseapp/msgNoRoute", nil) } +// aminoTxEncoder creates a amino TxEncoder for testing purposes. +func aminoTxEncoder() sdk.TxEncoder { + cdc := codec.NewLegacyAmino() + registerTestCodec(cdc) + + return legacytx.StdTxConfig{Cdc: cdc}.TxEncoder() +} + // simple one store baseapp func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { app := newBaseApp(t.Name(), options...) @@ -422,7 +431,7 @@ func TestLoadVersionPruning(t *testing.T) { for _, v := range []int64{1, 2, 4} { _, err = app.cms.CacheMultiStoreWithVersion(v) - require.Error(t, err) + require.NoError(t, err) } for _, v := range []int64{3, 5, 6, 7} { @@ -1118,13 +1127,13 @@ func TestSimulateTx(t *testing.T) { require.Nil(t, err) // simulate a message, check gas reported - gInfo, result, err := app.Simulate(txBytes, tx) + gInfo, result, err := app.Simulate(txBytes) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) // simulate again, same result - gInfo, result, err = app.Simulate(txBytes, tx) + gInfo, result, err = app.Simulate(txBytes) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) @@ -1171,7 +1180,7 @@ func TestRunInvalidTransaction(t *testing.T) { // transaction with no messages { emptyTx := &txTest{} - _, result, err := app.Deliver(emptyTx) + _, result, err := app.Deliver(aminoTxEncoder(), emptyTx) require.Error(t, err) require.Nil(t, result) @@ -1198,7 +1207,7 @@ func TestRunInvalidTransaction(t *testing.T) { for _, testCase := range testCases { tx := testCase.tx - _, result, err := app.Deliver(tx) + _, result, err := app.Deliver(aminoTxEncoder(), tx) if testCase.fail { require.Error(t, err) @@ -1215,7 +1224,7 @@ func TestRunInvalidTransaction(t *testing.T) { // transaction with no known route { unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false} - _, result, err := app.Deliver(unknownRouteTx) + _, result, err := app.Deliver(aminoTxEncoder(), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1224,7 +1233,7 @@ func TestRunInvalidTransaction(t *testing.T) { require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err) unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false} - _, result, err = app.Deliver(unknownRouteTx) + _, result, err = app.Deliver(aminoTxEncoder(), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1274,7 +1283,7 @@ func TestTxGasLimits(t *testing.T) { } }() - count := tx.(*txTest).Counter + count := tx.(txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return newCtx, nil @@ -1284,7 +1293,7 @@ func TestTxGasLimits(t *testing.T) { routerOpt := func(bapp *BaseApp) { r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) @@ -1322,7 +1331,7 @@ func TestTxGasLimits(t *testing.T) { for i, tc := range testCases { tx := tc.tx - gInfo, result, err := app.Deliver(tx) + gInfo, result, err := app.Deliver(aminoTxEncoder(), tx) // check gas used and wanted require.Equal(t, tc.gasUsed, gInfo.GasUsed, fmt.Sprintf("tc #%d; gas: %v, result: %v, err: %s", i, gInfo, result, err)) @@ -1359,7 +1368,7 @@ func TestMaxBlockGasLimits(t *testing.T) { } }() - count := tx.(*txTest).Counter + count := tx.(txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return @@ -1368,7 +1377,7 @@ func TestMaxBlockGasLimits(t *testing.T) { routerOpt := func(bapp *BaseApp) { r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) @@ -1412,7 +1421,7 @@ func TestMaxBlockGasLimits(t *testing.T) { // execute the transaction multiple times for j := 0; j < tc.numDelivers; j++ { - _, result, err := app.Deliver(tx) + _, result, err := app.Deliver(aminoTxEncoder(), tx) ctx := app.getState(runTxModeDeliver).ctx @@ -1480,7 +1489,7 @@ func TestCustomRunTxPanicHandler(t *testing.T) { { tx := newTxCounter(0, 0) - require.PanicsWithValue(t, customPanicMsg, func() { app.Deliver(tx) }) + require.PanicsWithValue(t, customPanicMsg, func() { app.Deliver(aminoTxEncoder(), tx) }) } } @@ -1589,7 +1598,7 @@ func TestGasConsumptionBadTx(t *testing.T) { routerOpt := func(bapp *BaseApp) { r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) @@ -1668,7 +1677,7 @@ func TestQuery(t *testing.T) { require.Equal(t, 0, len(res.Value)) // query is still empty after a CheckTx - _, resTx, err := app.Check(tx) + _, resTx, err := app.Check(aminoTxEncoder(), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) @@ -1678,7 +1687,7 @@ func TestQuery(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - _, resTx, err = app.Deliver(tx) + _, resTx, err = app.Deliver(aminoTxEncoder(), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) diff --git a/baseapp/helpers.go b/baseapp/helpers.go deleted file mode 100644 index 2fb7cc041a56..000000000000 --- a/baseapp/helpers.go +++ /dev/null @@ -1,33 +0,0 @@ -package baseapp - -import ( - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func (app *BaseApp) Check(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeCheck, nil, tx) -} - -func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeSimulate, txBytes, tx) -} - -func (app *BaseApp) Deliver(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeDeliver, nil, tx) -} - -// Context with current {check, deliver}State of the app used by tests. -func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Context { - if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, app.logger). - WithMinGasPrices(app.minGasPrices) - } - - return sdk.NewContext(app.deliverState.ms, header, false, app.logger) -} - -func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { - return sdk.NewContext(app.cms, header, isCheckTx, app.logger) -} diff --git a/baseapp/params.go b/baseapp/params.go index 7b3ec6ff7d23..9682afb31fc0 100644 --- a/baseapp/params.go +++ b/baseapp/params.go @@ -63,8 +63,8 @@ func ValidateEvidenceParams(i interface{}) error { return fmt.Errorf("evidence maximum age time duration must be positive: %v", v.MaxAgeDuration) } - if v.MaxNum <= 0 { - return fmt.Errorf("evidence maximum number of evidence must be positive: %v", v.MaxNum) + if v.MaxBytes < 0 { + return fmt.Errorf("maximum evidence bytes must be positive: %v", v.MaxBytes) } return nil diff --git a/baseapp/params_test.go b/baseapp/params_test.go index a78f132de46a..2d8f8c1addb0 100644 --- a/baseapp/params_test.go +++ b/baseapp/params_test.go @@ -36,8 +36,8 @@ func TestValidateEvidenceParams(t *testing.T) { {nil, true}, {&tmproto.EvidenceParams{}, true}, {tmproto.EvidenceParams{}, true}, - {tmproto.EvidenceParams{MaxAgeNumBlocks: -1, MaxAgeDuration: 18004000, MaxNum: 50}, true}, - {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: 18004000, MaxNum: 50}, false}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: -1, MaxAgeDuration: 18004000, MaxBytes: 5000000}, true}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: 18004000, MaxBytes: 5000000}, false}, } for _, tc := range testCases { diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go new file mode 100644 index 000000000000..407ebd9a7cd9 --- /dev/null +++ b/baseapp/test_helpers.go @@ -0,0 +1,46 @@ +package baseapp + +import ( + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func (app *BaseApp) Check(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { + // runTx expects tx bytes as argument, so we encode the tx argument into + // bytes. Note that runTx will actually decode those bytes again. But since + // this helper is only used in tests/simulation, it's fine. + bz, err := txEncoder(tx) + if err != nil { + return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) + } + return app.runTx(runTxModeCheck, bz) +} + +func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { + return app.runTx(runTxModeSimulate, txBytes) +} + +func (app *BaseApp) Deliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { + // See comment for Check(). + bz, err := txEncoder(tx) + if err != nil { + return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) + } + return app.runTx(runTxModeDeliver, bz) +} + +// Context with current {check, deliver}State of the app used by tests. +func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Context { + if isCheckTx { + return sdk.NewContext(app.checkState.ms, header, true, app.logger). + WithMinGasPrices(app.minGasPrices) + } + + return sdk.NewContext(app.deliverState.ms, header, false, app.logger) +} + +func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { + return sdk.NewContext(app.cms, header, isCheckTx, app.logger) +} diff --git a/client/account_retriever.go b/client/account_retriever.go index e7f9fbaea41d..51c0a7fa9bcc 100644 --- a/client/account_retriever.go +++ b/client/account_retriever.go @@ -1,11 +1,25 @@ package client -import "github.com/cosmos/cosmos-sdk/types" +import ( + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Account defines a read-only version of the auth module's AccountI. +type Account interface { + GetAddress() sdk.AccAddress + GetPubKey() crypto.PubKey // can return nil. + GetAccountNumber() uint64 + GetSequence() uint64 +} // AccountRetriever defines the interfaces required by transactions to // ensure an account exists and to be able to query for account fields necessary // for signing. type AccountRetriever interface { - EnsureExists(clientCtx Context, addr types.AccAddress) error - GetAccountNumberSequence(clientCtx Context, addr types.AccAddress) (accNum uint64, accSeq uint64, err error) + GetAccount(clientCtx Context, addr sdk.AccAddress) (Account, error) + GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (Account, int64, error) + EnsureExists(clientCtx Context, addr sdk.AccAddress) error + GetAccountNumberSequence(clientCtx Context, addr sdk.AccAddress) (accNum uint64, accSeq uint64, err error) } diff --git a/client/grpc/simulate/simulate.go b/client/grpc/simulate/simulate.go index eee54583cb4b..9eed3e30a167 100644 --- a/client/grpc/simulate/simulate.go +++ b/client/grpc/simulate/simulate.go @@ -8,11 +8,10 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" ) // BaseAppSimulateFn is the signature of the Baseapp#Simulate function. -type BaseAppSimulateFn func(txBytes []byte, txtypes sdk.Tx) (sdk.GasInfo, *sdk.Result, error) +type BaseAppSimulateFn func(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) type simulateServer struct { simulate BaseAppSimulateFn @@ -39,13 +38,12 @@ func (s simulateServer) Simulate(ctx context.Context, req *SimulateRequest) (*Si if err != nil { return nil, err } - txBuilder := authtx.WrapTx(req.Tx) txBytes, err := req.Tx.Marshal() if err != nil { return nil, err } - gasInfo, result, err := s.simulate(txBytes, txBuilder.GetTx()) + gasInfo, result, err := s.simulate(txBytes) if err != nil { return nil, err } diff --git a/client/test_helpers.go b/client/test_helpers.go index 673519c5fac9..1140298fda42 100644 --- a/client/test_helpers.go +++ b/client/test_helpers.go @@ -3,19 +3,67 @@ package client import ( "fmt" + "github.com/tendermint/tendermint/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" ) +var ( + _ AccountRetriever = TestAccountRetriever{} + _ Account = TestAccount{} +) + +// TestAccount represents a client Account that can be used in unit tests +type TestAccount struct { + Address sdk.AccAddress + Num uint64 + Seq uint64 +} + +// GetAddress implements client Account.GetAddress +func (t TestAccount) GetAddress() sdk.AccAddress { + return t.Address +} + +// GetPubKey implements client Account.GetPubKey +func (t TestAccount) GetPubKey() crypto.PubKey { + return nil +} + +// GetAccountNumber implements client Account.GetAccountNumber +func (t TestAccount) GetAccountNumber() uint64 { + return t.Num +} + +// GetSequence implements client Account.GetSequence +func (t TestAccount) GetSequence() uint64 { + return t.Seq +} + // TestAccountRetriever is an AccountRetriever that can be used in unit tests type TestAccountRetriever struct { - Accounts map[string]struct { - Address sdk.AccAddress - Num uint64 - Seq uint64 + Accounts map[string]TestAccount +} + +// GetAccount implements AccountRetriever.GetAccount +func (t TestAccountRetriever) GetAccount(_ Context, addr sdk.AccAddress) (Account, error) { + acc, ok := t.Accounts[addr.String()] + if !ok { + return nil, fmt.Errorf("account %s not found", addr) } + + return acc, nil } -var _ AccountRetriever = TestAccountRetriever{} +// GetAccountWithHeight implements AccountRetriever.GetAccountWithHeight +func (t TestAccountRetriever) GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (Account, int64, error) { + acc, err := t.GetAccount(clientCtx, addr) + if err != nil { + return nil, 0, err + } + + return acc, 0, nil +} // EnsureExists implements AccountRetriever.EnsureExists func (t TestAccountRetriever) EnsureExists(_ Context, addr sdk.AccAddress) error { diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go index 8bdb8168f5d5..8cffc58c5f85 100644 --- a/codec/types/interface_registry.go +++ b/codec/types/interface_registry.go @@ -146,6 +146,11 @@ func (registry *interfaceRegistry) ListImplementations(ifaceName string) []strin } func (registry *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error { + // here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding + if any == nil { + return nil + } + if any.TypeUrl == "" { // if TypeUrl is empty return nil because without it we can't actually unpack anything return nil diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md index 3d1eb531a376..0e9a644a5918 100644 --- a/docs/architecture/adr-020-protobuf-transaction-encoding.md +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -11,6 +11,7 @@ - 2020 August 07: Use ADR 027 for serializing `SignDoc`. - 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`. - 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. +- 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. ## Status @@ -315,6 +316,8 @@ and messages. ```go type AccountRetriever interface { + GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error) + GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error) EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) } diff --git a/docs/basics/README.md b/docs/basics/README.md index 131ee1ba452f..ad54c6bee783 100644 --- a/docs/basics/README.md +++ b/docs/basics/README.md @@ -6,11 +6,11 @@ parent: # Basics -This repository contains reference documentation on the basic concepts of the Cosmos SDK. +This repository contains reference documentation on the basic concepts of the Cosmos SDK. 1. [Anatomy of an SDK Application](./app-anatomy.md) 2. [Lifecycle of a transaction](./tx-lifecycle.md) 3. [Accounts](./accounts.md) 4. [Gas and Fees](./gas-fees.md) -After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. \ No newline at end of file +After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md index 7b32a1f55a12..411aa201a889 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -2,7 +2,7 @@ order: 3 --> -# Accounts +# Accounts This document describes the in-built accounts system of the Cosmos SDK. {synopsis} @@ -12,9 +12,9 @@ This document describes the in-built accounts system of the Cosmos SDK. {synopsi ## Account Definition -In the Cosmos SDK, an *account* designates a pair of *public key* `PubKey` and *private key* `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. +In the Cosmos SDK, an _account_ designates a pair of _public key_ `PubKey` and _private key_ `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. -To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. +To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. ``` Account 0 Account 1 Account 2 @@ -56,65 +56,65 @@ To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32 +-------------------+ ``` -In the Cosmos SDK, accounts are stored and managed via an object called a [`Keybase`](#keybase). +In the Cosmos SDK, accounts are stored and managed via an object called a [`Keyring`](#keyring). -## Keybase +## Keyring -A `Keybase` is an object that stores and manages accounts. In the Cosmos SDK, a `Keybase` implementation follows the `Keybase` interface: +A `Keyring` is an object that stores and manages accounts. In the Cosmos SDK, a `Keyring` implementation follows the `Keyring` interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/types.go#L13-L86 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keyring/keyring.go#L50-L88 -The default implementation of `Keybase` of the Cosmos SDK is `dbKeybase`. +The default implementation of `Keyring` comes from the third-party [`99designs/keyring`](https://github.com/99designs/keyring) library. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/keybase.go +A few notes on the `Keyring` methods: -A few notes on the `Keybase` methods as implemented in `dbKeybase`: +- `Sign(uid string, msg []byte) ([]byte, tmcrypto.PubKey, error)` strictly deals with the signature of the `message` bytes. Some preliminary work should be done beforehand to prepare and encode the `message` into a canonical `[]byte` form, and this is done in the `GetSignBytes` method. See an example of `message` preparation from the `x/bank` module. Note that signature verification is not implemented in the SDK by default. It is deferred to the [`anteHandler`](#antehandler). + +++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/x/bank/types/msgs.go#L51-L54 +- `NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/mintkey/mintkey.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `NewAccount` method derives keys and addresses using the algorithm specified in the last argument `algo`. Currently, the SDK supports two public key algorithms: + - `secp256k1`, as implemented in the [SDK's `crypto/keys/secp256k1` package](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/secp256k1/secp256k1.go), + - `ed25519`, as implemented in the [SDK's `crypto/keys/ed25519` package](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/ed25519/ed25519.go). +- `ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)` exports a private key in ASCII-armored encrypted format, using the given passphrase. You can then either import it again into the keyring using the `ImportPrivKey(uid, armor, passphrase string)` function, or decrypt it into a raw private key using the `UnarmorDecryptPrivKey(armorStr string, passphrase string)` function. -- `Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)` strictly deals with the signature of the `message` bytes. Some preliminary work should be done beforehand to prepare and encode the `message` into a canonical `[]byte` form. See an example of `message` preparation from the `auth` module. Note that signature verification is not implemented in the SDK by default. It is deferred to the [`anteHandler`](#antehandler). - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L176-L209 -- `CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)` creates a new mnemonic and prints it in the logs, but it **does not persist it on disk**. -- `CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/mintkey/mintkey.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `CreateAccount` method derives keys and addresses using `secp256k1` as implemented in the [Tendermint library](https://github.com/tendermint/tendermint/tree/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1). As a result, it only works for creating account keys and addresses, not consensus keys. See [`Addresses`](#addresses) for more. - -The current implementation of `dbKeybase` is basic and does not offer on-demand locking. If an instance of `dbKeybase` is created, the underlying `db` is locked meaning no other process can access it besides the one in which it was instantiated. This is the reason why the default SDK client uses another implementation of the `Keybase` interface called `lazyKeybase`: - - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/lazy_keybase.go - -`lazyKeybase` is simple wrapper around `dbKeybase` which locks the database only when operations are to be performed and unlocks it immediately after. With the `lazyKeybase`, it is possible for the [command-line interface](../interfaces/cli.md) to create a new account while the [rest server](../interfaces/rest.md) is running. It is also possible to pipe multiple CLI commands. +Also see the [`Addresses`](#addresses) section for more information. ## Addresses and PubKeys `Addresses` and `PubKey`s are both public information that identify actors in the application. There are 3 main types of `Addresses`/`PubKeys` available by default in the Cosmos SDK: -- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve. -- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve. +- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve. +- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve. - Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve. | | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length | -|--------------------|-----------------------|----------------------|-------------|---------------------|--------------------| +| ------------------ | --------------------- | -------------------- | ----------- | ------------------- | ------------------ | | Accounts | cosmos | cosmospub | `secp256k1` | `20` | `33` | | Validator Operator | cosmosvaloper | cosmosvaloperpub | `secp256k1` | `20` | `33` | -| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` | +| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` | ### PubKeys -`PubKey`s used in the Cosmos SDK follow the `Pubkey` interface defined in tendermint's `crypto` package: +`PubKey`s used in the Cosmos SDK are Protobuf messages and extend the `Pubkey` interface defined in tendermint's `crypto` package: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/types/types.go#L8-L13 + ++++ https://github.com/tendermint/tendermint/blob/01c32c62e8840d812359c9e87e9c575aa67acb09/crypto/crypto.go#L22-L28 -+++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/crypto.go#L22-L27 +- For `secp256k1` keys, the actual implementation can be found [here](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/secp256k1/secp256k1.go). +- For `ed25519` keys, it can be found [here](https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/crypto/keys/ed25519/ed25519.go). -For `secp256k1` keys, the actual implementation can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1/secp256k1.go#L140). For `ed25519` keys, it can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/ed25519/ed25519.go#L135). +In both case, the actual key (as raw bytes) is the compressed form of the pubkey. The first byte is a `0x02` byte if the `y`-coordinate is the lexicographically largest of the two associated with the `x`-coordinate. Otherwise the first byte is a `0x03`. This prefix is followed with the `x`-coordinate. -Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw form. Instead, they are double encoded using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`. +Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw bytes form. Instead, they are encoded to string using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK, it is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L579-L729 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L579-L729 ### Addresses The Cosmos SDK comes by default with 3 types of addresses: - `AccAddress` for accounts. -- `ValAddress` for validator operators. -- `ConsAddress` for validator nodes. +- `ValAddress` for validator operators. +- `ConsAddress` for validator nodes. Each of these address types are an alias for an hex-encoded `[]byte` array of length 20. Here is the standard way to obtain an address `aa` from a `Pubkey pub`: @@ -124,12 +124,12 @@ aa := sdk.AccAddress(pub.Address().Bytes()) These addresses implement the `Address` interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L71-L80 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L73-L82 -Of note, the `Marhsal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuff compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Next is an example: +Of note, the `Marshal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuf compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Here is an example: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L229-L243 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/address.go#L232-L246 ## Next {hide} -Learn about [gas and fees](./gas-fees.md) {hide} \ No newline at end of file +Learn about [gas and fees](./gas-fees.md) {hide} diff --git a/docs/basics/app-anatomy.md b/docs/basics/app-anatomy.md index fc64b772423f..529ec9b27567 100644 --- a/docs/basics/app-anatomy.md +++ b/docs/basics/app-anatomy.md @@ -2,7 +2,7 @@ order: 1 --> -# Anatomy of an SDK Application +# Anatomy of an SDK Application This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named `app` will be used. {synopsis} @@ -44,53 +44,54 @@ In general, the core of the state-machine is defined in a file called `app.go`. The first thing defined in `app.go` is the `type` of the application. It is generally comprised of the following parts: -- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing). -- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. -- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. -- **A reference to a [`codec`](../core/encoding.md).** The application's `codec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The `codec` must be deterministic. The default codec is [amino](../core/encoding.md). -- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering [`routes`](../core/baseapp.md#routing), [query routes](../core/baseapp.md#query-routing) or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). +- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing). +- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. +- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. +- **A reference to an [`appCodec`](../core/encoding.md).** The application's `appCodec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The default codec is [Protocol Buffers](../core/encoding.md). +- **A reference to a [`legacyAmino`](../core/encoding.md) codec.** Some parts of the SDK have not been migrated to use the `appCodec` above, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases. +- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering [`routes`](../core/baseapp.md#routing), [gRPC query services](../core/baseapp.md#grpc-query-services) and [legacy Tendermint query routes](../core/baseapp.md#legacy-query-routing) or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). -See an example of application type definition from [`gaia`](https://github.com/cosmos/gaia) +See an example of application type definition from `simapp`, the SDK's own app used for demo and testing purposes: -+++ https://github.com/cosmos/gaia/blob/5bc422e6868d04747e50b467e8eeb31ae2fe98a3/app/app.go#L87-L115 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L140-L179 ### Constructor Function -This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command. +This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L20 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/server/types/app.go#L42-L44 Here are the main actions performed by this function: - Instantiate a new [`codec`](../core/encoding.md) and initialize the `codec` of each of the application's module using the [basic manager](../building-modules/module-manager.md#basicmanager) - Instantiate a new application with a reference to a `baseapp` instance, a codec and all the appropriate store keys. -- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. -- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. -- With the module manager, initialize the application's [`routes`](../core/baseapp.md#routing) and [query routes](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`handler`](#handler) using the routes defined here. Likewise, when a query is received by the application, it is routed to the appropriate module's [`querier`](#querier) using the query routes defined here. -- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. -- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. +- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. +- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. +- With the module manager, initialize the application's [`routes`](../core/baseapp.md#routing), [`gRPC query services`](../core/baseapp.md#grpc-query-services) and [`legacy query routes`](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`handler`](#handler) using the `routes` defined here. Likewise, when a gRPC request is received by the application, it is routed to the appropriate module's [`gRPC query service`](#grpc-query-services) using the gRPC routes defined here. The SDK still supports legacy Tendermint queries, and these queries are routes using the `legacy query routes`. +- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. +- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. - Set the remainer of application's parameters: - + [`InitChainer`](#initchainer): used to initialize the application when it is first started. - + [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). - + [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification. -- Mount the stores. -- Return the application. + - [`InitChainer`](#initchainer): used to initialize the application when it is first started. + - [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). + - [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification. +- Mount the stores. +- Return the application. Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.appd/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. -See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia): +See an example of application constructor from `simapp`: -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L190-L427 ### InitChainer -The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. +The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. In general, the `InitChainer` is mostly composed of the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules. This is done by calling the `InitGenesis` function of the module manager, which in turn will call the `InitGenesis` function of each of the modules it contains. Note that the order in which the modules' `InitGenesis` functions must be called has to be set in the module manager using the [module manager's](../building-modules/module-manager.md) `SetOrderInitGenesis` method. This is done in the [application's constructor](#application-constructor), and the `SetOrderInitGenesis` has to be called before the `SetInitChainer`. -See an example of an `InitChainer` from [`gaia`](https://github.com/cosmos/gaia): - -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L235-L239 +See an example of an `InitChainer` from `simapp`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L450-L455 ### BeginBlocker and EndBlocker @@ -98,48 +99,58 @@ The SDK offers developers the possibility to implement automatic execution of co In general, the `BeginBlocker` and `EndBlocker` functions are mostly composed of the [`BeginBlock` and `EndBlock`](../building-modules/beginblock-endblock.md) functions of each of the application's modules. This is done by calling the `BeginBlock` and `EndBlock` functions of the module manager, which in turn will call the `BeginBLock` and `EndBlock` functions of each of the modules it contains. Note that the order in which the modules' `BegingBlock` and `EndBlock` functions must be called has to be set in the module manager using the `SetOrderBeginBlock` and `SetOrderEndBlock` methods respectively. This is done via the [module manager](../building-modules/module-manager.md) in the [application's constructor](#application-constructor), and the `SetOrderBeginBlock` and `SetOrderEndBlock` methods have to be called before the `SetBeginBlocker` and `SetEndBlocker` functions. -As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. +As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. -See an example of `BeginBlocker` and `EndBlocker` functions from [`gaia`](https://github.com/cosmos/gaia) +See an example of `BeginBlocker` and `EndBlocker` functions from `simapp` -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L224-L232 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L440-L448 ### Register Codec -The `MakeCodec` function is the last important function of the `app.go` file. The goal of this function is to instantiate a [codec `cdc`](../core/encoding.md) (e.g. amino) initialize the codec of the SDK and each of the application's modules using the `RegisterLegacyAminoCodec` function. +The `EncodingConfig` structure is the last important part of the `app.go` file. The goal of this structure is to define the codecs that will be used throughout the app. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/params/encoding.go#L9-L16 + +Here are descriptions of what each of the four fields means: -To register the application's modules, the `MakeCodec` function calls `RegisterLegacyAminoCodec` on `ModuleBasics`. `ModuleBasics` is a [basic manager](../building-modules/module-manager.md#basicmanager) which lists all of the application's modules. It is instanciated in the `init()` function, and only serves to easily register non-dependant elements of application's modules (such as codec). To learn more about the basic module manager, click [here](../building-modules/module-manager.md#basicmanager). +- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces, which are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct which contains a `type_url` (the concrete type of the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method, which can be used to register the module's own interfaces and implementations. + - To go more into details, the SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). +- `Marshaler`: The `Marshaler` is the default codec used throughout the SDK. It is composed of a `BinaryMarshaler` used to encode and decode state, and a `JSONMarshaler` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`. +- `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../core/transactions.md). +- `Amino`: Some legacy parts of the SDK still use Amino for backwards-compatibility. Each module exposes a `RegisterLegacyAmino` method to register the module's specific types within Amino. This `Amino` codec should not be used by app developers anymore, and will be removed in future releases. -See an example of a `MakeCodec` from [`gaia`](https://github.com/cosmos/gaia): - -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L64-L70 +The SDK exposes a `MakeCodecs` function used to create a `EncodingConfig`. It uses Protobuf as default `Marshaler`, and passes it down to the app's `appCodec` field. It also instantiates a legacy `Amino` codec inside the app's `legacyAmino` field. + +See an example of a `MakeCodecs` from `simapp`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/simapp/app.go#L429-L435 ## Modules -[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules). +[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules). ### Application Module Interface -Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`. +Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`. -`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules. +`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules. ### Message Types -[`Message`s](../building-modules/messages-and-queries.md#messages) are objects defined by each module that implement the [`message`](../building-modules/messages-and-queries.md#messages) interface. Each [`transaction`](../core/transactions.md) contains one or multiple `messages`. +[`Message`s](../building-modules/messages-and-queries.md#messages) are objects defined by each module that implement the [`message`](../building-modules/messages-and-queries.md#messages) interface. Each [`transaction`](../core/transactions.md) contains one or multiple `messages`. When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via [`DeliverTx`](https://tendermint.com/docs/app-dev/abci-spec.html#delivertx). Then, the application handles the transaction: 1. Upon receiving the transaction, the application first unmarshalls it from `[]bytes`. -2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the message(s) contained in the transaction. -3. With the `Type()` method of the `message`, `baseapp` is able to route it to the appropriate module's [`handler`](#handler) in order for it to be processed. -4. If the message is successfully processed, the state is updated. +2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the message(s) contained in the transaction. +3. With the `Type()` method of the `message`, `baseapp` is able to route it to the appropriate module's [`handler`](#handler) in order for it to be processed. +4. If the message is successfully processed, the state is updated. For a more detailed look at a transaction lifecycle, click [here](./tx-lifecycle.md). Module developers create custom message types when they build their own module. The general practice is to prefix the type declaration of the message with `Msg`. For example, the message type `MsgSend` allows users to transfer tokens: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/bank/internal/types/msgs.go#L10-L15 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/proto/cosmos/bank/v1beta1/tx.proto#L10-L19 It is processed by the `handler` of the `bank` module, which ultimately calls the `keeper` of the `auth` module in order to update the state. @@ -150,25 +161,36 @@ The [`handler`](../building-modules/handler.md) refers to the part of the module The `handler` of a module is generally defined in a file called `handler.go` and consists of: - A **switch function** `NewHandler` to route the message to the appropriate `handler` function. This function returns a `handler` function, and is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's router](../core/baseapp.md#routing). Next is an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) - +++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/handler.go#L12-L26 -- **One handler function for each message type defined by the module**. Developers write the message processing logic in these functions. This generally involves doing stateful checks to ensure the message is valid and calling [`keeper`](#keeper)'s methods to update the state. + +++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/handler.go#L12-L26 +- **One handler function for each message type defined by the module**. Developers write the message processing logic in these functions. This generally involves doing stateful checks to ensure the message is valid and calling [`keeper`](#keeper)'s methods to update the state. Handler functions return a result of type `sdk.Result`, which informs the application on whether the message was successfully processed: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/result.go#L15-L40 -### Querier +### gRPC Query Services -[`Queriers`](../building-modules/querier.md) are very similar to `handlers`, except they serve user queries to the state as opposed to processing transactions. A [query](../building-modules/messages-and-queries.md#queries) is initiated from an [interface](#interfaces) by an end-user who provides a `queryRoute` and some `data`. The query is then routed to the correct application's `querier` by `baseapp`'s `handleQueryCustom` method using `queryRoute`: +gRPC query services are introduced in the v0.40 Stargate release. They allow users to query the state using [gRPC](https://grpc.io). They are enabled by default, and can be configued under the `grpc.enable` and `grpc.address` fields inside `app.toml`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/abci.go#L395-L453 +gRPC query services are defined in the module's Protobuf definition, specifically inside `query.proto`. The `query.proto` definition file exposes a single `Query` [Protobuf service](https://developers.google.com/protocol-buffers/docs/proto#services). Each gRPC query endpoint corresponds to a service method, starting with the `rpc` keyword, inside the `Query` service. -The `Querier` of a module is defined in a file called `querier.go`, and consists of: +Protobuf generates a `QueryServer` interface for each module, containing all the service methods. A module's [`keeper`](#keeper) then needs to implement this `QueryServer` interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint. -- A **switch function** `NewQuerier` to route the query to the appropriate `querier` function. This function returns a `querier` function, and is is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's query router](../core/baseapp.md#query-routing). See an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): - +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32 -- **One querier function for each data type defined by the module that needs to be queryable**. Developers write the query processing logic in these functions. This generally involves calling [`keeper`](#keeper)'s methods to query the state and marshalling it to JSON. +Finally, each module should also implement the `RegisterQueryService` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterQueryServer` function provided by the generated Protobuf code. +### Legacy Querier + +Legacy queriers were queriers used before the introduction of Protobuf and gRPC in the SDK. They are present for existing modules, but will be deprecated in a future release of the SDK. If you are developing new modules, gRPC query services should be preferred, and you only need to implement the `LegacyQuerierHandler` interface if you wish to use legacy queriers. + +[`Legacy queriers`](../building-modules/query-services.md#legacy-queriers) are very similar to `handlers`, except they serve user queries to the state as opposed to processing transactions. A [query](../building-modules/messages-and-queries.md#queries) is initiated from an [interface](#application-interface) by an end-user who provides a `queryRoute` and some `data`. The query is then routed to the correct application's `querier` by `baseapp`'s `handleQueryCustom` method using `queryRoute`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/baseapp/abci.go#L388-L418 + +The `Querier` of a module is defined in a file called `keeper/querier.go`, and consists of: + +- A **switch function** `NewQuerier` to route the query to the appropriate `querier` function. This function returns a `querier` function, and is is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's query router](../core/baseapp.md#query-routing). See an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): + +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32 +- **One querier function for each data type defined by the module that needs to be queryable**. Developers write the query processing logic in these functions. This generally involves calling [`keeper`](#keeper)'s methods to query the state and marshalling it to JSON. ### Keeper @@ -184,36 +206,54 @@ The `keeper` type definition generally consists of: Along with the type definition, the next important component of the `keeper.go` file is the `keeper`'s constructor function, `NewKeeper`. This function instantiates a new `keeper` of the type defined above, with a `codec`, store `keys` and potentially references to other modules' `keeper`s as parameters. The `NewKeeper` function is called from the [application's constructor](#constructor-function). The rest of the file defines the `keeper`'s methods, primarily getters and setters. -### Command-Line and REST Interfaces +### Command-Line, gRPC Services and REST Interfaces -Each module defines command-line commands and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. +Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. #### CLI Generally, the [commands related to a module](../building-modules/module-interfaces.md#cli) are defined in a folder called `client/cli` in the module's folder. The CLI divides commands in two category, transactions and queries, defined in `client/cli/tx.go` and `client/cli/query.go` respectively. Both commands are built on top of the [Cobra Library](https://github.com/spf13/cobra): -- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata. -- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. +- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata. +- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. + +#### gRPC + +[gRPC](https://grpc.io) is a modern open source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node. + +Each module can expose gRPC endpoints, called [service methods](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition) and are defined in the [module's Protobuf `query.proto` file](#grpc-query-services). A service method is defined by its name, input arguments and output response. The module then needs to: + +- define a `RegisterGRPCRoutes` method on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. +- for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the `keeper/grpc_query.go` file. -#### REST +#### gRPC-gateway REST Endpoints -The [module's REST interface](../building-modules/module-interfaces.md#rest) lets users generate transactions and query the state through REST calls to the application's [light client daemon](../core/node.md#lcd) (LCD). REST routes are defined in a file `client/rest/rest.go`, which is composed of: +Some external clients may not wish to use gRPC. The SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) documentation to learn more. + +The REST endpoints are defined in the Protobuf files, along with the gRPC services, using Protobuf annotations. Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods. By default, all REST endpoints defined in the SDK have an URL starting with the `/cosmos/` prefix. + +The SDK also provides a development endpoint to generate [Swagger](https://swagger.io/) definition files for these REST endpoints. This endpoint can be enabled inside the `app.toml` config file, under the `api.swagger` key. + +#### Legacy API REST Endpoints + +The [module's Legacy REST interface](../building-modules/module-interfaces.md#legacy-rest) lets users generate transactions and query the state through REST calls to the application's Legacy API Service. REST routes are defined in a file `client/rest/rest.go`, which is composed of: - A `RegisterRoutes` function, which registers each route defined in the file. This function is called from the [main application's interface](#application-interfaces) for each module used within the application. The router used in the SDK is [Gorilla's mux](https://github.com/gorilla/mux). - Custom request type definitions for each query or transaction creation function that needs to be exposed. These custom request types build on the base `request` type of the Cosmos SDK: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/rest/rest.go#L47-L60 + +++ https://github.com/cosmos/cosmos-sdk/blob/d9175200920e96bfa4182b5c8bc46d91b17a28a1/types/rest/rest.go#L62-L76 - One handler function for each request that can be routed to the given module. These functions implement the core logic necessary to serve the request. +These Legacy API endpoints are present in the SDK for backward compatibility purposes and will be removed in the next release. + ## Application Interface [Interfaces](../interfaces/interfaces-intro.md) let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block. -The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application generally has the `-cli` suffix (e.g. `appcli`), and defined in a file called `cmd/appcli/main.go`. The file contains: +The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g. `appd`), and defined in a file called `appd/main.go`. The file contains: -- **A `main()` function**, which is executed to build the `appcli` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appCli`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. -- **Query commands** are added by calling the `queryCmd` function, also defined in `appcli/main.go`. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appcli query [query]` of the CLI. -- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appcli tx [tx]` of the CLI. -- **A `registerRoutes` function**, which is called from the `main()` function when initializing the [application's light-client daemon (LCD)](../core/node.md#lcd) (i.e. `rest-server`). `registerRoutes` calls the `RegisterRoutes` function of each of the application's module, thereby registering the routes of the module to the lcd's router. The LCD can be started by running the following command `appcli rest-server`. +- **A `main()` function**, which is executed to build the `appd` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appd`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. +- **Query commands** are added by calling the `queryCmd` function. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appd query [query]` of the CLI. +- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appd tx [tx]` of the CLI. See an example of an application's main command-line file from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) @@ -221,11 +261,11 @@ See an example of an application's main command-line file from the [nameservice ## Dependencies and Makefile -This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): +This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): +++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18 -For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appcli`](#application-interface). See an example of Makefile from the [nameservice tutorial]() +For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appd`](#application-interface). See an example of Makefile from the [nameservice tutorial]() +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/Makefile diff --git a/docs/basics/tx-lifecycle.md b/docs/basics/tx-lifecycle.md index 227257022640..7e375fc4db45 100644 --- a/docs/basics/tx-lifecycle.md +++ b/docs/basics/tx-lifecycle.md @@ -28,10 +28,11 @@ There are several required and optional flags for transaction creation. The `--f Additionally, there are several [flags](../interfaces/cli.md) users can use to indicate how much they are willing to pay in [fees](./gas-fees.md): -* `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. -* `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. -* `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. -* `--fees` specifies how much in fees the user is willing to pay in total. +- `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. +- `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. +- `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. +- `--fees` specifies how much in fees the user is willing to pay in total. +- `--timeout-height` specifies a block timeout height to prevent the tx from being committed past a certain height. The ultimate value of the fees paid is equal to the gas multiplied by the gas prices. In other words, `fees = ceil(gas * gasPrices)`. Thus, since fees can be calculated using gas prices and vice versa, the users specify only one of the two. @@ -42,7 +43,7 @@ Later, validators decide whether or not to include the transaction in their bloc Users of application `app` can enter the following command into their CLI to generate a transaction to send 1000uatom from a `senderAddress` to a `recipientAddress`. It specifies how much gas they are willing to pay: an automatic estimate scaled up by 1.5 times, with a gas price of 0.025uatom per unit gas. ```bash -appcli tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom +appd tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom ``` #### Other Transaction Creation Methods @@ -60,11 +61,11 @@ Each full-node (running Tendermint) that receives a `Tx` sends an [ABCI message] The full-nodes perform stateless, then stateful checks on `Tx` during `CheckTx`, with the goal to identify and reject an invalid transaction as early on as possible to avoid wasted computation. -***Stateless*** checks do not require nodes to access state - light clients or offline nodes can do +**_Stateless_** checks do not require nodes to access state - light clients or offline nodes can do them - and are thus less computationally expensive. Stateless checks include making sure addresses are not empty, enforcing nonnegative numbers, and other logic specified in the definitions. -***Stateful*** checks validate transactions and messages based on a committed state. Examples +**_Stateful_** checks validate transactions and messages based on a committed state. Examples include checking that the relevant values exist and are able to be transacted with, the address has sufficient funds, and the sender is authorized or has the correct ownership to transact. At any given moment, full-nodes typically have [multiple versions](../core/baseapp.md#volatile-states) @@ -140,39 +141,39 @@ locally, this process yields a single, unambiguous result, since the messages' s explicitly ordered in the block proposal. ``` - ----------------------- + ----------------------- |Receive Block Proposal| - ----------------------- - | + ----------------------- + | + v + ----------------------- + | BeginBlock | + ----------------------- + | v - ----------------------- - | BeginBlock | - ----------------------- - | - v - ----------------------- - | DeliverTx(tx0) | - | DeliverTx(tx1) | - | DeliverTx(tx2) | - | DeliverTx(tx3) | - | . | + ----------------------- + | DeliverTx(tx0) | + | DeliverTx(tx1) | + | DeliverTx(tx2) | + | DeliverTx(tx3) | + | . | | . | | . | - ----------------------- - | - v ----------------------- - | EndBlock | + | + v ----------------------- - | - v + | EndBlock | ----------------------- - | Consensus | + | + v ----------------------- - | - v + | Consensus | + ----------------------- + | + v ----------------------- - | Commit | + | Commit | ----------------------- ``` @@ -184,30 +185,29 @@ to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx [`runTx`](../core/baseapp.md#runtx) function in deliver mode instead of check mode. Instead of using their `checkState`, full-nodes use `deliverState`: -* **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. -Nodes first unmarshal the transaction, then call `runTx` in `runTxModeDeliver`, which is very -similar to `CheckTx` but also executes and writes state changes. +- **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. + Nodes first unmarshal the transaction, using the [`TxConfig`](./app-anatomy#register-codec) defined in the app, then call `runTx` in `runTxModeDeliver`, which is very similar to `CheckTx` but also executes and writes state changes. -* **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check -happens because they may not have seen the same transactions during the addition to Mempool stage\ -and a malicious proposer may have included invalid ones. One difference here is that the -`AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local -to each node - differing values across nodes would yield nondeterministic results. +- **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check + happens because they may not have seen the same transactions during the addition to Mempool stage\ + and a malicious proposer may have included invalid ones. One difference here is that the + `AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local + to each node - differing values across nodes would yield nondeterministic results. -* **Route and Handler:** While `CheckTx` would have exited, `DeliverTx` continues to run -[`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction. -Since the transaction may have messages from different modules, `baseapp` needs to know which module -to find the appropriate Handler. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to -retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module. +- **Route and Handler:** While `CheckTx` would have exited, `DeliverTx` continues to run + [`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction. + Since the transaction may have messages from different modules, `baseapp` needs to know which module + to find the appropriate Handler. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to + retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module. -* **Handler:** The `handler`, a step up from `AnteHandler`, is responsible for executing each -message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined -within a `Msg`'s module and writes to the appropriate stores within the module. +- **Handler:** The `handler`, a step up from `AnteHandler`, is responsible for executing each + message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined + within a `Msg`'s module and writes to the appropriate stores within the module. -* **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much -gas is being used; if execution completes, `GasUsed` is set and returned in the -`abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes -wrong, a deferred function at the end appropriately errors or panics. +- **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much + gas is being used; if execution completes, `GasUsed` is set and returned in the + `abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes + wrong, a deferred function at the end appropriately errors or panics. If there are any failed state changes resulting from a `Tx` being invalid or `GasMeter` running out, the transaction processing terminates and any state changes are reverted. Invalid transactions in a @@ -219,14 +219,14 @@ The final step is for nodes to commit the block and state changes. Validator nod perform the previous step of executing state transitions in order to validate the transactions, then sign the block to confirm it. Full nodes that are not validators do not participate in consensus - i.e. they cannot vote - but listen for votes to understand whether or -not they should commit the state changes. +not they should commit the state changes. -When they receive enough validator votes (2/3+ *precommits* weighted by voting power), full nodes commit to a new block to be added to the blockchain and +When they receive enough validator votes (2/3+ _precommits_ weighted by voting power), full nodes commit to a new block to be added to the blockchain and finalize the state transitions in the application layer. A new state root is generated to serve as a merkle proof for the state transitions. Applications use the [`Commit`](../core/baseapp.md#commit) ABCI method inherited from [Baseapp](../core/baseapp.md); it syncs all the state transitions by writing the `deliverState` into the application's internal state. As soon as the state changes are -committed, `checkState` start afresh from the most recently committed state and `deliverState` +committed, `checkState` start afresh from the most recently committed state and `deliverState` resets to `nil` in order to be consistent and reflect the changes. Note that not all blocks have the same number of transactions and it is possible for consensus to diff --git a/docs/building-modules/README.md b/docs/building-modules/README.md index 12dd140f89ad..43b8638ca96b 100644 --- a/docs/building-modules/README.md +++ b/docs/building-modules/README.md @@ -12,7 +12,7 @@ This repository contains documentation on concepts developers need to know in or 2. [`AppModule` Interface and Module Manager](./module-manager.md) 3. [Messages and Queries](./messages-and-queries.md) 4. [`Handler`s - Processing Messages](./handler.md) -5. [`Querier`s - Processing Queries](./querier.md) +5. [Query Services - Processing Queries](./query-services.md) 6. [BeginBlocker and EndBlocker](./beginblock-endblock.md) 7. [`Keeper`s](./keeper.md) 8. [Invariants](./invariants.md) diff --git a/docs/building-modules/beginblock-endblock.md b/docs/building-modules/beginblock-endblock.md index fc9c93302660..b4f3abaff971 100644 --- a/docs/building-modules/beginblock-endblock.md +++ b/docs/building-modules/beginblock-endblock.md @@ -28,11 +28,11 @@ It is possible for developers to defined the order of execution between the `Beg See an example implementation of `BeginBlocker` from the `distr` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/distribution/abci.go#L10-L32 ++++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/distribution/abci.go#L14-L38 and an example implementation of `EndBlocker` from the `staking` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/handler.go#L44-L96 ++++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/staking/abci.go#L22-L27 ## Next {hide} diff --git a/docs/building-modules/errors.md b/docs/building-modules/errors.md index 5e3c31bca0d8..3b6d90535e45 100644 --- a/docs/building-modules/errors.md +++ b/docs/building-modules/errors.md @@ -38,7 +38,7 @@ execution. Example: -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.38.1/x/distribution/keeper/querier.go#L62-L80 ++++ https://github.com/cosmos/cosmos-sdk/blob/b2d48a9e815fe534a7faeec6ca2adb0874252b81/x/bank/keeper/keeper.go#L85-L122 Regardless if an error is wrapped or not, the SDK's `errors` package provides an API to determine if an error is of a particular kind via `Is`. diff --git a/docs/building-modules/genesis.md b/docs/building-modules/genesis.md index a95064dc0369..7314442969a2 100644 --- a/docs/building-modules/genesis.md +++ b/docs/building-modules/genesis.md @@ -13,25 +13,25 @@ Modules generally handle a subset of the state and, as such, they need to define ## Type Definition -The subset of the genesis state defined from a given module is generally defined in a `./internal/types/genesis.go` file, along with the `DefaultGenesis` and `ValidateGenesis` methods. The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. +The subset of the genesis state defined from a given module is generally defined in a `genesis.proto` file ([more info](../core/encoding.md#gogoproto) on how to define protobuf messages). The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. -See an example of `GenesisState` type definition from the nameservice tutorial +See an example of `GenesisState` protobuf message definition from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L10-L12 ++++ https://github.com/cosmos/cosmos-sdk/blob/a9547b54ffac9729fe1393651126ddfc0d236cff/proto/cosmos/auth/v1beta1/genesis.proto Next we present the main genesis-related methods that need to be implemented by module developers in order for their module to be used in Cosmos SDK applications. ### `DefaultGenesis` -The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `nameservice` module: +The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L33-L37 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L48-L52 ### `ValidateGenesis` -The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `nameservice` module: +The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L18-L31 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/types/genesis.go#L57-L70 ## Other Genesis Methods @@ -43,18 +43,18 @@ The `InitGenesis` method is executed during [`InitChain`](../core/baseapp.md#ini The [module manager](./module-manager.md#manager) of the application is responsible for calling the `InitGenesis` method of each of the application's modules, in order. This order is set by the application developer via the manager's `SetOrderGenesisMethod`, which is called in the [application's constructor function](../basics/app-anatomy.md#constructor-function) -See an example of `InitGenesis` from the nameservice tutorial +See an example of `InitGenesis` from the `auth` module -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L39-L44 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L13-L28 ### `ExportGenesis` The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork. -See an example of `ExportGenesis` from the nameservice tutorial. +See an example of `ExportGenesis` from the `auth` module. -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L46-L57 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L31-L42 ## Next {hide} -Learn about [modules interfaces](#module-interfaces.md) {hide} \ No newline at end of file +Learn about [modules interfaces](module-interfaces.md) {hide} \ No newline at end of file diff --git a/docs/building-modules/handler.md b/docs/building-modules/handler.md index 88f114f390bd..45e9a4c11944 100644 --- a/docs/building-modules/handler.md +++ b/docs/building-modules/handler.md @@ -21,8 +21,8 @@ Let us break it down: - The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed. - The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state. -- The [`Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler`, [`gas`](../basics/gas-fees.md) consumption and [`events`](../core/events.md). - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 +- The [`*Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler` and [`events`](../core/events.md). + +++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95 ## Implementation of a module `handler`s @@ -37,10 +37,10 @@ func NewHandler(keeper Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case MsgType1: + case *MsgType1: return handleMsgType1(ctx, keeper, msg) - case MsgType2: + case *MsgType2: return handleMsgType2(ctx, keeper, msg) default: @@ -69,16 +69,28 @@ ctx.EventManager().EmitEvent( These `events` are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about `events`. -Finally, the `handler` function returns a `sdk.Result` which contains the aforementioned `events` and an optional `Data` field. +Finally, the `handler` function returns a `*sdk.Result` which contains the aforementioned `events` and an optional `Data` field. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95 -Next is an example of how to return a `Result` from the `gov` module: +Next is an example of how to return a `*Result` from the `gov` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/gov/handler.go#L59-L62 ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go#L67-L70 -For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/handler.go) from the nameservice tutorial. +For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go) from the `gov` module. + +The `handler` can then be registered from [`AppModule.Route()`](./module-manager.md#appmodule) as shown in the example below: + ++++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146 + +## Telemetry + +New [telemetry metrics](../core/telemetry.md) can be created from the `handler` when handling messages for instance. + +This is an example from the `auth` module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/vesting/handler.go#L68-L80 ## Next {hide} -Learn about [queriers](./querier.md) {hide} +Learn about [query services](./query-services.md) {hide} diff --git a/docs/building-modules/intro.md b/docs/building-modules/intro.md index 99a9a57caafd..7b3a4c3faa5e 100644 --- a/docs/building-modules/intro.md +++ b/docs/building-modules/intro.md @@ -81,7 +81,7 @@ Modules are by convention defined in the `.x/` subfolder (e.g. the `bank` module - Custom [`message` types](./messages-and-queries.md#messages) to trigger state-transitions. - A [`handler`](./handler.md) used to process messages when they are routed to the module by [`baseapp`](../core/baseapp.md#message-routing). - A [`keeper`](./keeper.md), used to access the module's store(s) and update the state. -- A [`querier`](./querier.md), used to process user queries when they are routed to the module by [`baseapp`](../core/baseapp.md#query-routing). +- A [query service](./query-services.md), used to process user queries when they are routed to the module by [`baseapp`](../core/baseapp.md#query-routing). - Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module. In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./module-manager.md). @@ -91,4 +91,3 @@ Please refer to the [structure document](./structure.md) to learn about the reco ## Next {hide} Read more on the [`AppModule` interface and the `module manager`](./module-manager.md) {hide} - diff --git a/docs/building-modules/invariants.md b/docs/building-modules/invariants.md index 576baa796b96..d21100cb24b5 100644 --- a/docs/building-modules/invariants.md +++ b/docs/building-modules/invariants.md @@ -18,7 +18,7 @@ An `Invariant` is a function that checks for a particular invariant within a mod where the `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check. -In practice, each module implements `Invariant`s in a `./internal/keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: +In practice, each module implements `Invariant`s in a `./keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: ```go // Example for an Invariant that checks balance-related invariants @@ -74,7 +74,7 @@ At its core, the `InvariantRegistry` is defined in the SDK as an interface: Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/crisis/internal/keeper/keeper.go#L45-L49 ++++ https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/keeper/keeper.go#L50-L54 The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../basics/app-anatomy.md#constructor-function). diff --git a/docs/building-modules/keeper.md b/docs/building-modules/keeper.md index ee373d8b567f..5c57faef1fe5 100644 --- a/docs/building-modules/keeper.md +++ b/docs/building-modules/keeper.md @@ -20,7 +20,7 @@ The core idea behind the object-capabilities approach is to only reveal what is ## Type Definition -`keeper`s are generally implemented in a `internal/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: +`keeper`s are generally implemented in a `/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: ```go type Keeper struct { @@ -32,15 +32,15 @@ type Keeper struct { } ``` -For example, here is the [type definition of the `keeper` from the nameservice tutorial: +For example, here is the type definition of the `keeper` from the `staking` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/keeper.go#L10-L17 ++++ https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go#L23-L33 Let us go through the different parameters: -- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `internal/types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. +- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. - `storeKey`s grant access to the store(s) of the [multistore](../core/store.md) managed by the module. They should always remain unexposed to external modules. -- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`. +- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`, that can be any of `codec.BinaryMarshaler`,`codec.JSONMarshaler` or `codec.Marshaler` based on your requirements. It can be either a proto or amino codec as long as they implement these interfaces. Of course, it is possible to define different types of internal `keeper`s for the same module (e.g. a read-only `keeper`). Each type of `keeper` comes with its own constructor function, which is called from the [application's constructor function](../basics/app-anatomy.md). This is where `keeper`s are instantiated, and where developers make sure to pass correct instances of modules' `keeper`s to other modules that require it. @@ -56,7 +56,7 @@ func (k Keeper) Get(ctx sdk.Context, key string) returnType and go through the following steps: -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`. +1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. 2. If it exists, get the `[]byte` value stored at location `[]byte(key)` using the `Get(key []byte)` method of the store. 3. Unmarshall the retrieved value from `[]byte` to `returnType` using the codec `cdc`. Return the value. @@ -68,11 +68,17 @@ func (k Keeper) Set(ctx sdk.Context, key string, value valueType) and go through the following steps: -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`. -2. Marhsall `value` to `[]byte` using the codec `cdc`. +1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. +2. Marshal `value` to `[]byte` using the codec `cdc`. 3. Set the encoded value in the store at location `key` using the `Set(key []byte, value []byte)` method of the store. -For more, see an example of `keeper`'s [methods implementation from the nameservice tutorial](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/keeper.go). +For more, see an example of `keeper`'s [methods implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go). + +The [module `KVStore`](../core/store.md#kvstore-and-commitkvstore-interfaces) also provides an `Iterator()` method which returns an `Iterator` object to iterate over a domain of keys. + +This is an example from the `auth` module to iterate accounts: + ++++ https://github.com/cosmos/cosmos-sdk/blob/bf8809ef9840b4f5369887a38d8345e2380a567f/x/auth/keeper/account.go#L70-L83 ## Next {hide} diff --git a/docs/building-modules/messages-and-queries.md b/docs/building-modules/messages-and-queries.md index 30ea4f344d9f..71390449812e 100644 --- a/docs/building-modules/messages-and-queries.md +++ b/docs/building-modules/messages-and-queries.md @@ -16,39 +16,47 @@ order: 3 When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`baseapp`](../core/baseapp.md). Then, each `message` contained in the transaction is extracted and routed to the appropriate module via `baseapp`'s `router` so that it can be processed by the module's [`handler`](./handler.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md). -Defining `message`s is the responsibility of module developers. Typically, they are defined in a `./internal/types/msgs.go` file inside the module's folder. The `message`'s type definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said `message`. +Defining `message`s is the responsibility of module developers. Typically, they are defined as protobuf messages in a `proto/` directory (see more info about [conventions and naming](../core/encoding.md#faq)). The `message`'s definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said `message`. -```go -// Example of a message type definition +Here's an example of a protobuf message definition: -type MsgSubmitProposal struct { - Content Content `json:"content" yaml:"content"` - InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"` - Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/gov/v1beta1/tx.proto#L15-L27 The `Msg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./module-interfaces.md). `message`s also need to implement the [`Msg`] interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L7-L29 ++++ https://github.com/cosmos/cosmos-sdk/blob/4a1b2fba43b1052ca162b3a1e0b6db6db9c26656/types/tx_msg.go#L10-L33 -It contains the following methods: +It extends `proto.Message` and contains the following methods: - `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name. - `Type() string`: Type of the message, used primarly in [events](../core/events.md). This should return a message-specific `string`, typically the denomination of the message itself. -- `ValidateBasic() Error`: This method is called by `baseapp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer). +- `ValidateBasic() error`: This method is called by `baseapp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer). - `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature. - `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method. -See an example implementation of a `message` from the `nameservice` module: +See an example implementation of a `message` from the `gov` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/types/msgs.go#L10-L51 ++++ https://github.com/cosmos/cosmos-sdk/blob/master/x/gov/types/msgs.go#L94-L136 ## Queries -A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `baseapp`'s `queryrouter` so that it can be processed by the module's [`querier`](./querier.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md). +A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `baseapp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md). + +### gRPC Queries + +Starting from v0.40, the prefered way to define queries is by using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`. + +Here's an example of such a `Query` service definition: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/auth/v1beta1/query.proto#L12-L23 + +As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). -Contrary to `message`s, there is usually no specific `query` object defined by module developers. Instead, the SDK takes the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following: +A `RegisterQueryServer` method is also generated and should be used to register the module's query server in `RegisterQueryService` method from the [`AppModule` interface](./module-manager.md#appmodule). + +### Legacy Queries + +Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific `query` object defined by module developers, contrary to `message`s. Instead, the SDK took the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following: ``` queryCategory/queryRoute/queryType/arg1/arg2/... @@ -58,18 +66,24 @@ where: - `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `baseapp`'s [`Query` method](../core/baseapp.md#query). - `queryRoute` is used by `baseapp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module. -- `queryType` is used by the module's [`querier`](./querier.md) to map the `query` to the appropriate `querier function` within the module. +- `queryType` is used by the module's [`querier`](./query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module. - `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`. The `path` for each `query` must be defined by the module developer in the module's [command-line interface file](./module-interfaces.md#query-commands).Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable: -- A [`querier`](./querier.md), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing). +- A [`querier`](./query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing). - [Query commands](./module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified. -- `query` return types. Typically defined in a file `internal/types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). +- `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). + +### Store Queries + +Store queries query directly for store keys. They use `clientCtx.QueryABCI(req abci.RequestQuery)` to return the full `abci.ResponseQuery` with inclusion Merkle proofs. + +See following examples: -See an example of `query` return types from the `nameservice` module: ++++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/x/ibc/core/client/query.go#L36-L46 -+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/types/querier.go#L5-L21 ++++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/baseapp/abci.go#L722-L749 ## Next {hide} diff --git a/docs/building-modules/module-interfaces.md b/docs/building-modules/module-interfaces.md index d2761248d94b..1c8dcdbce26e 100644 --- a/docs/building-modules/module-interfaces.md +++ b/docs/building-modules/module-interfaces.md @@ -16,49 +16,47 @@ One of the main interfaces for an application is the [command-line interface](.. ### Transaction Commands -[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions prefixed with `GetCmd` and include the name of the command. +[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions and include the name of the command. -Here is an example from the `nameservice` module: +Here is an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/tx.go#L33-L58 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/client/cli/tx_sign.go#L160-L194 -This getter function creates the command for the Buy Name transaction. It does the following: +This getter function creates the command for the `Sign` transaction. It does the following: -- **Construct the command:** Read the [Cobra Documentation](https://github.com/spf13/cobra) for details on how to create commands. +- **Construct the command:** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) for details on how to create commands. - **Use:** Specifies the format of a command-line entry users should type in order to invoke this command. In this case, the user uses `buy-name` as the name of the transaction command and provides the `name` the user wishes to buy and the `amount` the user is willing to pay. - **Args:** The number of arguments the user provides, in this case exactly two: `name` and `amount`. - **Short and Long:** A description for the function is provided here. A `Short` description is expected, and `Long` can be used to provide a more detailed description when a user uses the `--help` flag to ask for more information. - **RunE:** Defines a function that can return an error, called when the command is executed. Using `Run` would do the same thing, but would not allow for errors to be returned. - **`RunE` Function Body:** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new transaction that is ready to be relayed to nodes. - - The function should first initialize a [`TxBuilder`](../core/transactions.md#txbuilder) with the application `codec`'s `TxEncoder`, as well as a new [`Context`](../interfaces/query-lifecycle.md#context) with the `codec` and `AccountDecoder`. These contexts contain all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation). - - If applicable, the command's arguments are parsed. Here, the `amount` given by the user is parsed into a denomination of `coins`. - - If applicable, the `Context` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `cliCtx.getFromAddress()`. + - The function should first get the `clientCtx` with `client.GetClientContextFromCmd(cmd)` and `client.ReadTxCommandFlags(clientCtx, cmd.Flags())`. This context contains all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation). + - If applicable, the command's arguments are parsed. + - If applicable, the `Context` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `clientCtx.GetFromAddress()`. - A [message](./messages-and-queries.md) is created using all parameters parsed from the command arguments and `Context`. The constructor function of the specific message type is called directly. It is good practice to call `ValidateBasic()` on the newly created message to run a sanity check and check for invalid arguments. - Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `GenerateOrBroadcastMsgs()`. -- **Flags.** Add any [flags](#flags) to the command. No flags were specified here, but all transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These _persistent_ [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands. +- **Flags.** Add any [flags](#flags) to the command. All transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These _persistent_ [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands. -Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign` and `MultiSign` commands. +Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign`, `MultiSign`, `ValidateSignatures` and `SignBatch` commands. -+++ https://github.com/cosmos/cosmos-sdk/blob/67f6b021180c7ef0bcf25b6597a629aca27766b8/x/auth/client/cli/tx.go#L11-L25 ++++ https://github.com/cosmos/cosmos-sdk/blob/351192aa0b52a42b66ff06e81cfa7a9e26667a7f/x/auth/client/cli/tx.go#L10-L26 An application using this module likely adds `auth` module commands to its root `TxCmd` command by calling `txCmd.AddCommand(authModuleClient.GetTxCmd())`. ### Query Commands -[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions and have the prefix `GetCmdQuery`. Here is an example of a query command from the `nameservice` module: +[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions. Here is an example of a query command from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/query.go#L52-L73 ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/client/cli/query.go#L76-L108 -This query returns the address that owns a particular name. The getter function does the following: +This query returns the account at a given address. The getter function does the following: -- **`codec` and `queryRoute`.** In addition to taking in the application `codec`, query command getters also take a `queryRoute` used to construct a path [Baseapp](../core/baseapp.md#query-routing) uses to route the query in the application. -- **Construct the command.** Read the [Cobra Documentation](https://github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `whois` and provide the `name` they are querying for as the only argument. +- **Construct the command.** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `account` and provide the `address` they are querying for as the only argument. - **`RunE`.** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new query that is ready to be relayed to nodes. - - The function should first initialize a new [`Context`](../interfaces/query-lifecycle.md#context) with the application `codec`. + - The function should first initialize a new client [`Context`](../interfaces/query-lifecycle.md#context) as described in the [previous section](#transaction-commands) - If applicable, the `Context` is used to retrieve any parameters (e.g. the query originator's address to be used in the query) and marshal them with the query parameter type, in preparation to be relayed to a node. There are no `Context` parameters in this case because the query does not involve any information about the user. - - The `queryRoute` is used to construct a `path` [`baseapp`](../core/baseapp.md) will use to route the query to the appropriate [querier](./querier.md). The expected format for a query `path` is "queryCategory/queryRoute/queryType/arg1/arg2/...", where `queryCategory` can be `p2p`, `store`, `app`, or `custom`, `queryRoute` is the name of the module, and `queryType` is the name of the query type defined within the module. [`baseapp`](../core/baseapp.md) can handle each type of `queryCategory` by routing it to a module querier or retrieving results directly from stores and functions for querying peer nodes. Module queries are `custom` type queries (some SDK modules have exceptions, such as `auth` and `gov` module queries). - - The `Context` `QueryWithData()` function is used to relay the query to a node and retrieve the response. It requires the `path`. It returns the result and height of the query upon success or an error if the query fails. - - The `codec` is used to nmarshal the response and the `Context` is used to print the output back to the user. + - A new `queryClient` should be initialized using `NewQueryClient(clientCtx)`, this method being generated from `query.proto`. Then it can be used to call the appropriate [query](./messages-and-queries.md#grpc-queries). + - The `clientCtx.PrintOutput` method is used to print the output back to the user. - **Flags.** Add any [flags](#flags) to the command. Finally, the module also needs a `GetQueryCmd`, which aggregates all of the query commands of the module. Application developers wishing to include the module's queries will call this function to add them as subcommands in their CLI. Its structure is identical to the `GetTxCmd` command shown above. @@ -71,9 +69,9 @@ The flags for a module are typically found in a `flags.go` file in the `./x/modu For full details on flags, visit the [Cobra Documentation](https://github.com/spf13/cobra). -For example, the SDK `./client/flags` package includes a `PostCommands()` function that adds necessary flags to transaction commands, such as the `from` flag to indicate which address the transaction originates from. +For example, the SDK `./client/flags` package includes a `AddTxFlagsToCmd(cmd *cobra.Command)` function that adds necessary flags to a transaction command, such as the `from` flag to indicate which address the transaction originates from. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/flags/flags.go#L85-L116 ++++ https://github.com/cosmos/cosmos-sdk/blob/cfb5fc03e5092395403d10156c0ee96e6ff1ddbe/client/flags/flags.go#L85-L112 Here is an example of how to add a flag using the `from` flag from this function. @@ -89,11 +87,54 @@ A flag can be marked as _required_ so that an error is automatically thrown if t cmd.MarkFlagRequired(FlagFrom) ``` -Since `PostCommands()` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). For a full list of what flags are included in the `PostCommands()` function, including which are required inputs from users, see the CLI documentation [here](../interfaces/cli.md#transaction-flags). +Since `AddTxFlagsToCmd(cmd *cobra.Command)` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). -## REST +Similarly, there is a `AddQueryFlagsToCmd(cmd *cobra.Command)` to add common flags to a module query command. -Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers will also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers. The module developer's responsibility is to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder. +## gRPC + +[gRPC](https://grpc.io/) is the prefered way for external clients like wallets and exchanges to interact with a node. + +In addition to providing an ABCI query pathway, modules [custom queries](./messages-and-queries.md#grpc-queries) can provide a GRPC proxy server that routes requests in the GRPC protocol to ABCI query requests under the hood. + +In order to do that, module should implement `RegisterGRPCRoutes(clientCtx client.Context, mux *runtime.ServeMux)` on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. + +Here's an example from the `auth` module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L69-L72 + +## gRPC-gateway REST + +Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers can also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers. + +[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) translates REST calls into gRPC calls, which might be useful for clients that do not use gRPC. + +Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods, such as in the example below from the `auth` module: + +```proto +// Query defines the gRPC querier service. +service Query{ + // Account returns account details based on address. + rpc Account (QueryAccountRequest) returns (QueryAccountResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; + } + + // Params queries all parameters. + rpc Params (QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/params"; + } +} +``` + +gRPC gateway is started in-process along with the application and Tendermint. It can be enabled or disabled by setting gRPC Configuration `enable` in `app.toml`. + +The SDK provides a command for generating [Swagger](https://swagger.io/) documentation (`protoc-gen-swagger`). Setting `swagger` in `app.toml` defines if swagger documentation should be automatically registered. + +## Legacy REST + +Legacy REST endpoints will be deprecated. But developers may choose to keep using legacy REST endpoints for backward compatibility, although the recommended way is to use [gRPC](#grpc) and [gRPC-gateway](#grpc-gateway-rest). + +With this implementation, module developers need to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder. To support HTTP requests, the module developer needs to define possible request types, how to handle them, and provide a way to register them with a provided router. @@ -101,11 +142,11 @@ To support HTTP requests, the module developer needs to define possible request Request types, which define structured interactions from users, must be defined for all _transaction_ requests. Users using this method to interact with an application will send HTTP Requests with the required fields in order to trigger state changes in the application. Conventionally, each request is named with the suffix `Req`, e.g. `SendReq` for a Send transaction. Each struct should include a base request [`baseReq`](../interfaces/rest.md#basereq), the name of the transaction, and all the arguments the user must provide for the transaction. -Here is an example of a request to buy a name from the `nameservice` module: +Here is an example of a request to send coins from the `bank` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L14-L19 ++++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L15-L19 -The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the arguments `Name` and `Amount` fields in the body and `Buyer` will be provided by the user's address. +The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the argument `Amount` fields in the body. #### BaseReq @@ -116,6 +157,7 @@ The `BaseReq` includes basic information that every request needs to have, simil - `ChainID` specifies the unique identifier of the blockchain the transaction pertains to. - `AccountNumber` is an identifier for the account. - `Sequence`is the value of a counter measuring how many transactions have been sent from the account. It is used to prevent replay attacks. +- `TimeoutHeight` allows a transaction to be rejected if it's committed at a height greater than the timeout. - `Gas` refers to how much [gas](../basics/gas-fees.md), which represents computational resources, Tx consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing auto as the value for `Gas`. - `GasAdjustment` can be used to scale gas up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. - `GasPrices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, --gas-prices=0.025uatom, 0.025upho means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. @@ -124,17 +166,17 @@ The `BaseReq` includes basic information that every request needs to have, simil ### Request Handlers -Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the application's `codec` and the [`Context`](../interfaces/query-lifecycle.md#context) created in the user interaction. +Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the [client `Context`](../interfaces/query-lifecycle.md#context). -Here is an example of a request handler for the nameservice module `buyNameReq` request (the same one shown above): +Here is an example of a request handler for the `bank` module `SendReq` request (the same one shown above): -+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L21-L57 ++++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L21-L51 The request handler can be broken down as follows: -- **Parse Request:** The request handler first attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Next, it attempts to parse the arguments `Buyer` and `Amount` to the types `AccountAddress` and `Coins` respectively. -- **Message:** Then, a [message](./messages-and-queries.md) of the type `MsgBuyName` (defined by the module developer to trigger the state changes for this transaction) is created from the values and another sanity check, `ValidateBasic` is run on it. -- **Generate Transaction:** Finally, the HTTP `ResponseWriter`, application [`codec`](../core/encoding.md), [`Context`](../interfaces/query-lifecycle.md#context), request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGenerateStdTxResponse` to further process the request. +- **Parse Request:** First, it tries to parse the argument `address` into a `AccountAddress`. Then, the request handler attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Finally, it attempts to parse `BaseReq.From` to the type `AccountAddress`. +- **Message:** Then, a [message](./messages-and-queries.md#messages) of the type `MsgSend` (defined by the module developer to trigger the state changes for this transaction) is created from the values. +- **Generate Transaction:** Finally, the HTTP `ResponseWriter`, client `Context`, request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGeneratedTxResponse` to further process the request. To read more about how a transaction is generated, visit the transactions documentation [here](../core/transactions.md#transaction-generation). diff --git a/docs/building-modules/module-manager.md b/docs/building-modules/module-manager.md index dad35a9da3aa..1c034bde7157 100644 --- a/docs/building-modules/module-manager.md +++ b/docs/building-modules/module-manager.md @@ -29,15 +29,17 @@ are only used for genesis can take advantage of the `Module` patterns without ha The `AppModuleBasic` interface defines the independent methods modules need to implement. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L46-L59 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L49-L63 Let us go through the methods: - `Name()`: Returns the name of the module as a `string`. -- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marhsal and unmarshal structs to/from `[]byte` in order to persist them in the moduel's `KVStore`. -- `DefaultGenesis()`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. -- `ValidateGenesis(json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer. +- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marshal and unmarshal structs to/from `[]byte` in order to persist them in the module's `KVStore`. +- `RegisterInterfaces(codectypes.InterfaceRegistry)`: Registers a module's interface types and their concrete implementations as `proto.Message`. +- `DefaultGenesis(codec.JSONMarshaler)`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. +- `ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer. - `RegisterRESTRoutes(client.Context, *mux.Router)`: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See [../interfaces/rest.md] for more. +- `RegisterGRPCRoutes(client.Context, *runtime.ServeMux)`: Registers gRPC routes for the module. - `GetTxCmd()`: Returns the root [`Tx` command](./module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./messages-and-queries.md#queries) defined in the module. - `GetQueryCmd()`: Return the root [`query` command](./module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module. @@ -47,20 +49,20 @@ All the `AppModuleBasic` of an application are managed by the [`BasicManager`](# The `AppModuleGenesis` interface is a simple embedding of the `AppModuleBasic` interface with two added methods. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L126-L131 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L152-L158 Let us go through the two added methods: -- `InitGenesis(sdk.Context, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). -- `ExportGenesis(sdk.Context)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. +- `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). +- `ExportGenesis(sdk.Context, codec.JSONMarshaler)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. -It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, json.RawMessage)` and `ExportGenesis(sdk.Context)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface. +It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)` and `ExportGenesis(sdk.Context, codec.JSONMarshaler)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface. ### `AppModule` The `AppModule` interface defines the inter-dependent methods modules need to implement. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L133-L149 ++++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/types/module/module.go#L160-L182 `AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter. @@ -68,8 +70,9 @@ Let us go through the methods of `AppModule`: - `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./invariants.md) of the module. If the invariants deviates from its predicted value, the [`InvariantRegistry`](./invariants.md#registry) triggers appropriate logic (most often the chain will be halted). - `Route()`: Returns the route for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`baseapp`](../core/baseapp.md#message-routing). -- `QuerierRoute()`: Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing). -- `NewQuerierHandler()`: Returns a [`querier`](./querier.md) given the query `path`, in order to process the `query`. +- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing). +- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`. +- `RegisterQueryService(grpc.Server)`: Allows a module to register a gRPC query service. - `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module. - `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the beginning of each block for this module. @@ -103,15 +106,17 @@ Module managers are used to manage collections of `AppModuleBasic` and `AppModul The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L61-L63 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L65-L66 It implements the following methods: - `NewBasicManager(modules ...AppModuleBasic)`: Constructor function. It takes a list of the application's `AppModuleBasic` and builds a new `BasicManager`. This function is generally called in the `init()` function of [`app.go`](../basics/app-anatomy.md#core-application-file) to quickly initialize the independent elements of the application's modules (click [here](https://github.com/cosmos/gaia/blob/master/app/app.go#L59-L74) to see an example). -- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec`s](../core/encoding.md) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor). -- `DefaultGenesis()`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis()`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. -- `ValidateGenesis(genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis()`](./genesis.md#validategenesis) function of each module. +- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec.LegacyAmino`s](../core/encoding.md#amino) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor). +- `RegisterInterfaces(registry codectypes.InterfaceRegistry)`: Registers interface types and implementations of each of the application's `AppModuleBasic`. +- `DefaultGenesis(cdc codec.JSONMarshaler)`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis(cdc codec.JSONMarshaler)`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. +- `ValidateGenesis(cdc codec.JSONMarshaler, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`](./genesis.md#validategenesis) function of each module. - `RegisterRESTRoutes(ctx client.Context, rtr *mux.Router)`: Registers REST routes for modules by calling the [`RegisterRESTRoutes`](./module-interfaces.md#register-routes) function of each module. This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). +- `RegisterGRPCRoutes(clientCtx client.Context, rtr *runtime.ServeMux)`: Registers gRPC routes for modules. - `AddTxCommands(rootTxCmd *cobra.Command)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../interfaces/cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). - `AddQueryCommands(rootQueryCmd *cobra.Command)`: Adds modules' query commands to the application's [`rootQueryCommand`](../interfaces/cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). @@ -119,7 +124,7 @@ It implements the following methods: The `Manager` is a structure that holds all the `AppModule` of an application, and defines the order of execution between several key components of these modules: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L190-L198 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L223-L231 The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods: @@ -129,12 +134,17 @@ The module manager is used throughout the application whenever an action on a co - `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). - `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function). - `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./invariants.md) of each module. -- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./querier.md). -- `InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. -- `ExportGenesis(ctx sdk.Context)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. +- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./query-services.md#legacy-queriers). +- `RegisterQueryServices(grpcRouter grpc.Server)`: Registers all module gRPC query services. +- `InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. +- `ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. - `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`baseapp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events. - `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`baseapp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any). +Here's an example of a concrete integration within an application: + ++++ https://github.com/cosmos/cosmos-sdk/blob/2323f1ac0e9a69a0da6b43693061036134193464/simapp/app.go#L315-L362 + ## Next {hide} Learn more about [`message`s and `queries`](./messages-and-queries.md) {hide} diff --git a/docs/building-modules/querier.md b/docs/building-modules/querier.md deleted file mode 100644 index 61eb1c5e10df..000000000000 --- a/docs/building-modules/querier.md +++ /dev/null @@ -1,56 +0,0 @@ - - -# Queriers - -A `Querier` designates a function that processes [`queries`](./messages-and-queries.md#queries). `querier`s are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `baseapp`'s [`Query` method](../core/baseapp.md#query). {synopsis} - -## Pre-requisite Readings - -- [Module Manager](./module-manager.md) {prereq} -- [Messages and Queries](./messages-and-queries.md) {prereq} - -## `Querier` type - -The `querier` type defined in the Cosmos SDK specifies the typical structure of a `querier` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/queryable.go#L6 - -Let us break it down: - -- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information. -- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. -- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. -- The result `res` returned to `baseapp`, marhsalled using the application's [`codec`](../core/encoding.md). - -## Implementation of a module `querier`s - -Module `querier`s are typically implemented in a `./internal/keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: - -```go -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case QueryType1: - return queryType1(ctx, path[1:], req, keeper) - - case QueryType2: - return queryType2(ctx, path[1:], req, keeper) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} -``` - -This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. - -The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result. - -For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/querier.go) from the nameservice tutorial. - -## Next {hide} - -Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/docs/building-modules/query-services.md b/docs/building-modules/query-services.md new file mode 100644 index 000000000000..fa7a6e0bae4f --- /dev/null +++ b/docs/building-modules/query-services.md @@ -0,0 +1,77 @@ + + +# Query Services + +A query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `baseapp`'s [`Query` method](../core/baseapp.md#query). {synopsis} + +## Pre-requisite Readings + +- [Module Manager](./module-manager.md) {prereq} +- [Messages and Queries](./messages-and-queries.md) {prereq} + +## `Querier` type + +The `querier` type defined in the Cosmos SDK will be deprecated in favor of [gRPC Services](#grpc-service). It specifies the typical structure of a `querier` function: + ++++ https://github.com/cosmos/cosmos-sdk/blob/9a183ffbcc0163c8deb71c7fd5f8089a83e58f05/types/queryable.go#L9 + +Let us break it down: + +- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information. +- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. +- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. +- The result `res` returned to `baseapp`, marshalled using the application's [`codec`](../core/encoding.md). + +## Implementation of a module query service + +### gRPC Service + +When defining a Protobuf `Query` service, a `QueryServer` interface is generated for each module with all the service methods: + +```go +type QueryServer interface { + QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) + QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) +} +``` + +These custom queries methods should be implemented by a module's keeper, typically in `./keeper/grpc_query.go`. The first parameter of these methods is a generic `context.Context`, whereas querier methods generally need an instance of `sdk.Context` to read +from the store. Therefore, the SDK provides a function `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided +`context.Context`. + +Here's an example implementation for the bank module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/keeper/grpc_query.go + +### Legacy Queriers + +Module legacy `querier`s are typically implemented in a `./keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: + +```go +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + case QueryType1: + return queryType1(ctx, path[1:], req, keeper) + + case QueryType2: + return queryType2(ctx, path[1:], req, keeper) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + } +} +``` + +This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. + +The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result. + +For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/gov/keeper/querier.go) from the bank module. + +## Next {hide} + +Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/docs/building-modules/structure.md b/docs/building-modules/structure.md index 15e9c4bcede2..cdc2c6983c8c 100644 --- a/docs/building-modules/structure.md +++ b/docs/building-modules/structure.md @@ -27,6 +27,7 @@ x/{module} │   ├── keeper.go │   ├── ... │   └── querier.go +│   └── grpc_query.go ├── types │ ├── codec.go │ ├── errors.go @@ -36,10 +37,12 @@ x/{module} │ ├── keys.go │ ├── msgs.go │ ├── params.go -│ ├── types.pb.go │ ├── types.proto │ ├── ... │ └── querier.go +│ └── {module_name}.pb.go +│ └── query.pb.go +│ └── genesis.pb.go ├── simulation │   ├── decoder.go │   ├── genesis.go diff --git a/docs/core/baseapp.md b/docs/core/baseapp.md index beaba8b5e8b3..c4ea2c9faf08 100644 --- a/docs/core/baseapp.md +++ b/docs/core/baseapp.md @@ -200,7 +200,7 @@ The application's `router` is initilalized with all the routes using the applica ### Query Routing -Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/querier.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query). +Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/query-services.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query). Just like the `router`, the `query router` is initilalized with all the query routes using the application's [module manager](../building-modules/module-manager.md), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor). diff --git a/docs/core/encoding.md b/docs/core/encoding.md index 2e0012407fab..69059b39c6a0 100644 --- a/docs/core/encoding.md +++ b/docs/core/encoding.md @@ -80,7 +80,7 @@ Modules are encouraged to utilize Protobuf encoding for their respective types. Protobuf types can be defined to encode: - state - [`Msg`s](../building-modules/messages-and-queries.md#messages) - - [queries](../building-modules/querier.md) + - [Query services](../building-modules/query-services.md) - [genesis](../building-modules/genesis.md) **Naming and conventions** diff --git a/docs/core/transactions.md b/docs/core/transactions.md index b5ae3bc57fb1..507f47e6b1f0 100644 --- a/docs/core/transactions.md +++ b/docs/core/transactions.md @@ -39,7 +39,7 @@ A transaction is created by an end-user through one of the possible [interfaces] Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line or through HTTP requests. -For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers. +For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#legacy-rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers. When users interact with the application's interfaces, they invoke the underlying modules' handlers or command functions, directly creating messages. diff --git a/docs/interfaces/query-lifecycle.md b/docs/interfaces/query-lifecycle.md index 26fe4df978fe..ae4a2e800b50 100644 --- a/docs/interfaces/query-lifecycle.md +++ b/docs/interfaces/query-lifecycle.md @@ -107,7 +107,7 @@ params := types.NewQueryDelegatorParams(delegatorAddr) #### Query Route Creation -Important to note is that there will never be a "query" object created for `Query`; the SDK actually takes a simpler approach. Instead of an object, all the full-node needs to process a query is its `route` which specifies exactly which module to route the query to and the name of this query type. The `route` will be passed to the application `baseapp`, then module, then [querier](../building-modules/querier.md), and each will understand the `route` and pass it to the appropriate next step. [`baseapp`](../core/baseapp.md#query-routing) will understand this query to be a `custom` query in the module `staking`, and the `staking` module querier supports the type `QueryDelegatorDelegations`. Thus, the route will be `"custom/staking/delegatorDelegations"`. +Important to note is that there will never be a "query" object created for `Query`; the SDK actually takes a simpler approach. Instead of an object, all the full-node needs to process a query is its `route` which specifies exactly which module to route the query to and the name of this query type. The `route` will be passed to the application `baseapp`, then module, then [querier](../building-modules/query-services.md#legacy-queriers), and each will understand the `route` and pass it to the appropriate next step. [`baseapp`](../core/baseapp.md#query-routing) will understand this query to be a `custom` query in the module `staking`, and the `staking` module querier supports the type `QueryDelegatorDelegations`. Thus, the route will be `"custom/staking/delegatorDelegations"`. Here is what the code looks like: @@ -133,9 +133,9 @@ Read more about ABCI Clients and Tendermint RPC in the Tendermint documentation ## Application Query Handling -When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The `queryRoute` is parsed such that the first string must be one of the four options, then the rest of the path is parsed within the subroutines handling each type of query. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [querier](../building-modules/querier.md). +When a query is received by the full-node after it has been relayed from the underlying consensus engine, it is now being handled within an environment that understands application-specific types and has a copy of the state. [`baseapp`](../core/baseapp.md) implements the ABCI [`Query()`](../core/baseapp.md#query) function and handles four different types of queries: `app`, `store`, `p2p`, and `custom`. The `queryRoute` is parsed such that the first string must be one of the four options, then the rest of the path is parsed within the subroutines handling each type of query. The first three types (`app`, `store`, `p2p`) are purely application-level and thus directly handled by `baseapp` or the stores, but the `custom` query type requires `baseapp` to route the query to a module's [query service](../building-modules/query-services.md). -Since `Query` is a custom query type from the `staking` module, `baseapp` first parses the path, then uses the `QueryRouter` to retrieve the corresponding querier, and routes the query to the module. The querier is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about queriers [here](../building-modules/querier.md). +Since `Query` is a custom query type from the `staking` module, `baseapp` first parses the path, then uses the `QueryRouter` to retrieve the corresponding querier, and routes the query to the module. The querier is responsible for recognizing this query, retrieving the appropriate values from the application's stores, and returning a response. Read more about query services [here](../building-modules/query-services.md). Once a result is received from the querier, `baseapp` begins the process of returning a response to the user. diff --git a/docs/interfaces/rest.md b/docs/interfaces/rest.md index 6db69bc8bf81..00e94b1d9a9d 100644 --- a/docs/interfaces/rest.md +++ b/docs/interfaces/rest.md @@ -4,7 +4,7 @@ order: 4 # REST Interface -This document describes how to create a REST interface for an SDK **application**. A separate document for creating a [**module**](../building-modules/intro.md) REST interface can be found [here](#../module-interfaces.md#rest). {synopsis} +This document describes how to create a REST interface for an SDK **application**. A separate document for creating a [**module**](../building-modules/intro.md) REST interface can be found [here](#../module-interfaces.md#legacy-rest). {synopsis} ## Pre-requisite Readings @@ -41,7 +41,7 @@ In order to enable the REST Server in an SDK application, the `rest.ServeCommand ## Registering Routes -To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)). +To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#legacy-rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)). At the bare minimum, a `RegisterRoutes()` function should use the SDK client package `RegisterRoutes()` function to be able to route RPC calls, and instruct the application Module Manager to call `RegisterRESTRoutes()` for all of its modules. This is done in the `main.go` file of the CLI (typically located in `./cmd/appcli/main.go`). diff --git a/docs/spec/SPEC-SPEC.md b/docs/spec/SPEC-SPEC.md index bdbf4d53b718..fb95c3ab96ef 100644 --- a/docs/spec/SPEC-SPEC.md +++ b/docs/spec/SPEC-SPEC.md @@ -19,33 +19,28 @@ element as a part of a larger description. ## Common Layout -The specifications should be contained in a single `README.md` file inside the -`spec/` folder of a given module. - -The following generalized document structure should be used to breakdown -specifications for modules. Each bullet item corresponds to a new section in -the document, and should begin with a secondary heading (`## {HEADING}` in -Markdown). The `XX` at the beginning of the section name should be replaced -with a number to indicate document flow (ex. read `01. Concepts` before -`02. State Transitions`). The following list is nonbinding and all sections are -optional. - -- `XX. Abstract` - overview of the module -- `XX. Concepts` - describe specialized concepts and definitions used throughout the spec -- `XX. State` - specify and describe structures expected to marshalled into the store, and their keys -- `XX. State Transitions` - standard state transition operations triggered by hooks, messages, etc. -- `XX. Messages` - specify message structure(s) and expected state machine behaviour(s) -- `XX. BeginBlock` - specify any begin-block operations -- `XX. EndBlock` - specify any end-block operations -- `XX. Hooks` - describe available hooks to be called by/from this module -- `XX. Events` - list and describe event tags used -- `XX. Params` - list all module parameters, their types (in JSON) and examples -- `XX. Future Improvements` - describe future improvements of this module -- `XX. Appendix` - supplementary details referenced elsewhere within the spec +The following generalized file structure should be used to breakdown +specifications for modules. With the exception of README.md, `XX` at the +beginning of the file name should be replaced with a number to indicate +document flow (ex. read `01_state.md` before `02_state_transitions.md`). The +following list is nonbinding and all files are optional. + +- `README.md` - overview of the module +- `XX_concepts.md` - describe specialized concepts and definitions used throughout the spec +- `XX_state.md` - specify and describe structures expected to marshalled into the store, and their keys +- `XX_state_transitions.md` - standard state transition operations triggered by hooks, messages, etc. +- `XX_messages.md` - specify message structure(s) and expected state machine behaviour(s) +- `XX_begin_block.md` - specify any begin-block operations +- `XX_end_block.md` - specify any end-block operations +- `XX_hooks.md` - describe available hooks to be called by/from this module +- `XX_events.md` - list and describe event tags used +- `XX_params.md` - list all module parameters, their types (in JSON) and examples +- `XX_future_improvements.md` - describe future improvements of this module +- `XX_appendix.md` - supplementary details referenced elsewhere within the spec ### Notation for key-value mapping -Within the `State` section, the following notation `->` should be used to describe key to +Within `state.md` the following notation `->` should be used to describe key to value mapping: ``` diff --git a/go.mod b/go.mod index 7504e763d16d..2dfba4034d00 100644 --- a/go.mod +++ b/go.mod @@ -4,22 +4,27 @@ module github.com/cosmos/cosmos-sdk require ( github.com/99designs/keyring v1.1.6 + github.com/DataDog/zstd v1.4.5 // indirect github.com/armon/go-metrics v0.3.4 github.com/bgentry/speakeasy v0.1.0 github.com/btcsuite/btcd v0.21.0-beta github.com/btcsuite/btcutil v1.0.2 - github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb + github.com/confio/ics23/go v0.6.3 github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d - github.com/cosmos/iavl v0.15.0-rc3 + github.com/cosmos/iavl v0.15.0-rc4 github.com/cosmos/ledger-cosmos-go v0.11.1 + github.com/dgraph-io/badger/v2 v2.2007.2 // indirect + github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 github.com/gogo/gateway v1.1.0 github.com/gogo/protobuf v1.3.1 github.com/golang/mock v1.4.4 github.com/golang/protobuf v1.4.2 + github.com/golang/snappy v0.0.2 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 - github.com/grpc-ecosystem/grpc-gateway v1.15.0 + github.com/grpc-ecosystem/grpc-gateway v1.15.2 github.com/hashicorp/golang-lru v0.5.4 github.com/magiconair/properties v1.8.4 github.com/mattn/go-isatty v0.0.12 @@ -32,7 +37,7 @@ require ( github.com/regen-network/cosmos-proto v0.3.0 github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cast v1.3.1 - github.com/spf13/cobra v1.0.0 + github.com/spf13/cobra v1.1.0 github.com/spf13/jwalterweatherman v1.1.0 // indirect; indirects github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 @@ -40,11 +45,12 @@ require ( github.com/tendermint/btcd v0.1.1 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 github.com/tendermint/go-amino v0.16.0 - github.com/tendermint/tendermint v0.34.0-rc4.0.20201005135527-d7d0ffea13c6 + github.com/tendermint/tendermint v0.34.0-rc5 github.com/tendermint/tm-db v0.6.2 - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a - google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 - google.golang.org/grpc v1.32.0 + golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + google.golang.org/genproto v0.0.0-20201014134559-03b6142f0dc9 + google.golang.org/grpc v1.33.0 google.golang.org/protobuf v1.25.0 gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index b395dd5f6653..8b0a54ba6510 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1: github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -103,8 +105,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb h1:+7FsS1gZ1Km5LRjGV2hztpier/5i6ngNjvNpxbWP5I0= -github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= +github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0= +github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -117,8 +119,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= -github.com/cosmos/iavl v0.15.0-rc3 h1:rSm60IFfDCD9qDfvXKEmaJhcv0rB5uCbVlBDKsynxqw= -github.com/cosmos/iavl v0.15.0-rc3/go.mod h1:rQ2zK/LuivThMjve3Yr6VkjvCqCXl+fgHCY7quiUA68= +github.com/cosmos/iavl v0.15.0-rc4 h1:P1wmET7BueqCzfxsn+BzVkDWDLY9ij2JNwkbIdM7RG8= +github.com/cosmos/iavl v0.15.0-rc4/go.mod h1:5CsecJdh44Uj4vZ6WSPeWq84hNW5BwRI36ZsAbfJvRw= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= @@ -135,17 +137,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dgraph-io/badger/v2 v2.0.3 h1:inzdf6VF/NZ+tJ8RwwYMjJMvsOALTHYdozn0qSl6XJI= -github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM= github.com/dgraph-io/badger/v2 v2.2007.1 h1:t36VcBCpo4SsmAD5M8wVv1ieVzcALyGfaJ92z4ccULM= github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= -github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU= -github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= +github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -228,6 +232,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -247,6 +253,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -265,16 +272,14 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.14.7 h1:Nk5kuHrnWUTf/0GL1a/vchH/om9Ap2/HnVna+jYZgTY= -github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU= -github.com/grpc-ecosystem/grpc-gateway v1.15.0 h1:ntPNC9TD/6l2XDenJZe6T5lSMg95thpV9sGAqHX4WU8= -github.com/grpc-ecosystem/grpc-gateway v1.15.0/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8= +github.com/grpc-ecosystem/grpc-gateway v1.15.2 h1:HC+hWRWf+v5zTMPyoaYTKIJih+4sd4XRWmj0qlG87Co= +github.com/grpc-ecosystem/grpc-gateway v1.15.2/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -288,7 +293,6 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -301,7 +305,6 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -314,29 +317,24 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -347,7 +345,6 @@ github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNr github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -362,7 +359,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -377,15 +373,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= -github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -416,13 +409,11 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= @@ -486,7 +477,6 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= @@ -541,8 +531,9 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.0 h1:aq3wCKjTPmzcNWLVGnsFVN4rflK7Uzn10F8/aw8MhdQ= +github.com/spf13/cobra v1.1.0/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -552,6 +543,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -568,7 +560,6 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= @@ -579,12 +570,9 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RM github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/tendermint v0.34.0-rc3 h1:d7Fsd5rdbxq4GmJ0kRfx7l7LesQM7e70f0ytWLTQ/Go= -github.com/tendermint/tendermint v0.34.0-rc3/go.mod h1:BoHcEpjfpBHc1Be7RQz3AHaXFNObcDG7SNHCev6Or4g= -github.com/tendermint/tendermint v0.34.0-rc4.0.20201005135527-d7d0ffea13c6 h1:gqZ0WDpDYgMm/iaiMEXvI1nt/GoWCuwtBomVpUMiAIs= -github.com/tendermint/tendermint v0.34.0-rc4.0.20201005135527-d7d0ffea13c6/go.mod h1:BSXqR6vWbOecet726v66qVwSkFDLfEeBrq+EhkKbij4= -github.com/tendermint/tm-db v0.6.1 h1:w3X87itMPXopcRPlFiqspEKhw4FXihPk2rnFFkP0zGk= -github.com/tendermint/tm-db v0.6.1/go.mod h1:m3x9kRP4UFd7JODJL0yBAZqE7wTw+S37uAE90cTx7OA= +github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4= +github.com/tendermint/tendermint v0.34.0-rc5 h1:2bnQfWyOMfTCbol5pwB8CgM2nxi6/Kz6zqlS6Udm/Cg= +github.com/tendermint/tendermint v0.34.0-rc5/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4= github.com/tendermint/tm-db v0.6.2 h1:DOn8jwCdjJblrCFJbtonEIPD1IuJWpbRUUdR8GWE4RM= github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -627,12 +615,11 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -679,8 +666,9 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -761,7 +749,6 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa h1:5E4dL8+NgFOgjwbTKz+OOEGGhP+ectTmF842l6KjupQ= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -794,8 +781,8 @@ google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201014134559-03b6142f0dc9 h1:fG84H9C3EXfuDlzkG+VEPDYHHExklP6scH1QZ5gQTqU= +google.golang.org/genproto v0.0.0-20201014134559-03b6142f0dc9/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -811,11 +798,10 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.0 h1:IBKSUNL2uBS2DkJBncPP+TwT0sp9tgA8A75NjHt6umg= +google.golang.org/grpc v1.33.0/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -831,8 +817,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/proto/ibc/lightclients/localhost/v1/localhost.proto b/proto/ibc/lightclients/localhost/v1/localhost.proto index 766bc17dc362..d48a1c0076be 100644 --- a/proto/ibc/lightclients/localhost/v1/localhost.proto +++ b/proto/ibc/lightclients/localhost/v1/localhost.proto @@ -10,10 +10,8 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localho // access to keys outside the client prefix. message ClientState { option (gogoproto.goproto_getters) = false; - // client id - string id = 1; // self chain ID - string chain_id = 2 [(gogoproto.moretags) = "yaml:\"chain_id\""]; + string chain_id = 1 [(gogoproto.moretags) = "yaml:\"chain_id\""]; // self latest block height - ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; } diff --git a/proto/ibc/lightclients/solomachine/v1/solomachine.proto b/proto/ibc/lightclients/solomachine/v1/solomachine.proto index 605af48b5b53..5903b1b651cf 100644 --- a/proto/ibc/lightclients/solomachine/v1/solomachine.proto +++ b/proto/ibc/lightclients/solomachine/v1/solomachine.proto @@ -63,6 +63,7 @@ message SignatureAndData { bytes signature = 1; DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; bytes data = 3; + uint64 timestamp = 4; } // TimestampedSignatureData contains the signature data and the timestamp of the diff --git a/server/export.go b/server/export.go index b5fccaf050f9..3ed41a64940b 100644 --- a/server/export.go +++ b/server/export.go @@ -90,7 +90,7 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com Evidence: tmproto.EvidenceParams{ MaxAgeNumBlocks: exported.ConsensusParams.Evidence.MaxAgeNumBlocks, MaxAgeDuration: exported.ConsensusParams.Evidence.MaxAgeDuration, - MaxNum: exported.ConsensusParams.Evidence.MaxNum, + MaxBytes: exported.ConsensusParams.Evidence.MaxBytes, }, Validator: tmproto.ValidatorParams{ PubKeyTypes: exported.ConsensusParams.Validator.PubKeyTypes, diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 95b1c6fa4298..d6c8b665f9c4 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -146,7 +146,7 @@ func printlnJSON(v interface{}) error { func UnsafeResetAllCmd() *cobra.Command { return &cobra.Command{ Use: "unsafe-reset-all", - Short: "Resets the blockchain database, removes address book files, and resets priv_validator.json to the genesis state", + Short: "Resets the blockchain database, removes address book files, and resets data/priv_validator_state.json to the genesis state", RunE: func(cmd *cobra.Command, args []string) error { serverCtx := GetServerContextFromCmd(cmd) cfg := serverCtx.Config diff --git a/simapp/app.go b/simapp/app.go index 9ba94183cd65..c4ea3d070a56 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -9,6 +9,7 @@ import ( "github.com/gorilla/mux" "github.com/rakyll/statik/fs" abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -139,7 +140,7 @@ var _ App = (*SimApp)(nil) // capabilities aren't needed for testing. type SimApp struct { *baseapp.BaseApp - cdc *codec.LegacyAmino + legacyAmino *codec.LegacyAmino appCodec codec.Marshaler interfaceRegistry types.InterfaceRegistry @@ -195,7 +196,7 @@ func NewSimApp( // TODO: Remove cdc in favor of appCodec once all modules are migrated. appCodec := encodingConfig.Marshaler - cdc := encodingConfig.Amino + legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) @@ -215,7 +216,7 @@ func NewSimApp( app := &SimApp{ BaseApp: bApp, - cdc: cdc, + legacyAmino: legacyAmino, appCodec: appCodec, interfaceRegistry: interfaceRegistry, invCheckPeriod: invCheckPeriod, @@ -224,7 +225,7 @@ func NewSimApp( memKeys: memKeys, } - app.ParamsKeeper = initParamsKeeper(appCodec, cdc, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) // set the BaseApp's parameter store bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable())) @@ -450,7 +451,9 @@ func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Re // InitChainer application update at chain initialization func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { var genesisState GenesisState - app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState) + if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } return app.mm.InitGenesis(ctx, app.appCodec, genesisState) } @@ -485,7 +488,7 @@ func (app *SimApp) BlockedAddrs() map[string]bool { // NOTE: This is solely to be used for testing purposes as it may be desirable // for modules to register their own custom testing types. func (app *SimApp) LegacyAmino() *codec.LegacyAmino { - return app.cdc + return app.legacyAmino } // AppCodec returns SimApp's app codec. diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 8be599139b1d..e795394000d8 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -38,7 +38,7 @@ var DefaultConsensusParams = &abci.ConsensusParams{ Evidence: &tmproto.EvidenceParams{ MaxAgeNumBlocks: 302400, MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration - MaxNum: 50, + MaxBytes: 10000, }, Validator: &tmproto.ValidatorParams{ PubKeyTypes: []string{ @@ -320,12 +320,12 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.C // the parameter 'expPass' against the result. A corresponding result is // returned. func SignCheckDeliver( - t *testing.T, txGen client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, + t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, ) (sdk.GasInfo, *sdk.Result, error) { tx, err := helpers.GenTx( - txGen, + txCfg, msgs, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, helpers.DefaultGenTxGas, @@ -335,11 +335,11 @@ func SignCheckDeliver( priv..., ) require.NoError(t, err) - txBytes, err := txGen.TxEncoder()(tx) + txBytes, err := txCfg.TxEncoder()(tx) require.Nil(t, err) // Must simulate now as CheckTx doesn't run Msgs anymore - _, res, err := app.Simulate(txBytes, tx) + _, res, err := app.Simulate(txBytes) if expSimPass { require.NoError(t, err) @@ -351,7 +351,7 @@ func SignCheckDeliver( // Simulate a sending a transaction and committing a block app.BeginBlock(abci.RequestBeginBlock{Header: header}) - gInfo, res, err := app.Deliver(tx) + gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) if expPass { require.NoError(t, err) diff --git a/store/iavl/store.go b/store/iavl/store.go index 3d6bedabdbbb..e3a3f897d70f 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -41,9 +41,17 @@ type Store struct { // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the // store's version (id) from the provided DB. An error is returned if the version -// fails to load. +// fails to load, or if called with a positive version on an empty tree. func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) { - tree, err := iavl.NewMutableTree(db, defaultIAVLCacheSize) + return LoadStoreWithInitialVersion(db, id, lazyLoading, 0) +} + +// LoadStoreWithInitialVersion returns an IAVL Store as a CommitKVStore setting its initialVersion +// to the one given. Internally, it will load the store's version (id) from the +// provided DB. An error is returned if the version fails to load, or if called with a positive +// version on an empty tree. +func LoadStoreWithInitialVersion(db dbm.DB, id types.CommitID, lazyLoading bool, initialVersion uint64) (types.CommitKVStore, error) { + tree, err := iavl.NewMutableTreeWithOpts(db, defaultIAVLCacheSize, &iavl.Options{InitialVersion: initialVersion}) if err != nil { return nil, err } @@ -78,11 +86,11 @@ func UnsafeNewStore(tree *iavl.MutableTree) *Store { // GetImmutable returns a reference to a new store backed by an immutable IAVL // tree at a specific version (height) without any pruning options. This should // be used for querying and iteration only. If the version does not exist or has -// been pruned, an error will be returned. Any mutable operations executed will -// result in a panic. +// been pruned, an empty immutable IAVL tree will be used. +// Any mutable operations executed will result in a panic. func (st *Store) GetImmutable(version int64) (*Store, error) { if !st.VersionExists(version) { - return nil, iavl.ErrVersionDoesNotExist + return &Store{tree: &immutableTree{&iavl.ImmutableTree{}}}, nil } iTree, err := st.tree.GetImmutable(version) @@ -153,7 +161,6 @@ func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.Ca // Implements types.KVStore. func (st *Store) Set(key, value []byte) { - defer telemetry.MeasureSince(time.Now(), "store", "iavl", "set") types.AssertValidKey(key) types.AssertValidValue(value) st.tree.Set(key, value) diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index 052021310f99..790830038ba9 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -50,6 +50,62 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { return tree, types.CommitID{Version: ver, Hash: hash} } +func TestLoadStore(t *testing.T) { + db := dbm.NewMemDB() + tree, _ := newAlohaTree(t, db) + store := UnsafeNewStore(tree) + + // Create non-pruned height H + require.True(t, tree.Set([]byte("hello"), []byte("hallo"))) + hash, verH, err := tree.SaveVersion() + cIDH := types.CommitID{Version: verH, Hash: hash} + require.Nil(t, err) + + // Create pruned height Hp + require.True(t, tree.Set([]byte("hello"), []byte("hola"))) + hash, verHp, err := tree.SaveVersion() + cIDHp := types.CommitID{Version: verHp, Hash: hash} + require.Nil(t, err) + + // TODO: Prune this height + + // Create current height Hc + require.True(t, tree.Set([]byte("hello"), []byte("ciao"))) + hash, verHc, err := tree.SaveVersion() + cIDHc := types.CommitID{Version: verHc, Hash: hash} + require.Nil(t, err) + + // Querying an existing store at some previous non-pruned height H + hStore, err := store.GetImmutable(verH) + require.NoError(t, err) + require.Equal(t, string(hStore.Get([]byte("hello"))), "hallo") + + // Querying an existing store at some previous pruned height Hp + hpStore, err := store.GetImmutable(verHp) + require.NoError(t, err) + require.Equal(t, string(hpStore.Get([]byte("hello"))), "hola") + + // Querying an existing store at current height Hc + hcStore, err := store.GetImmutable(verHc) + require.NoError(t, err) + require.Equal(t, string(hcStore.Get([]byte("hello"))), "ciao") + + // Querying a new store at some previous non-pruned height H + newHStore, err := LoadStore(db, cIDH, false) + require.NoError(t, err) + require.Equal(t, string(newHStore.Get([]byte("hello"))), "hallo") + + // Querying a new store at some previous pruned height Hp + newHpStore, err := LoadStore(db, cIDHp, false) + require.NoError(t, err) + require.Equal(t, string(newHpStore.Get([]byte("hello"))), "hola") + + // Querying a new store at current height H + newHcStore, err := LoadStore(db, cIDHc, false) + require.NoError(t, err) + require.Equal(t, string(newHcStore.Get([]byte("hello"))), "ciao") +} + func TestGetImmutable(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) @@ -61,7 +117,7 @@ func TestGetImmutable(t *testing.T) { require.Nil(t, err) _, err = store.GetImmutable(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) newStore, err := store.GetImmutable(cID.Version - 1) require.NoError(t, err) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index bf8a473b8d0b..90633a922078 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -187,7 +187,14 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { var newStores = make(map[types.StoreKey]types.CommitKVStore) for key, storeParams := range rs.storesParams { - store, err := rs.loadCommitStoreFromParams(key, rs.getCommitID(infos, key.Name()), storeParams) + commitID := rs.getCommitID(infos, key.Name()) + + // If it has been added, set the initial version + if upgrades.IsAdded(key.Name()) { + storeParams.initialVersion = uint64(ver) + 1 + } + + store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) if err != nil { return errors.Wrap(err, "failed to load store") } @@ -813,7 +820,15 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID panic("recursive MultiStores not yet supported") case types.StoreTypeIAVL: - store, err := iavl.LoadStore(db, id, rs.lazyLoading) + var store types.CommitKVStore + var err error + + if params.initialVersion == 0 { + store, err = iavl.LoadStore(db, id, rs.lazyLoading) + } else { + store, err = iavl.LoadStoreWithInitialVersion(db, id, rs.lazyLoading, params.initialVersion) + } + if err != nil { return nil, err } @@ -868,9 +883,10 @@ func (rs *Store) buildCommitInfo(version int64) *types.CommitInfo { } type storeParams struct { - key types.StoreKey - db dbm.DB - typ types.StoreType + key types.StoreKey + db dbm.DB + typ types.StoreType + initialVersion uint64 } func getLatestVersion(db dbm.DB) int64 { diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 7341154f2edd..efe2618779a4 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -79,9 +79,9 @@ func TestCacheMultiStoreWithVersion(t *testing.T) { cID := ms.Commit() require.Equal(t, int64(1), cID.Version) - // require failure when given an invalid or pruned version + // require no failure when given an invalid or pruned version _, err = ms.CacheMultiStoreWithVersion(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) // require a valid version can be cache-loaded cms, err := ms.CacheMultiStoreWithVersion(cID.Version) @@ -192,6 +192,9 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) s3.Set(k3, v3) + s4, _ := store.getStoreByName("store4").(types.KVStore) + require.Nil(t, s4) + // do one commit commitID := store.Commit() expectedCommitID := getExpectedCommitID(store, 1) @@ -231,6 +234,24 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) require.Nil(t, s3.Get(k3)) // data was deleted + // store4 is mounted, with empty data + s4, _ = restore.getStoreByName("store4").(types.KVStore) + require.NotNil(t, s4) + + iterator := s4.Iterator(nil, nil) + + values := 0 + for ; iterator.Valid(); iterator.Next() { + values += 1 + } + require.Zero(t, values) + + require.NoError(t, iterator.Close()) + + // write something inside store4 + k4, v4 := []byte("fourth"), []byte("created") + s4.Set(k4, v4) + // store2 is no longer mounted st2 := restore.getStoreByName("store2") require.Nil(t, st2) @@ -258,12 +279,16 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, rl2) require.Equal(t, v2, rl2.Get(k2)) + rl4, _ := reload.getStoreByName("store4").(types.KVStore) + require.NotNil(t, rl4) + require.Equal(t, v4, rl4.Get(k4)) + // check commitInfo in storage ci, err = getCommitInfo(db, 2) require.NoError(t, err) require.Equal(t, int64(2), ci.Version) - require.Equal(t, 3, len(ci.StoreInfos), ci.StoreInfos) - checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3"}) + require.Equal(t, 4, len(ci.StoreInfos), ci.StoreInfos) + checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3", "store4"}) } func TestParsePath(t *testing.T) { @@ -474,7 +499,7 @@ func TestMultiStore_Pruning(t *testing.T) { for _, v := range tc.deleted { _, err := ms.CacheMultiStoreWithVersion(v) - require.Error(t, err, "expected error when loading height: %d", v) + require.NoError(t, err, "expected error when loading height: %d", v) } }) } @@ -510,7 +535,7 @@ func TestMultiStore_PruningRestart(t *testing.T) { for _, v := range pruneHeights { _, err := ms.CacheMultiStoreWithVersion(v) - require.Error(t, err, "expected error when loading height: %d", v) + require.NoError(t, err, "expected error when loading height: %d", v) } } @@ -796,8 +821,10 @@ func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions store.MountStoreWithDB(types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("restore2"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewKVStoreKey("store4"), types.StoreTypeIAVL, nil) upgrades := &types.StoreUpgrades{ + Added: []string{"store4"}, Renamed: []types.StoreRename{{ OldKey: "store2", NewKey: "restore2", diff --git a/store/types/store.go b/store/types/store.go index 989ae94e8c42..f78fcad3f402 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -9,6 +9,7 @@ import ( snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/types/kv" + tmstrings "github.com/tendermint/tendermint/libs/strings" ) type Store interface { @@ -44,6 +45,7 @@ type Queryable interface { // StoreUpgrades defines a series of transformations to apply the multistore db upon load type StoreUpgrades struct { + Added []string `json:"added"` Renamed []StoreRename `json:"renamed"` Deleted []string `json:"deleted"` } @@ -63,6 +65,14 @@ type StoreRename struct { NewKey string `json:"new_key"` } +// IsDeleted returns true if the given key should be added +func (s *StoreUpgrades) IsAdded(key string) bool { + if s == nil { + return false + } + return tmstrings.StringInSlice(key, s.Added) +} + // IsDeleted returns true if the given key should be deleted func (s *StoreUpgrades) IsDeleted(key string) bool { if s == nil { diff --git a/third_party/proto/confio/proofs.proto b/third_party/proto/confio/proofs.proto index da43503ecbd6..9dd39e614d70 100644 --- a/third_party/proto/confio/proofs.proto +++ b/third_party/proto/confio/proofs.proto @@ -1,7 +1,6 @@ syntax = "proto3"; package ics23; -option go_package = "github.com/confio/ics23/go"; enum HashOp { // NO_HASH is the default if no data passed. Note this is an illegal argument some places. diff --git a/third_party/proto/tendermint/abci/types.proto b/third_party/proto/tendermint/abci/types.proto index 09b96f1ddf7a..2cbcabb29b33 100644 --- a/third_party/proto/tendermint/abci/types.proto +++ b/third_party/proto/tendermint/abci/types.proto @@ -334,9 +334,9 @@ message TxResult { // Validator message Validator { - bytes address = 1; // The first 20 bytes of SHA256(public key) + bytes address = 1; // The first 20 bytes of SHA256(public key) // PubKey pub_key = 2 [(gogoproto.nullable)=false]; - int64 power = 3; // The voting power + int64 power = 3; // The voting power } // ValidatorUpdate @@ -352,19 +352,19 @@ message VoteInfo { } enum EvidenceType { - UNKNOWN = 0; - DUPLICATE_VOTE = 1; - LIGHT_CLIENT_ATTACK = 2; + UNKNOWN = 0; + DUPLICATE_VOTE = 1; + LIGHT_CLIENT_ATTACK = 2; } message Evidence { - EvidenceType type = 1; + EvidenceType type = 1; // The offending validator - Validator validator = 2 [(gogoproto.nullable) = false]; - // The height when the offense occurred - int64 height = 3; + Validator validator = 2 [(gogoproto.nullable) = false]; + // The height when the offense occurred + int64 height = 3; // The corresponding time where the offense occurred - google.protobuf.Timestamp time = 4 [ + google.protobuf.Timestamp time = 4 [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true ]; diff --git a/third_party/proto/tendermint/types/evidence.proto b/third_party/proto/tendermint/types/evidence.proto index 3ff10c1b33e3..5f7d860bf4e4 100644 --- a/third_party/proto/tendermint/types/evidence.proto +++ b/third_party/proto/tendermint/types/evidence.proto @@ -4,28 +4,28 @@ package tendermint.types; option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; import "tendermint/types/types.proto"; -import "tendermint/crypto/keys.proto"; // DuplicateVoteEvidence contains evidence a validator signed two conflicting // votes. message DuplicateVoteEvidence { Vote vote_a = 1; Vote vote_b = 2; +} - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +message LightClientAttackEvidence { + LightBlock conflicting_block = 1; + int64 common_height = 2; } message Evidence { oneof sum { - DuplicateVoteEvidence duplicate_vote_evidence = 1; + DuplicateVoteEvidence duplicate_vote_evidence = 1; + LightClientAttackEvidence light_client_attack_evidence = 2; } } // EvidenceData contains any evidence of malicious wrong-doing by validators message EvidenceData { repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; - bytes hash = 2; } diff --git a/third_party/proto/tendermint/types/params.proto b/third_party/proto/tendermint/types/params.proto index 897c07c17f6a..0de7d846fbd3 100644 --- a/third_party/proto/tendermint/types/params.proto +++ b/third_party/proto/tendermint/types/params.proto @@ -48,11 +48,10 @@ message EvidenceParams { google.protobuf.Duration max_age_duration = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; - // This sets the maximum number of evidence that can be committed in a single block. - // and should fall comfortably under the max block bytes when we consider the size of - // each evidence (See MaxEvidenceBytes). The maximum number is MaxEvidencePerBlock. - // Default is 50 - uint32 max_num = 3; + // This sets the maximum size of total evidence in bytes that can be committed in a single block. + // and should fall comfortably under the max block bytes. + // Default is 1048576 or 1MB + int64 max_bytes = 3; } // ValidatorParams restrict the public key types validators can use. diff --git a/third_party/proto/tendermint/types/types.proto b/third_party/proto/tendermint/types/types.proto index 2762f4a78568..7f7ea74cac21 100644 --- a/third_party/proto/tendermint/types/types.proto +++ b/third_party/proto/tendermint/types/types.proto @@ -5,7 +5,6 @@ option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; import "gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; -import "tendermint/libs/bits/types.proto"; import "tendermint/crypto/proof.proto"; import "tendermint/version/types.proto"; import "tendermint/types/validator.proto"; @@ -26,13 +25,13 @@ enum SignedMsgType { option (gogoproto.goproto_enum_stringer) = true; option (gogoproto.goproto_enum_prefix) = false; - SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; + SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; // Votes SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; // Proposals - SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; + SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; } // PartsetHeader @@ -88,8 +87,6 @@ message Data { // NOTE: not all txs here are valid. We're just agreeing on the order first. // This means that block.AppHash does not include these txs. repeated bytes txs = 1; - // Volatile - bytes hash = 2; } // Vote represents a prevote, precommit, or commit vote from validators for @@ -113,8 +110,6 @@ message Commit { int32 round = 2; BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; - bytes hash = 5; - tendermint.libs.bits.BitArray bit_array = 6; } // CommitSig is a part of the Vote included in a Commit. @@ -144,7 +139,7 @@ message SignedHeader { message LightBlock { SignedHeader signed_header = 1; - tendermint.types.ValidatorSet validator_set = 2; + tendermint.types.ValidatorSet validator_set = 2; } message BlockMeta { diff --git a/x/auth/client/cli/broadcast.go b/x/auth/client/cli/broadcast.go index 5f09d4ca7c1a..64df000dc1e9 100644 --- a/x/auth/client/cli/broadcast.go +++ b/x/auth/client/cli/broadcast.go @@ -21,7 +21,7 @@ flag and signed with the sign command. Read a transaction from [file_path] and broadcast it to a node. If you supply a dash (-) argument in place of an input filename, the command reads from standard input. -$ tx broadcast ./mytxn.json +$ tx broadcast ./mytxn.json `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/auth/client/cli/cli_test.go b/x/auth/client/cli/cli_test.go index 996b458ffc2e..72e53a97a4ee 100644 --- a/x/auth/client/cli/cli_test.go +++ b/x/auth/client/cli/cli_test.go @@ -28,6 +28,8 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -833,6 +835,66 @@ func (s *IntegrationTestSuite) TestQueryParamsCmd() { } } +// TestTxWithoutPublicKey makes sure sending a proto tx message without the +// public key doesn't cause any error in the RPC layer (broadcast). +// See https://github.com/cosmos/cosmos-sdk/issues/7585 for more details. +func (s *IntegrationTestSuite) TestTxWithoutPublicKey() { + val1 := s.network.Validators[0] + txCfg := val1.ClientCtx.TxConfig + + // Create a txBuilder with an unsigned tx. + txBuilder := txCfg.NewTxBuilder() + msg := banktypes.NewMsgSend(val1.Address, val1.Address, sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + )) + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)))) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + // Set empty signature to set signer infos. + sigV2 := signing.SignatureV2{ + PubKey: val1.PubKey, + Data: &signing.SingleSignatureData{ + SignMode: txCfg.SignModeHandler().DefaultMode(), + Signature: nil, + }, + } + err = txBuilder.SetSignatures(sigV2) + s.Require().NoError(err) + + // Create a file with the unsigned tx. + txJSON, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + unsignedTxFile, cleanup := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + defer cleanup() + + // Sign the file with the unsignedTx. + signedTx, err := authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name()) + s.Require().NoError(err) + + // Remove the signerInfo's `public_key` field manually from the signedTx. + // Note: this method is only used for test purposes! In general, one should + // use txBuilder and TxEncoder/TxDecoder to manipulate txs. + var tx tx.Tx + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(signedTx.Bytes(), &tx) + s.Require().NoError(err) + tx.AuthInfo.SignerInfos[0].PublicKey = nil + // Re-encode the tx again, to another file. + txJSON, err = val1.ClientCtx.JSONMarshaler.MarshalJSON(&tx) + s.Require().NoError(err) + signedTxFile, cleanup2 := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + s.Require().True(strings.Contains(string(txJSON), "\"public_key\":null")) + defer cleanup2() + + // Broadcast tx, test that it shouldn't panic. + val1.ClientCtx.BroadcastMode = flags.BroadcastSync + out, err := authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + var res sdk.TxResponse + s.Require().NoError(val1.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &res)) + s.Require().NotEqual(0, res.Code) + s.Require().NoError(err) +} + func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index df0dce31fc49..c6fb14054fde 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -49,7 +49,7 @@ func QueryParamsCmd() *cobra.Command { Args: cobra.NoArgs, Long: strings.TrimSpace(`Query the current auth parameters: -$ query auth params +$ query auth params `), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index e3ef24b42eec..fc5e907de0a2 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -19,6 +19,7 @@ import ( signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/auth/signing" ) @@ -52,6 +53,7 @@ recommended to set such parameters manually. cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") flags.AddTxFlagsToCmd(cmd) cmd.Flags().String(flags.FlagChainID, "", "network chain ID") @@ -147,9 +149,28 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) error { sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) - json, err := marshalSignatureJSON(txCfg, txBuilder, sigOnly) - if err != nil { - return err + aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + + var json []byte + + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) + if err != nil { + return err + } + + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + + json, _ = clientCtx.LegacyAmino.MarshalJSON(req) + + } else { + json, err = marshalSignatureJSON(txCfg, txBuilder, sigOnly) + if err != nil { + return err + } } outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index b5bceecb2fa0..7046c495d663 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -12,12 +12,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/client/rest" ) const ( flagMultisig = "multisig" flagAppend = "append" flagSigOnly = "signature-only" + flagAmino = "amino" ) // GetSignBatchCommand returns the transaction sign-batch command. @@ -187,6 +189,7 @@ be generated via the 'multisign' command. cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") + cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") cmd.MarkFlagRequired(flags.FlagFrom) flags.AddTxFlagsToCmd(cmd) @@ -257,11 +260,36 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error { return err } - json, err := marshalSignatureJSON(txCfg, txBuilder, generateSignatureOnly) + aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + if err != nil { return err } + var json []byte + + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) + if err != nil { + return err + } + + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + + json, err = clientCtx.LegacyAmino.MarshalJSON(req) + if err != nil { + return err + } + } else { + json, err = marshalSignatureJSON(txCfg, txBuilder, generateSignatureOnly) + if err != nil { + return err + } + } + outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) if outputDoc == "" { cmd.Printf("%s\n", json) diff --git a/x/auth/keeper/account.go b/x/auth/keeper/account.go index e39c98ac52e7..9921bb96f945 100644 --- a/x/auth/keeper/account.go +++ b/x/auth/keeper/account.go @@ -5,7 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// NewAccountWithAddress implements sdk.AccountKeeper. +// NewAccountWithAddress implements AccountKeeperI. func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) types.AccountI { acc := ak.proto() err := acc.SetAddress(addr) @@ -25,7 +25,7 @@ func (ak AccountKeeper) NewAccount(ctx sdk.Context, acc types.AccountI) types.Ac return acc } -// GetAccount implements sdk.AccountKeeper. +// GetAccount implements AccountKeeperI. func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI { store := ctx.KVStore(ak.key) bz := store.Get(types.AddressStoreKey(addr)) @@ -46,7 +46,7 @@ func (ak AccountKeeper) GetAllAccounts(ctx sdk.Context) (accounts []types.Accoun return accounts } -// SetAccount implements sdk.AccountKeeper. +// SetAccount implements AccountKeeperI. func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc types.AccountI) { addr := acc.GetAddress() store := ctx.KVStore(ak.key) diff --git a/x/auth/keeper/keeper.go b/x/auth/keeper/keeper.go index eaefead6c2ea..86189b62a24d 100644 --- a/x/auth/keeper/keeper.go +++ b/x/auth/keeper/keeper.go @@ -14,6 +14,36 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) +// AccountKeeperI is the interface contract that x/auth's keeper implements. +type AccountKeeperI interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI + + // Return a new account with the next account number. Does not save the new account to the store. + NewAccount(sdk.Context, types.AccountI) types.AccountI + + // Retrieve an account from the store. + GetAccount(sdk.Context, sdk.AccAddress) types.AccountI + + // Set an account in the store. + SetAccount(sdk.Context, types.AccountI) + + // Remove an account from the store. + RemoveAccount(sdk.Context, types.AccountI) + + // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. + IterateAccounts(sdk.Context, func(types.AccountI) bool) + + // Fetch the public key of an account at a specified address + GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error) + + // Fetch the sequence of an account at a specified address. + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) + + // Fetch the next account number, and increment the internal counter. + GetNextAccountNumber(sdk.Context) uint64 +} + // AccountKeeper encodes/decodes accounts using the go-amino (binary) // encoding/decoding library. type AccountKeeper struct { @@ -26,7 +56,9 @@ type AccountKeeper struct { proto func() types.AccountI } -// NewAccountKeeper returns a new sdk.AccountKeeper that uses go-amino to +var _ AccountKeeperI = &AccountKeeper{} + +// NewAccountKeeper returns a new AccountKeeperI that uses go-amino to // (binary) encode and decode concrete sdk.Accounts. func NewAccountKeeper( cdc codec.BinaryMarshaler, key sdk.StoreKey, paramstore paramtypes.Subspace, proto func() types.AccountI, diff --git a/x/auth/spec/02_state.md b/x/auth/spec/02_state.md index 37afc5e03d0c..b9008603d158 100644 --- a/x/auth/spec/02_state.md +++ b/x/auth/spec/02_state.md @@ -15,30 +15,39 @@ Accounts are exposed externally as an interface, and stored internally as either a base account or vesting account. Module clients wishing to add more account types may do so. -- `0x01 | Address -> amino(account)` +- `0x01 | Address -> ProtocolBuffer(account)` ### Account Interface The account interface exposes methods to read and write standard account information. -Note that all of these methods operate on an account struct confirming to the interface -- in order to write the account to the store, the account keeper will need to be used. +Note that all of these methods operate on an account struct confirming to the +interface - in order to write the account to the store, the account keeper will +need to be used. ```go -type Account interface { - GetAddress() AccAddress - SetAddress(AccAddress) +// AccountI is an interface used to store coins at a given address within state. +// It presumes a notion of sequence numbers for replay protection, +// a notion of account numbers for replay protection for previously pruned accounts, +// and a pubkey for authentication purposes. +// +// Many complex conditions can be used in the concrete struct which implements AccountI. +type AccountI interface { + proto.Message - GetPubKey() PubKey - SetPubKey(PubKey) + GetAddress() sdk.AccAddress + SetAddress(sdk.AccAddress) error // errors if already set. - GetAccountNumber() uint64 - SetAccountNumber(uint64) + GetPubKey() crypto.PubKey // can return nil. + SetPubKey(crypto.PubKey) error - GetSequence() uint64 - SetSequence(uint64) + GetAccountNumber() uint64 + SetAccountNumber(uint64) error - GetCoins() Coins - SetCoins(Coins) + GetSequence() uint64 + SetSequence(uint64) error + + // Ensure that account implements stringer + String() string } ``` @@ -47,13 +56,15 @@ type Account interface { A base account is the simplest and most common account type, which just stores all requisite fields directly in a struct. -```go -type BaseAccount struct { - Address AccAddress - Coins Coins - PubKey PubKey - AccountNumber uint64 - Sequence uint64 +```protobuf +// BaseAccount defines a base account type. It contains all the necessary fields +// for basic account functionality. Any custom account type should extend this +// type for additional functionality (e.g. vesting). +message BaseAccount { + string address = 1; + google.protobuf.Any pub_key = 2; + uint64 account_number = 3; + uint64 sequence = 4; } ``` diff --git a/x/auth/spec/03_messages.md b/x/auth/spec/03_antehandlers.md similarity index 90% rename from x/auth/spec/03_messages.md rename to x/auth/spec/03_antehandlers.md index 37834d8d1627..709cae3789f4 100644 --- a/x/auth/spec/03_messages.md +++ b/x/auth/spec/03_antehandlers.md @@ -2,16 +2,14 @@ order: 3 --> -# Messages - -TODO make this file conform to typical messages spec +# AnthHandlers ## Handlers The auth module presently has no transaction handlers of its own, but does expose the special `AnteHandler`, used for performing basic validity checks on a transaction, such that it could be thrown out of the mempool. Note that the ante handler is called on -`CheckTx`, but *also* on `DeliverTx`, as Tendermint proposers presently have the ability +`CheckTx`, but _also_ on `DeliverTx`, as Tendermint proposers presently have the ability to include in their proposed block transactions which fail `CheckTx`. ### Ante Handler diff --git a/x/auth/spec/03_types.md b/x/auth/spec/03_types.md deleted file mode 100644 index 5e768fc3b828..000000000000 --- a/x/auth/spec/03_types.md +++ /dev/null @@ -1,70 +0,0 @@ - - -# Types - -Besides accounts (specified in [State](02_state.md)), the types exposed by the auth module -are `StdFee`, the combination of an amount and gas limit, `StdSignature`, the combination -of an optional public key and a cryptographic signature as a byte array, `StdTx`, -a struct which implements the `sdk.Tx` interface using `StdFee` and `StdSignature`, and -`StdSignDoc`, a replay-prevention structure for `StdTx` which transaction senders must sign over. - -## StdFee - -A `StdFee` is simply the combination of a fee amount, in any number of denominations, -and a gas limit (where dividing the amount by the gas limit gives a "gas price"). - -```go -type StdFee struct { - Amount Coins - Gas uint64 -} -``` - -## StdSignature - -A `StdSignature` is the combination of an optional public key and a cryptographic signature -as a byte array. The SDK is agnostic to particular key or signature formats and supports any -supported by the `PubKey` interface. - -```go -type StdSignature struct { - PubKey PubKey - Signature []byte -} -``` - -## StdTx - -A `StdTx` is a struct which implements the `sdk.Tx` interface, and is likely to be generic -enough to serve the purposes of many Cosmos SDK blockchains. - -```go -type StdTx struct { - Msgs []sdk.Msg - Fee StdFee - Signatures []StdSignature - Memo string -} -``` - -## StdSignDoc - -A `StdSignDoc` is a replay-prevention structure to be signed over, which ensures that -any submitted transaction (which is simply a signature over a particular bytestring) -will only be executable once on a particular blockchain. - -`json.RawMessage` is preferred over using the SDK types for future compatibility. - -```go -type StdSignMsg struct { - ChainID string - AccountNumber uint64 - Sequence uint64 - TimeoutHeight uint64 - Fee StdFee - Msgs []sdk.Msg - Memo string -} -``` diff --git a/x/auth/spec/04_keepers.md b/x/auth/spec/04_keepers.md index e154e5383042..f57988b4fa7c 100644 --- a/x/auth/spec/04_keepers.md +++ b/x/auth/spec/04_keepers.md @@ -12,32 +12,33 @@ Presently only one fully-permissioned account keeper is exposed, which has the a all fields of all accounts, and to iterate over all stored accounts. ```go -type AccountKeeper interface { - // Return a new account with the next account number and the specified address. Does not save the new account to the store. - NewAccountWithAddress(AccAddress) Account +// AccountKeeperI is the interface contract that x/auth's keeper implements. +type AccountKeeperI interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI - // Return a new account with the next account number. Does not save the new account to the store. - NewAccount(Account) Account + // Return a new account with the next account number. Does not save the new account to the store. + NewAccount(sdk.Context, types.AccountI) types.AccountI - // Retrieve an account from the store - GetAccount(AccAddress) Account + // Retrieve an account from the store. + GetAccount(sdk.Context, sdk.AccAddress) types.AccountI - // Set an account in the store - SetAccount(Account) + // Set an account in the store. + SetAccount(sdk.Context, types.AccountI) - // Remove an account from the store - RemoveAccount(Account) + // Remove an account from the store. + RemoveAccount(sdk.Context, types.AccountI) - // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. - IterateAccounts(func(Account) (bool)) + // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. + IterateAccounts(sdk.Context, func(types.AccountI) bool) - // Fetch the public key of an account at a specified address - GetPubKey(AccAddress) PubKey + // Fetch the public key of an account at a specified address + GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error) - // Fetch the sequence of an account at a specified address - GetSequence(AccAddress) uint64 + // Fetch the sequence of an account at a specified address. + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) - // Fetch the next account number, and increment the internal counter - GetNextAccountNumber() uint64 + // Fetch the next account number, and increment the internal counter. + GetNextAccountNumber(sdk.Context) uint64 } ``` diff --git a/x/auth/spec/07_params.md b/x/auth/spec/07_params.md index 6ef63ed1215b..93ddc89c2219 100644 --- a/x/auth/spec/07_params.md +++ b/x/auth/spec/07_params.md @@ -7,7 +7,7 @@ order: 7 The auth module contains the following parameters: | Key | Type | Example | -|------------------------|-----------------|---------| +| ---------------------- | --------------- | ------- | | MaxMemoCharacters | string (uint64) | "256" | | TxSigLimit | string (uint64) | "7" | | TxSizeCostPerByte | string (uint64) | "10" | diff --git a/x/auth/spec/README.md b/x/auth/spec/README.md index ea49a9736efe..f666e0923b2e 100644 --- a/x/auth/spec/README.md +++ b/x/auth/spec/README.md @@ -21,24 +21,19 @@ This module will be used in the Cosmos Hub. ## Contents 1. **[Concepts](01_concepts.md)** - - [Gas & Fees](01_concepts.md#gas-&-fees) + - [Gas & Fees](01_concepts.md#gas-&-fees) 2. **[State](02_state.md)** - - [Accounts](02_state.md#accounts) -3. **[Messages](03_messages.md)** - - [Handlers](03_messages.md#handlers) -4. **[Types](03_types.md)** - - [StdFee](03_types.md#stdfee) - - [StdSignature](03_types.md#stdsignature) - - [StdTx](03_types.md#stdtx) - - [StdSignDoc](03_types.md#stdsigndoc) -5. **[Keepers](04_keepers.md)** - - [Account Keeper](04_keepers.md#account-keeper) -6. **[Vesting](05_vesting.md)** - - [Intro and Requirements](05_vesting.md#intro-and-requirements) - - [Vesting Account Types](05_vesting.md#vesting-account-types) - - [Vesting Account Specification](05_vesting.md#vesting-account-specification) - - [Keepers & Handlers](05_vesting.md#keepers-&-handlers) - - [Genesis Initialization](05_vesting.md#genesis-initialization) - - [Examples](05_vesting.md#examples) - - [Glossary](05_vesting.md#glossary) -7. **[Parameters](07_params.md)** + - [Accounts](02_state.md#accounts) +3. **[AnteHandlers](03_antehandlers.md)** + - [Handlers](03_antehandlers.md#handlers) +4. **[Keepers](04_keepers.md)** + - [Account Keeper](04_keepers.md#account-keeper) +5. **[Vesting](05_vesting.md)** + - [Intro and Requirements](05_vesting.md#intro-and-requirements) + - [Vesting Account Types](05_vesting.md#vesting-account-types) + - [Vesting Account Specification](05_vesting.md#vesting-account-specification) + - [Keepers & Handlers](05_vesting.md#keepers-&-handlers) + - [Genesis Initialization](05_vesting.md#genesis-initialization) + - [Examples](05_vesting.md#examples) + - [Glossary](05_vesting.md#glossary) +6. **[Parameters](07_params.md)** diff --git a/x/auth/types/account_retriever.go b/x/auth/types/account_retriever.go index 2b8d24ac279c..f84c744188ff 100644 --- a/x/auth/types/account_retriever.go +++ b/x/auth/types/account_retriever.go @@ -13,13 +13,18 @@ import ( grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" ) +var ( + _ client.Account = AccountI(nil) + _ client.AccountRetriever = AccountRetriever{} +) + // AccountRetriever defines the properties of a type that can be used to // retrieve accounts. type AccountRetriever struct{} // GetAccount queries for an account given an address and a block height. An // error is returned if the query or decoding fails. -func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddress) (AccountI, error) { +func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddress) (client.Account, error) { account, _, err := ar.GetAccountWithHeight(clientCtx, addr) return account, err } @@ -28,7 +33,7 @@ func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddr // height of the query with the account. An error is returned if the query // or decoding fails. //nolint:interfacer -func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (AccountI, int64, error) { +func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (client.Account, int64, error) { var header metadata.MD queryClient := NewQueryClient(clientCtx) diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index a5cc84bd155d..58761365b71c 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -46,12 +46,12 @@ func BenchmarkOneBankSendTxPerBlock(b *testing.B) { // Committing, and what time comes from Check/Deliver Tx. for i := 0; i < b.N; i++ { benchmarkApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: height}}) - _, _, err := benchmarkApp.Check(txs[i]) + _, _, err := benchmarkApp.Check(txGen.TxEncoder(), txs[i]) if err != nil { panic("something is broken in checking transaction") } - _, _, err = benchmarkApp.Deliver(txs[i]) + _, _, err = benchmarkApp.Deliver(txGen.TxEncoder(), txs[i]) require.NoError(b, err) benchmarkApp.EndBlock(abci.RequestEndBlock{Height: height}) benchmarkApp.Commit() @@ -88,12 +88,12 @@ func BenchmarkOneBankMultiSendTxPerBlock(b *testing.B) { // Committing, and what time comes from Check/Deliver Tx. for i := 0; i < b.N; i++ { benchmarkApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: height}}) - _, _, err := benchmarkApp.Check(txs[i]) + _, _, err := benchmarkApp.Check(txGen.TxEncoder(), txs[i]) if err != nil { panic("something is broken in checking transaction") } - _, _, err = benchmarkApp.Deliver(txs[i]) + _, _, err = benchmarkApp.Deliver(txGen.TxEncoder(), txs[i]) require.NoError(b, err) benchmarkApp.EndBlock(abci.RequestEndBlock{Height: height}) benchmarkApp.Commit() diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 286dcc3fdbc6..0f7d1194981c 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -123,7 +123,7 @@ func sendMsgSend( return err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return err } @@ -280,7 +280,7 @@ func sendMsgMultiSend( return err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return err } diff --git a/x/bank/spec/01_state.md b/x/bank/spec/01_state.md new file mode 100644 index 000000000000..f744e2e779a1 --- /dev/null +++ b/x/bank/spec/01_state.md @@ -0,0 +1,11 @@ + + +# State + +The `x/bank` module keeps state of two primary objects, account balances and the +total supply of all balances. + +- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)` +- Supply: `0x0 -> ProtocolBuffer(Supply)` diff --git a/x/bank/spec/02_keepers.md b/x/bank/spec/02_keepers.md new file mode 100644 index 000000000000..ec74358fbb70 --- /dev/null +++ b/x/bank/spec/02_keepers.md @@ -0,0 +1,123 @@ + + +# Keepers + +The bank module provides three different exported keeper interfaces which can be passed to other modules which need to read or update account balances. Modules should use the least-permissive interface which provides the functionality they require. + +Note that you should always review the `bank` module code to ensure that permissions are limited in the way that you expect. + +## Common Types + +### Input + +An input of a multiparty transfer + +```protobuf +// Input models transaction input. +message Input { + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2; +} +``` + +### Output + +An output of a multiparty transfer. + +```protobuf +// Output models transaction outputs. +message Output { + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2; +} +``` + +## BaseKeeper + +The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins. + +```go +// Keeper defines a module interface that facilitates the transfer of coins +// between accounts. +type Keeper interface { + SendKeeper + + InitGenesis(sdk.Context, types.GenesisState) + ExportGenesis(sdk.Context) *types.GenesisState + + GetSupply(ctx sdk.Context) exported.SupplyI + SetSupply(ctx sdk.Context, supply exported.SupplyI) + + GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata + SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) + IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) + + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + + DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error + UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error + MarshalSupply(supplyI exported.SupplyI) ([]byte, error) + UnmarshalSupply(bz []byte) (exported.SupplyI, error) + + types.QueryServer +} +``` + +## SendKeeper + +The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins). + +```go +// SendKeeper defines a module interface that facilitates the transfer of coins +// between accounts without the possibility of creating coins. +type SendKeeper interface { + ViewKeeper + + InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error + + SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error + SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error + + GetParams(ctx sdk.Context) types.Params + SetParams(ctx sdk.Context, params types.Params) + + SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool + SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + + BlockedAddr(addr sdk.AccAddress) bool +} +``` + +## ViewKeeper + +The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`. + +```go +// ViewKeeper defines a module interface that facilitates read only access to +// account balances. +type ViewKeeper interface { + ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error + HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool + + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + GetAccountsBalances(ctx sdk.Context) []types.Balance + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin + LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + + IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) + IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) +} +``` diff --git a/x/bank/spec/03_messages.md b/x/bank/spec/03_messages.md new file mode 100644 index 000000000000..ecb9dc695fae --- /dev/null +++ b/x/bank/spec/03_messages.md @@ -0,0 +1,32 @@ + + +# Messages + +## MsgSend + +```go +// MsgSend represents a message to send coins from one account to another. +message MsgSend { + string from_address = 1; + string to_address = 2; + repeated cosmos.base.v1beta1.Coin amount = 3; +} +``` + +`handleMsgSend` just runs `inputOutputCoins`. + +``` +handleMsgSend(msg MsgSend) + inputSum = 0 + for input in inputs + inputSum += input.Amount + outputSum = 0 + for output in outputs + outputSum += output.Amount + if inputSum != outputSum: + fail with "input/output amount mismatch" + + return inputOutputCoins(msg.Inputs, msg.Outputs) +``` diff --git a/x/bank/spec/04_events.md b/x/bank/spec/04_events.md new file mode 100644 index 000000000000..55c93b805ed1 --- /dev/null +++ b/x/bank/spec/04_events.md @@ -0,0 +1,29 @@ + + +# Events + +The bank module emits the following events: + +## Handlers + +### MsgSend + +| Type | Attribute Key | Attribute Value | +| -------- | ------------- | ------------------ | +| transfer | recipient | {recipientAddress} | +| transfer | amount | {amount} | +| message | module | bank | +| message | action | send | +| message | sender | {senderAddress} | + +### MsgMultiSend + +| Type | Attribute Key | Attribute Value | +| -------- | ------------- | ------------------ | +| transfer | recipient | {recipientAddress} | +| transfer | amount | {amount} | +| message | module | bank | +| message | action | multisend | +| message | sender | {senderAddress} | diff --git a/x/bank/spec/05_params.md b/x/bank/spec/05_params.md new file mode 100644 index 000000000000..8d254243c4bc --- /dev/null +++ b/x/bank/spec/05_params.md @@ -0,0 +1,24 @@ + + +# Parameters + +The bank module contains the following parameters: + +| Key | Type | Example | +| ------------------ | ------------- | ---------------------------------- | +| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] | +| DefaultSendEnabled | bool | true | + +## SendEnabled + +The send enabled parameter is an array of SendEnabled entries mapping coin +denominations to their send_enabled status. Entries in this list take +precedence over the `DefaultSendEnabled` setting. + +## DefaultSendEnabled + +The default send enabled value controls send transfer capability for all +coin denominations unless specifically included in the array of `SendEnabled` +parameters. diff --git a/x/bank/spec/README.md b/x/bank/spec/README.md index 254488945c69..9a1a0afb6edc 100644 --- a/x/bank/spec/README.md +++ b/x/bank/spec/README.md @@ -1,26 +1,13 @@ -# `x/bank` - -## Table of Contents - - + -- **[01. Abstract](#01-abstract)** -- **[02. Concepts](#02-concepts)** - - [Supply](#supply) - - [Module Accounts](#module-accounts) -- **[03. State](#03-state)** -- **[04. Keepers](#04-keepers)** - - [Common Types](#common-types) - - [BaseKeeper](#basekeeper) - - [SendKeeper](#sendkeeper) - - [ViewKeeper](#viewkeeper) -- **[05. Messages](#05-messages)** - - [MsgSend](#msgsend) -- **[06. Events](#06-events)** - - [Handlers](#handlers) -- **[07. Parameters](#07-parameters)** +# `x/bank` -## 01. Abstract +## Abstract This document specifies the bank module of the Cosmos SDK. @@ -35,46 +22,42 @@ supply of all assets used in the application. This module will be used in the Cosmos Hub. -## 02. Concepts - -### Supply +## Supply -The `supply` module: +The `supply` functionality: - passively tracks the total supply of coins within a chain, - provides a pattern for modules to hold/interact with `Coins`, and - introduces the invariant check to verify a chain's total supply. -#### Total Supply +### Total Supply The total `Supply` of the network is equal to the sum of all coins from the account. The total supply is updated every time a `Coin` is minted (eg: as part of the inflation mechanism) or burned (eg: due to slashing or if a governance proposal is vetoed). -### Module Accounts +## Module Accounts -The supply module introduces a new type of `auth.AccountI` interface, called `ModuleAccountI`, which can be used by +The supply functionality introduces a new type of `auth.Account` which can be used by modules to allocate tokens and in special cases mint or burn tokens. At a base level these module accounts are capable of sending/receiving tokens to and from -`auth.AccountI` interfaces and other module accounts. This design replaces previous +`auth.Account`s and other module accounts. This design replaces previous alternative designs where, to hold tokens, modules would burn the incoming tokens from the sender account, and then track those tokens internally. Later, in order to send tokens, the module would need to effectively mint tokens within a destination account. The new design removes duplicate logic between modules to perform this accounting. -The `ModuleAccountI` interface is defined as follows: +The `ModuleAccount` interface is defined as follows: ```go -// ModuleAccountI defines an account interface for modules that hold tokens in -// an escrow. -type ModuleAccountI interface { - AccountI // same methods as the Account interface +type ModuleAccount interface { + auth.Account // same methods as the Account interface - GetName() string // name of the module; used to obtain the address - GetPermissions() []string // permissions of module account - HasPermission(string) bool + GetName() string // name of the module; used to obtain the address + GetPermissions() []string // permissions of module account + HasPermission(string) bool } ``` @@ -82,20 +65,20 @@ type ModuleAccountI interface { > Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed). The supply `Keeper` also introduces new wrapper functions for the auth `Keeper` -and the bank `Keeper` that are related to `ModuleAccountI`s in order to be able +and the bank `Keeper` that are related to `ModuleAccount`s in order to be able to: -- Get and set `ModuleAccountI`s by providing the `Name`. -- Send coins from and to other `ModuleAccountI`s or standard `Account`s +- Get and set `ModuleAccount`s by providing the `Name`. +- Send coins from and to other `ModuleAccount`s or standard `Account`s (`BaseAccount` or `VestingAccount`) by passing only the `Name`. -- `Mint` or `Burn` coins for a `ModuleAccountI` (restricted to its permissions). +- `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions). -#### Permissions +### Permissions -Each `ModuleAccountI` has a different set of permissions that provide different +Each `ModuleAccount` has a different set of permissions that provide different object capabilities to perform certain actions. Permissions need to be registered upon the creation of the supply `Keeper` so that every time a -`ModuleAccountI` calls the allowed functions, the `Keeper` can lookup the +`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the permissions to that specific account and perform or not the action. The available permissions are: @@ -104,217 +87,16 @@ The available permissions are: - `Burner`: allows for a module to burn a specific amount of coins. - `Staking`: allows for a module to delegate and undelegate a specific amount of coins. -## 03. State - -The `x/bank` module keeps state of two primary objects, account balances and the -total supply of all balances. - -- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)` -- Supply: `0x0 -> ProtocolBuffer(Supply)` - -## 04. Keepers - -The bank module provides three different exported keeper interfaces which can be passed to other modules which need to read or update account balances. Modules should use the least-permissive interface which provides the functionality they require. - -Note that you should always review the `bank` module code to ensure that permissions are limited in the way that you expect. - -### Common Types - -#### Input - -An input of a multiparty transfer - -```protobuf -// Input models transaction input. -message Input { - string address = 1; - repeated cosmos.base.v1beta1.Coin coins = 2; -} -``` - -#### Output - -An output of a multiparty transfer. - -```protobuf -// Output models transaction outputs. -message Output { - string address = 1; - repeated cosmos.base.v1beta1.Coin coins = 2; -} -``` - -### BaseKeeper - -The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins. The `BaseKeeper` struct implements the following `Keeper` interface. - -```go -// Keeper defines a module interface that facilitates the transfer of coins -// between accounts. -type Keeper interface { - SendKeeper - - InitGenesis(sdk.Context, types.GenesisState) - ExportGenesis(sdk.Context) *types.GenesisState - - GetSupply(ctx sdk.Context) exported.SupplyI - SetSupply(ctx sdk.Context, supply exported.SupplyI) - - GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata - SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) - IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) - - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - - DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error - UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error - MarshalSupply(supplyI exported.SupplyI) ([]byte, error) - UnmarshalSupply(bz []byte) (exported.SupplyI, error) - - types.QueryServer -} -``` - -### SendKeeper - -The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins). - -```go -// SendKeeper defines a module interface that facilitates the transfer of coins -// between accounts without the possibility of creating coins. -type SendKeeper interface { - ViewKeeper - - InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - - SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error - SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error - - GetParams(ctx sdk.Context) types.Params - SetParams(ctx sdk.Context, params types.Params) - - SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool - SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error - - BlockedAddr(addr sdk.AccAddress) bool -} -``` - -### ViewKeeper - -The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`. - -```go -// ViewKeeper defines a module interface that facilitates read only access to -// account balances. -type ViewKeeper interface { - ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error - HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool - - GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - GetAccountsBalances(ctx sdk.Context) []types.Balance - GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - - IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) - IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) -} -``` - -## 05. Messages - -### MsgSend - -```protobuf -// MsgSend represents a message to send coins from one account to another. -message MsgSend { - string from_address = 1; - string to_address = 2; - repeated cosmos.base.v1beta1.Coin amount = 3; -} -``` - -`handleMsgSend` just runs `inputOutputCoins`. - -``` -handleMsgSend(msg MsgSend) - inputSum = 0 - for input in inputs - inputSum += input.Amount - outputSum = 0 - for output in outputs - outputSum += output.Amount - if inputSum != outputSum: - fail with "input/output amount mismatch" - - return inputOutputCoins(msg.Inputs, msg.Outputs) -``` - -## 06. Events - -The bank module emits the following events: - -### Handlers - -#### MsgSend - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | ------------------ | -| transfer | recipient | {recipientAddress} | -| transfer | amount | {amount} | -| message | module | bank | -| message | action | send | -| message | sender | {senderAddress} | - -#### MsgMultiSend - -| Type | Attribute Key | Attribute Value | -| -------- | ------------- | ------------------ | -| transfer | recipient | {recipientAddress} | -| transfer | amount | {amount} | -| message | module | bank | -| message | action | multisend | -| message | sender | {senderAddress} | - -## 07. Parameters - -The bank module contains the following parameters: - -| Key | Type | Example | -| ------------------ | ------------- | ---------------------------------- | -| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] | -| DefaultSendEnabled | bool | true | - -The corresponding Protobuf message is: - -```protobuf -// Params defines the parameters for the bank module. -message Params { - option = false; - repeated SendEnabled send_enabled = 1; - bool default_send_enabled = 2; -} -``` - -### SendEnabled - -The send enabled parameter is an array of SendEnabled entries mapping coin -denominations to their send_enabled status. Entries in this list take -precedence over the `DefaultSendEnabled` setting. - -### DefaultSendEnabled - -The default send enabled value controls send transfer capability for all -coin denominations unless specifically included in the array of `SendEnabled` -parameters. +## Contents + +1. **[State](01_state.md)** +2. **[Keepers](02_keepers.md)** + - [Common Types](02_keepers.md#common-types) + - [BaseKeeper](02_keepers.md#basekeeper) + - [SendKeeper](02_keepers.md#sendkeeper) + - [ViewKeeper](02_keepers.md#viewkeeper) +3. **[Messages](03_messages.md)** + - [MsgSend](03_messages.md#msgsend) +4. **[Events](04_events.md)** + - [Handlers](04_events.md#handlers) +5. **[Parameters](05_params.md)** diff --git a/x/distribution/simulation/operations.go b/x/distribution/simulation/operations.go index 812982e1ea1d..ff0d0d4ef5e7 100644 --- a/x/distribution/simulation/operations.go +++ b/x/distribution/simulation/operations.go @@ -115,7 +115,7 @@ func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, bk types.BankKeeper, return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -167,7 +167,7 @@ func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, bk types.BankKee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -222,7 +222,7 @@ func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, bk types.Ban return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -277,7 +277,7 @@ func SimulateMsgFundCommunityPool(ak types.AccountKeeper, bk types.BankKeeper, k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } diff --git a/x/evidence/spec/01_concepts.md b/x/evidence/spec/01_concepts.md new file mode 100644 index 000000000000..736e75c9a68e --- /dev/null +++ b/x/evidence/spec/01_concepts.md @@ -0,0 +1,75 @@ + + +# Concepts + +## Evidence + +Any concrete type of evidence submitted to the `x/evidence` module must fulfill the +`Evidence` contract outlined below. Not all concrete types of evidence will fulfill +this contract in the same way and some data may be entirely irrelevant to certain +types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`, +has also been created to define a contract for evidence against malicious validators. + +```go +// Evidence defines the contract which concrete evidence types of misbehavior +// must implement. +type Evidence interface { + Route() string + Type() string + String() string + Hash() tmbytes.HexBytes + ValidateBasic() error + + // Height at which the infraction occurred + GetHeight() int64 +} + +// ValidatorEvidence extends Evidence interface to define contract +// for evidence against malicious validators +type ValidatorEvidence interface { + Evidence + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() sdk.ConsAddress + + // The total power of the malicious validator at time of infraction + GetValidatorPower() int64 + + // The total validator set power at time of infraction + GetTotalPower() int64 +} +``` + +## Registration & Handling + +The `x/evidence` module must first know about all types of evidence it is expected +to handle. This is accomplished by registering the `Route` method in the `Evidence` +contract with what is known as a `Router` (defined below). The `Router` accepts +`Evidence` and attempts to find the corresponding `Handler` for the `Evidence` +via the `Route` method. + +```go +type Router interface { + AddRoute(r string, h Handler) Router + HasRoute(r string) bool + GetRoute(path string) Handler + Seal() + Sealed() bool +} +``` + +The `Handler` (defined below) is responsible for executing the entirety of the +business logic for handling `Evidence`. This typically includes validating the +evidence, both stateless checks via `ValidateBasic` and stateful checks via any +keepers provided to the `Handler`. In addition, the `Handler` may also perform +capabilities such as slashing and jailing a validator. + +```go +// Handler defines an agnostic Evidence handler. The handler is responsible +// for executing all corresponding business logic necessary for verifying the +// evidence as valid. In addition, the Handler may execute any necessary +// slashing and potential jailing. +type Handler func(Context, Evidence) error +``` diff --git a/x/evidence/spec/02_state.md b/x/evidence/spec/02_state.md new file mode 100644 index 000000000000..00d8d05bedff --- /dev/null +++ b/x/evidence/spec/02_state.md @@ -0,0 +1,19 @@ + + +# State + +Currently the `x/evidence` module only stores valid submitted `Evidence` in state. +The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`. + +```protobuf +// GenesisState defines the evidence module's genesis state. +message GenesisState { + // evidence defines all the evidence at genesis. + repeated google.protobuf.Any evidence = 1; +} + +``` + +All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`). diff --git a/x/evidence/spec/03_messages.md b/x/evidence/spec/03_messages.md new file mode 100644 index 000000000000..85a8228905e7 --- /dev/null +++ b/x/evidence/spec/03_messages.md @@ -0,0 +1,48 @@ + + +# Messages + +## MsgSubmitEvidence + +Evidence is submitted through a `MsgSubmitEvidence` message: + +```protobuf +// MsgSubmitEvidence represents a message that supports submitting arbitrary +// Evidence of misbehavior such as equivocation or counterfactual signing. +message MsgSubmitEvidence { + string submitter = 1; + google.protobuf.Any evidence = 2; +} +``` + +Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding +`Handler` registered with the `x/evidence` module's `Router` in order to be processed +and routed correctly. + +Given the `Evidence` is registered with a corresponding `Handler`, it is processed +as follows: + +```go +func SubmitEvidence(ctx Context, evidence Evidence) error { + if _, ok := GetEvidence(ctx, evidence.Hash()); ok { + return ErrEvidenceExists(codespace, evidence.Hash().String()) + } + if !router.HasRoute(evidence.Route()) { + return ErrNoEvidenceHandlerExists(codespace, evidence.Route()) + } + + handler := router.GetRoute(evidence.Route()) + if err := handler(ctx, evidence); err != nil { + return ErrInvalidEvidence(codespace, err.Error()) + } + + SetEvidence(ctx, evidence) + return nil +} +``` + +First, there must not already exist valid submitted `Evidence` of the exact same +type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally, +if there is no error in handling the `Evidence`, it is persisted to state. diff --git a/x/evidence/spec/04_events.md b/x/evidence/spec/04_events.md new file mode 100644 index 000000000000..35fd77b3f5ac --- /dev/null +++ b/x/evidence/spec/04_events.md @@ -0,0 +1,18 @@ + + +# Events + +The `x/evidence` module emits the following events: + +## Handlers + +### MsgSubmitEvidence + +| Type | Attribute Key | Attribute Value | +| --------------- | ------------- | --------------- | +| submit_evidence | evidence_hash | {evidenceHash} | +| message | module | evidence | +| message | sender | {senderAddress} | +| message | action | submit_evidence | diff --git a/x/evidence/spec/05_params.md b/x/evidence/spec/05_params.md new file mode 100644 index 000000000000..4c48b540afdd --- /dev/null +++ b/x/evidence/spec/05_params.md @@ -0,0 +1,7 @@ + + +# Parameters + +The evidence module does not contain any parameters. diff --git a/x/evidence/spec/06_begin_block.md b/x/evidence/spec/06_begin_block.md new file mode 100644 index 000000000000..317b5523ee93 --- /dev/null +++ b/x/evidence/spec/06_begin_block.md @@ -0,0 +1,155 @@ + + +# BeginBlock + +## Evidence Handling + +Tendermint blocks can include +[Evidence](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#evidence), +which indicates that a validator committed malicious behavior. The relevant information is +forwarded to the application as ABCI Evidence in `abci.RequestBeginBlock` so that +the validator an be accordingly punished. + +### Equivocation + +Currently, the SDK handles two types of evidence inside ABCI's `BeginBlock`: + +- `DuplicateVoteEvidence`, +- `LightClientAttackEvidence`. + +These two evidence types are handled the same way by the evidence module. First, the SDK converts the Tendermint concrete evidence type to a SDK `Evidence` interface using `Equivocation` as the concrete type. + +```proto +// Equivocation implements the Evidence interface. +message Equivocation { + int64 height = 1; + google.protobuf.Timestamp time = 2; + int64 power = 3; + string consensus_address = 4; +} +``` + +For some `Equivocation` submitted in `block` to be valid, it must satisfy: + +`Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge` + +Where `Evidence.Timestamp` is the timestamp in the block at height `Evidence.Height` and +`block.Timestamp` is the current block timestamp. + +If valid `Equivocation` evidence is included in a block, the validator's stake is +reduced (slashed) by `SlashFractionDoubleSign`, which is defined by the `x/slashing` module, +of what their stake was when the infraction occurred (rather than when the evidence was discovered). +We want to "follow the stake", i.e. the stake which contributed to the infraction +should be slashed, even if it has since been redelegated or started unbonding. + +In addition, the validator is permanently jailed and tombstoned making it impossible for that +validator to ever re-enter the validator set. + +The `Equivocation` evidence is handled as follows: + +```go +func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) { + logger := k.Logger(ctx) + consAddr := evidence.GetConsensusAddress() + + if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { + // Ignore evidence that cannot be handled. + // + // NOTE: We used to panic with: + // `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`, + // but this couples the expectations of the app to both Tendermint and + // the simulator. Both are expected to provide the full range of + // allowable but none of the disallowed evidence types. Instead of + // getting this coordination right, it is easier to relax the + // constraints and ignore evidence that cannot be handled. + return + } + + // calculate the age of the evidence + infractionHeight := evidence.GetHeight() + infractionTime := evidence.GetTime() + ageDuration := ctx.BlockHeader().Time.Sub(infractionTime) + ageBlocks := ctx.BlockHeader().Height - infractionHeight + + // Reject evidence if the double-sign is too old. Evidence is considered stale + // if the difference in time and number of blocks is greater than the allowed + // parameters defined. + cp := ctx.ConsensusParams() + if cp != nil && cp.Evidence != nil { + if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks { + logger.Info( + "ignored equivocation; evidence too old", + "validator", consAddr, + "infraction_height", infractionHeight, + "max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks, + "infraction_time", infractionTime, + "max_age_duration", cp.Evidence.MaxAgeDuration, + ) + return + } + } + + validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) + if validator == nil || validator.IsUnbonded() { + // Defensive: Simulation doesn't take unbonding periods into account, and + // Tendermint might break this assumption at some point. + return + } + + if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { + panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr)) + } + + // ignore if the validator is already tombstoned + if k.slashingKeeper.IsTombstoned(ctx, consAddr) { + logger.Info( + "ignored equivocation; validator already tombstoned", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + return + } + + logger.Info( + "confirmed equivocation", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + + // We need to retrieve the stake distribution which signed the block, so we + // subtract ValidatorUpdateDelay from the evidence height. + // Note, that this *can* result in a negative "distributionHeight", up to + // -ValidatorUpdateDelay, i.e. at the end of the + // pre-genesis block (none) = at the beginning of the genesis block. + // That's fine since this is just used to filter unbonding delegations & redelegations. + distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay + + // Slash validator. The `power` is the int64 power of the validator as provided + // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via + // ABCI, and now received as evidence. The fraction is passed in to separately + // to slash unbonding and rebonding delegations. + k.slashingKeeper.Slash( + ctx, + consAddr, + k.slashingKeeper.SlashFractionDoubleSign(ctx), + evidence.GetValidatorPower(), distributionHeight, + ) + + // Jail the validator if not already jailed. This will begin unbonding the + // validator if not already unbonding (tombstoned). + if !validator.IsJailed() { + k.slashingKeeper.Jail(ctx, consAddr) + } + + k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) + k.slashingKeeper.Tombstone(ctx, consAddr) +} +``` + +Note, the slashing, jailing, and tombstoning calls are delegated through the `x/slashing` module +which emit informative events and finally delegate calls to the `x/staking` module. Documentation +on slashing and jailing can be found in the [x/staking spec](/.././cosmos-sdk/x/staking/spec/02_state_transitions.md) diff --git a/x/evidence/spec/README.md b/x/evidence/spec/README.md index b94a73ffd14b..dd72780164e7 100644 --- a/x/evidence/spec/README.md +++ b/x/evidence/spec/README.md @@ -1,18 +1,24 @@ + + # `x/evidence` ## Table of Contents -- **[01. Abstract](#01-abstract)** -- **[02. Concepts](#02-concepts)** -- **[03. State](#03-state)** -- **[04. Messages](#04-messages)** -- **[05. Events](#05-events)** -- **[06. Parameters](#06-parameters)** -- **[07. BeginBlock](#07-beginblock)** +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[Messages](03_messages.md)** +4. **[Events](04_events.md)** +5. **[Params](05_params.md)** +6. **[BeginBlock](06_begin_block.md)** -## 01. Abstract +## Abstract `x/evidence` is an implementation of a Cosmos SDK module, per [ADR 009](./../../../docs/architecture/adr-009-evidence-module.md), that allows for the submission and handling of arbitrary evidence of misbehavior such @@ -32,253 +38,3 @@ keeper in order for it to be successfully routed and executed. Each corresponding handler must also fulfill the `Handler` interface contract. The `Handler` for a given `Evidence` type can perform any arbitrary state transitions such as slashing, jailing, and tombstoning. - -## 02. Concepts - -### Evidence - -Any concrete type of evidence submitted to the `x/evidence` module must fulfill the -`Evidence` contract outlined below. Not all concrete types of evidence will fulfill -this contract in the same way and some data may be entirely irrelevant to certain -types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`, has also -been created to define a contract for evidence against malicious validators. - -```go -// Evidence defines the contract which concrete evidence types of misbehavior -// must implement. -type Evidence interface { - Route() string - Type() string - String() string - Hash() tmbytes.HexBytes - ValidateBasic() error - - // Height at which the infraction occurred - GetHeight() int64 -} - -// ValidatorEvidence extends Evidence interface to define contract -// for evidence against malicious validators -type ValidatorEvidence interface { - Evidence - - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() sdk.ConsAddress - - // The total power of the malicious validator at time of infraction - GetValidatorPower() int64 - - // The total validator set power at time of infraction - GetTotalPower() int64 -} -``` - -### Registration & Handling - -The `x/evidence` module must first know about all types of evidence it is expected -to handle. This is accomplished by registering the `Route` method in the `Evidence` -contract with what is known as a `Router` (defined below). The `Router` accepts -`Evidence` and attempts to find the corresponding `Handler` for the `Evidence` -via the `Route` method. - -```go -// Router defines a contract for which any Evidence handling module must -// implement in order to route Evidence to registered Handlers. -type Router interface { - AddRoute(r string, h Handler) Router - HasRoute(r string) bool - GetRoute(path string) Handler - Seal() - Sealed() bool -} -``` - -The `Handler` (defined below) is responsible for executing the entirety of the -business logic for handling `Evidence`. This typically includes validating the -evidence, both stateless checks via `ValidateBasic` and stateful checks via any -keepers provided to the `Handler`. In addition, the `Handler` may also perform -capabilities such as slashing and jailing a validator. - -```go -// Handler defines an agnostic Evidence handler. The handler is responsible -// for executing all corresponding business logic necessary for verifying the -// evidence as valid. In addition, the Handler may execute any necessary -// slashing and potential jailing. -type Handler func(Context, Evidence) error -``` - -## 03. State - -Currently the `x/evidence` module only stores valid submitted `Evidence` in state. -The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`. - -```protobuf -// GenesisState defines the evidence module's genesis state. -message GenesisState { - // evidence defines all the evidence at genesis. - repeated google.protobuf.Any evidence = 1; -} -``` - -All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`). - -## 04. Messages - -### MsgSubmitEvidence - -Evidence is submitted through a `MsgSubmitEvidence` message: - -```protobuf -// MsgSubmitEvidence represents a message that supports submitting arbitrary -// Evidence of misbehavior such as equivocation or counterfactual signing. -message MsgSubmitEvidence { - string submitter = 1; - google.protobuf.Any evidence = 2; -} -``` - -Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding -`Handler` registered with the `x/evidence` module's `Router` in order to be processed -and routed correctly. - -Given the `Evidence` is registered with a corresponding `Handler`, it is processed -as follows: - -```go -func SubmitEvidence(ctx Context, evidence Evidence) error { - if _, ok := GetEvidence(ctx, evidence.Hash()); ok { - return ErrEvidenceExists(codespace, evidence.Hash().String()) - } - if !router.HasRoute(evidence.Route()) { - return ErrNoEvidenceHandlerExists(codespace, evidence.Route()) - } - - handler := router.GetRoute(evidence.Route()) - if err := handler(ctx, evidence); err != nil { - return ErrInvalidEvidence(codespace, err.Error()) - } - - SetEvidence(ctx, evidence) - return nil -} -``` - -First, there must not already exist valid submitted `Evidence` of the exact same -type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally, -if there is no error in handling the `Evidence`, it is persisted to state. - -## 05. Events - -The `x/evidence` module emits the following events: - -### Handlers - -#### MsgSubmitEvidence - -| Type | Attribute Key | Attribute Value | -| --------------- | ------------- | --------------- | -| submit_evidence | evidence_hash | {evidenceHash} | -| message | module | evidence | -| message | sender | {senderAddress} | -| message | action | submit_evidence | - -## 06. Parameters - -The evidence module does not have any parameters. - -## 07. BeginBlock - -### Evidence Handling - -Tendermint blocks can include -[Evidence](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#evidence), -which indicates that a validator committed malicious behavior. The relevant information is -forwarded to the application as ABCI Evidence in `abci.RequestBeginBlock` so that -the validator an be accordingly punished. - -#### Equivocation - -Currently, the evidence module only handles evidence of type `Equivocation` which is derived from -Tendermint's `ABCIEvidenceTypeDuplicateVote` during `BeginBlock`. - -For some `Equivocation` submitted in `block` to be valid, it must satisfy: - -`Evidence.Timestamp >= block.Timestamp - MaxEvidenceAge` - -Where `Evidence.Timestamp` is the timestamp in the block at height `Evidence.Height` and -`block.Timestamp` is the current block timestamp. - -If valid `Equivocation` evidence is included in a block, the validator's stake is -reduced (slashed) by `SlashFractionDoubleSign`, which is defined by the `x/slashing` module, -of what their stake was when the infraction occurred (rather than when the evidence was discovered). -We want to "follow the stake", i.e. the stake which contributed to the infraction -should be slashed, even if it has since been redelegated or started unbonding. - -In addition, the validator is permanently jailed and tombstoned making it impossible for that -validator to ever re-enter the validator set. - -The `Equivocation` evidence is handled as follows: - -```go -func (k Keeper) HandleDoubleSign(ctx Context, evidence Equivocation) { - consAddr := evidence.GetConsensusAddress() - infractionHeight := evidence.GetHeight() - - // calculate the age of the evidence - blockTime := ctx.BlockHeader().Time - age := blockTime.Sub(evidence.GetTime()) - - // reject evidence we cannot handle - if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { - return - } - - // reject evidence if it is too old - if age > k.MaxEvidenceAge(ctx) { - return - } - - // reject evidence if the validator is already unbonded - validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) - if validator == nil || validator.IsUnbonded() { - return - } - - // verify the validator has signing info in order to be slashed and tombstoned - if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { - panic(...) - } - - // reject evidence if the validator is already tombstoned - if k.slashingKeeper.IsTombstoned(ctx, consAddr) { - return - } - - // We need to retrieve the stake distribution which signed the block, so we - // subtract ValidatorUpdateDelay from the evidence height. - // Note, that this *can* result in a negative "distributionHeight", up to - // -ValidatorUpdateDelay, i.e. at the end of the - // pre-genesis block (none) = at the beginning of the genesis block. - // That's fine since this is just used to filter unbonding delegations & redelegations. - distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay - - // Slash validator. The `power` is the int64 power of the validator as provided - // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via - // ABCI, and now received as evidence. The fraction is passed in to separately - // to slash unbonding and rebonding delegations. - k.slashingKeeper.Slash(ctx, consAddr, evidence.GetValidatorPower(), distributionHeight) - - // Jail the validator if not already jailed. This will begin unbonding the - // validator if not already unbonding (tombstoned). - if !validator.IsJailed() { - k.slashingKeeper.Jail(ctx, consAddr) - } - - k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) - k.slashingKeeper.Tombstone(ctx, consAddr) -} -``` - -Note, the slashing, jailing, and tombstoning calls are delegated through the `x/slashing` module -which emit informative events and finally delegate calls to the `x/staking` module. Documentation -on slashing and jailing can be found in the [x/staking spec](/.././cosmos-sdk/x/staking/spec/02_state_transitions.md) diff --git a/x/gov/simulation/operations.go b/x/gov/simulation/operations.go index 13c8bbab4a3a..0dedf648889e 100644 --- a/x/gov/simulation/operations.go +++ b/x/gov/simulation/operations.go @@ -157,7 +157,7 @@ func SimulateMsgSubmitProposal( return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -243,7 +243,7 @@ func SimulateMsgDeposit(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Ke if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -306,7 +306,7 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } diff --git a/x/ibc/applications/transfer/keeper/relay.go b/x/ibc/applications/transfer/keeper/relay.go index 45a51a9a2e8b..c9bc4470d344 100644 --- a/x/ibc/applications/transfer/keeper/relay.go +++ b/x/ibc/applications/transfer/keeper/relay.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/armon/go-metrics" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/x/ibc/core/02-client/keeper/client_test.go b/x/ibc/core/02-client/keeper/client_test.go index eae37c1aa757..d0e8a062544d 100644 --- a/x/ibc/core/02-client/keeper/client_test.go +++ b/x/ibc/core/02-client/keeper/client_test.go @@ -60,14 +60,14 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { heightPlus3 := clienttypes.NewHeight(suite.header.GetHeight().GetVersionNumber(), suite.header.GetHeight().GetVersionHeight()+3) height := suite.header.GetHeight().(clienttypes.Height) - return ibctmtypes.CreateTestHeader(testChainID, heightPlus3, height, suite.header.Header.Time.Add(time.Hour), + return suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus3.VersionHeight), height, suite.header.Header.Time.Add(time.Hour), suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } createPastUpdateFn := func(s *KeeperTestSuite) *ibctmtypes.Header { heightMinus2 := clienttypes.NewHeight(suite.header.GetHeight().GetVersionNumber(), suite.header.GetHeight().GetVersionHeight()-2) heightMinus4 := clienttypes.NewHeight(suite.header.GetHeight().GetVersionNumber(), suite.header.GetHeight().GetVersionHeight()-4) - return ibctmtypes.CreateTestHeader(testChainID, heightMinus2, heightMinus4, suite.header.Header.Time, + return suite.chainA.CreateTMClientHeader(testChainID, int64(heightMinus2.VersionHeight), heightMinus4, suite.header.Header.Time, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) } var ( @@ -393,7 +393,7 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { // Create signer array and ensure it is in same order as bothValSet _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := ibctmtypes.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) altSigners := []tmtypes.PrivValidator{altPrivVal} @@ -412,8 +412,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusting period misbehavior should pass", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -429,8 +429,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior at later height should pass", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -456,8 +456,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehavior at later height with different trusted heights should pass", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -483,8 +483,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusted ConsensusState1 not found", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, altTime, bothValSet, bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), heightPlus3, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -500,8 +500,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "trusted ConsensusState2 not found", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, testClientHeight, altTime, bothValSet, valSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, heightPlus5, heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.VersionHeight), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -523,8 +523,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "client already frozen at earlier height", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), ChainId: testChainID, ClientId: testClientID, }, @@ -543,8 +543,8 @@ func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { { "misbehaviour check failed", &ibctmtypes.Misbehaviour{ - Header1: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, altTime, bothValSet, bothValSet, bothSigners), - Header2: ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), ChainId: testChainID, ClientId: testClientID, }, diff --git a/x/ibc/core/02-client/keeper/keeper_test.go b/x/ibc/core/02-client/keeper/keeper_test.go index 8db04b643249..c2f18311be33 100644 --- a/x/ibc/core/02-client/keeper/keeper_test.go +++ b/x/ibc/core/02-client/keeper/keeper_test.go @@ -94,7 +94,7 @@ func (suite *KeeperTestSuite) SetupTest() { validator := tmtypes.NewValidator(pubKey.(cryptotypes.IntoTmPubKey).AsTmPubKey(), 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) suite.valSetHash = suite.valSet.Hash() - suite.header = ibctmtypes.CreateTestHeader(testChainID, testClientHeight, testClientHeightMinus1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.VersionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) var validators stakingtypes.Validators @@ -319,7 +319,7 @@ func (suite KeeperTestSuite) TestConsensusStateHelpers() { testClientHeightPlus5 := types.NewHeight(0, height+5) - header := ibctmtypes.CreateTestHeader(testClientID, testClientHeightPlus5, testClientHeight, suite.header.Header.Time.Add(time.Minute), + header := suite.chainA.CreateTMClientHeader(testClientID, int64(testClientHeightPlus5.VersionHeight), testClientHeight, suite.header.Header.Time.Add(time.Minute), suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) // mock update functionality diff --git a/x/ibc/core/02-client/types/genesis_test.go b/x/ibc/core/02-client/types/genesis_test.go index 5661fa05f70a..1de91033549f 100644 --- a/x/ibc/core/02-client/types/genesis_test.go +++ b/x/ibc/core/02-client/types/genesis_test.go @@ -53,7 +53,7 @@ func (suite *TypesTestSuite) TestValidateGenesis() { valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) heightMinus1 := types.NewHeight(0, height-1) - header := ibctmtypes.CreateTestHeader(chainID, clientHeight, heightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) + header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.VersionHeight), heightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) testCases := []struct { name string diff --git a/x/ibc/core/02-client/types/msgs_test.go b/x/ibc/core/02-client/types/msgs_test.go index 23c31e3dcbf6..adaf95cde8d6 100644 --- a/x/ibc/core/02-client/types/msgs_test.go +++ b/x/ibc/core/02-client/types/msgs_test.go @@ -57,7 +57,7 @@ func (suite *TypesTestSuite) TestMarshalMsgCreateClient() { { "tendermint client", func() { tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CreateTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, }, @@ -109,7 +109,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { "valid - tendermint client", func() { tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CreateTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, true, @@ -117,7 +117,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { { "invalid tendermint client", func() { - msg, err = types.NewMsgCreateClient("tendermint", &ibctmtypes.ClientState{}, suite.chainA.CreateTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgCreateClient("tendermint", &ibctmtypes.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, false, @@ -133,7 +133,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { "failed to unpack consensus state", func() { tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) - msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CreateTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgCreateClient("tendermint", tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) msg.ConsensusState = nil }, @@ -216,7 +216,7 @@ func (suite *TypesTestSuite) TestMarshalMsgUpdateClient() { }, { "tendermint client", func() { - msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CreateTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, @@ -268,7 +268,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "valid - tendermint header", func() { - msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CreateTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, true, @@ -315,7 +315,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { { "unsupported - localhost", func() { - msg, err = types.NewMsgUpdateClient(exported.Localhost, suite.chainA.CreateTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + msg, err = types.NewMsgUpdateClient(exported.Localhost, suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) suite.Require().NoError(err) }, false, @@ -500,8 +500,8 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() { "tendermint client", func() { height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) - header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.VersionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.VersionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) @@ -558,8 +558,8 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { func() { height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) - header1 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) - header2 := ibctmtypes.CreateTestHeader(suite.chainA.ChainID, height, heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.VersionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.VersionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.ChainID, header1, header2) msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) diff --git a/x/ibc/core/04-channel/types/msgs_test.go b/x/ibc/core/04-channel/types/msgs_test.go index be7c71353c56..3edebb031ba6 100644 --- a/x/ibc/core/04-channel/types/msgs_test.go +++ b/x/ibc/core/04-channel/types/msgs_test.go @@ -20,8 +20,16 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" ) -// define constants used for testing const ( + // valid constatns used for testing + portid = "testportid" + chanid = "testchannel" + cpportid = "testcpport" + cpchanid = "testcpchannel" + + version = "1.0" + + // invalid constants used for testing invalidPort = "(invalidport1)" invalidShortPort = "p" invalidLongPort = "invalidlongportinvalidlongportinvalidlongportidinvalidlongportidinvalid" @@ -51,20 +59,13 @@ var ( invalidProofs1 = exported.Proof(nil) invalidProofs2 = emptyProof - addr1 = sdk.AccAddress("testaddr111111111111") + addr = sdk.AccAddress("testaddr111111111111") emptyAddr sdk.AccAddress - portid = "testportid" - chanid = "testchannel" - cpportid = "testcpport" - cpchanid = "testcpchannel" - connHops = []string{"testconnection"} invalidConnHops = []string{"testconnection", "testconnection"} invalidShortConnHops = []string{invalidShortConnection} invalidLongConnHops = []string{invalidLongConnection} - - addr = sdk.AccAddress("testaddr") ) type TypesTestSuite struct { @@ -103,375 +104,297 @@ func TestTypesTestSuite(t *testing.T) { suite.Run(t, new(TypesTestSuite)) } -// TestMsgChannelOpenInit tests ValidateBasic for MsgChannelOpenInit -func (suite *TypesTestSuite) TestMsgChannelOpenInit() { - testMsgs := []*types.MsgChannelOpenInit{ - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // valid msg - types.NewMsgChannelOpenInit(invalidShortPort, "testchannel", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short port id - types.NewMsgChannelOpenInit(invalidLongPort, "testchannel", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long port id - types.NewMsgChannelOpenInit(invalidPort, "testchannel", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // port id contains non-alpha - types.NewMsgChannelOpenInit("testportid", invalidShortChannel, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too short channel id - types.NewMsgChannelOpenInit("testportid", invalidLongChannel, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // too long channel id - types.NewMsgChannelOpenInit("testportid", invalidChannel, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", addr), // channel id contains non-alpha - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.Order(3), connHops, "testcpport", "testcpchannel", addr), // invalid channel order - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.ORDERED, invalidConnHops, "testcpport", "testcpchannel", addr), // connection hops more than 1 - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", addr), // too short connection id - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", addr), // too long connection id - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", addr), // connection id contains non-alpha - types.NewMsgChannelOpenInit("testportid", "testchannel", "", types.UNORDERED, connHops, "testcpport", "testcpchannel", addr), // empty channel version - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.UNORDERED, connHops, invalidPort, "testcpchannel", addr), // invalid counterparty port id - types.NewMsgChannelOpenInit("testportid", "testchannel", "1.0", types.UNORDERED, connHops, "testcpport", invalidChannel, addr), // invalid counterparty channel id - } - +func (suite *TypesTestSuite) TestMsgChannelOpenInitValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelOpenInit expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, - {testMsgs[7], false, "invalid channel order"}, - {testMsgs[8], false, "connection hops more than 1 "}, - {testMsgs[9], false, "too short connection id"}, - {testMsgs[10], false, "too long connection id"}, - {testMsgs[11], false, "connection id contains non-alpha"}, - {testMsgs[12], true, ""}, - {testMsgs[13], false, "invalid counterparty port id"}, - {testMsgs[14], false, "invalid counterparty channel id"}, + {"", types.NewMsgChannelOpenInit(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, addr), true}, + {"too short port id", types.NewMsgChannelOpenInit(invalidShortPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"too long port id", types.NewMsgChannelOpenInit(invalidLongPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenInit(invalidPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"too short channel id", types.NewMsgChannelOpenInit(portid, invalidShortChannel, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"too long channel id", types.NewMsgChannelOpenInit(portid, invalidLongChannel, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenInit(portid, invalidChannel, version, types.ORDERED, connHops, cpportid, cpchanid, addr), false}, + {"invalid channel order", types.NewMsgChannelOpenInit(portid, chanid, version, types.Order(3), connHops, cpportid, cpchanid, addr), false}, + {"connection hops more than 1 ", types.NewMsgChannelOpenInit(portid, chanid, version, types.ORDERED, invalidConnHops, cpportid, cpchanid, addr), false}, + {"too short connection id", types.NewMsgChannelOpenInit(portid, chanid, version, types.UNORDERED, invalidShortConnHops, cpportid, cpchanid, addr), false}, + {"too long connection id", types.NewMsgChannelOpenInit(portid, chanid, version, types.UNORDERED, invalidLongConnHops, cpportid, cpchanid, addr), false}, + {"connection id contains non-alpha", types.NewMsgChannelOpenInit(portid, chanid, version, types.UNORDERED, []string{invalidConnection}, cpportid, cpchanid, addr), false}, + {"", types.NewMsgChannelOpenInit(portid, chanid, "", types.UNORDERED, connHops, cpportid, cpchanid, addr), true}, + {"invalid counterparty port id", types.NewMsgChannelOpenInit(portid, chanid, version, types.UNORDERED, connHops, invalidPort, cpchanid, addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenInit(portid, chanid, version, types.UNORDERED, connHops, cpportid, invalidChannel, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} + for _, tc := range testCases { + tc := tc -// TestMsgChannelOpenTry tests ValidateBasic for MsgChannelOpenTry -func (suite *TypesTestSuite) TestMsgChannelOpenTry() { - testMsgs := []*types.MsgChannelOpenTry{ - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // valid msg - types.NewMsgChannelOpenTry(invalidShortPort, "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too short port id - types.NewMsgChannelOpenTry(invalidLongPort, "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too long port id - types.NewMsgChannelOpenTry(invalidPort, "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // port id contains non-alpha - types.NewMsgChannelOpenTry("testportid", invalidShortChannel, chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too short channel id - types.NewMsgChannelOpenTry("testportid", invalidLongChannel, chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too long channel id - types.NewMsgChannelOpenTry("testportid", invalidChannel, chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // channel id contains non-alpha - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "", suite.proof, height, addr), // empty counterparty version - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, clienttypes.ZeroHeight(), addr), // suite.proof height is zero - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.Order(4), connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // invalid channel order - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, invalidConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // connection hops more than 1 - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, invalidShortConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too short connection id - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, invalidLongConnHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // too long connection id - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, []string{invalidConnection}, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // connection id contains non-alpha - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "", types.UNORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // empty channel version - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, connHops, invalidPort, "testcpchannel", "1.0", suite.proof, height, addr), // invalid counterparty port id - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, connHops, "testcpport", invalidChannel, "1.0", suite.proof, height, addr), // invalid counterparty channel id - types.NewMsgChannelOpenTry("testportid", "testchannel", chanid, "1.0", types.UNORDERED, connHops, "testcpport", "testcpchannel", "1.0", emptyProof, height, addr), // empty proof - types.NewMsgChannelOpenTry("testportid", "testchannel", "", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // valid proved channel id - types.NewMsgChannelOpenTry("testportid", "testchannel", "differentchannel", "1.0", types.ORDERED, connHops, "testcpport", "testcpchannel", "1.0", suite.proof, height, addr), // different channel id + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } +} +func (suite *TypesTestSuite) TestMsgChannelOpenTryValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelOpenTry expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, - {testMsgs[7], true, ""}, - {testMsgs[8], false, "proof height is zero"}, - {testMsgs[9], false, "invalid channel order"}, - {testMsgs[10], false, "connection hops more than 1 "}, - {testMsgs[11], false, "too short connection id"}, - {testMsgs[12], false, "too long connection id"}, - {testMsgs[13], false, "connection id contains non-alpha"}, - {testMsgs[14], true, ""}, - {testMsgs[15], false, "invalid counterparty port id"}, - {testMsgs[16], false, "invalid counterparty channel id"}, - {testMsgs[17], false, "empty proof"}, - {testMsgs[18], true, "valid empty proved channel id"}, - {testMsgs[19], false, "invalid proved channel id, doesn't match channel id"}, + {"", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenTry(invalidShortPort, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenTry(invalidLongPort, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenTry(invalidPort, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenTry(portid, invalidShortChannel, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenTry(portid, invalidLongChannel, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenTry(portid, invalidChannel, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, "", suite.proof, height, addr), true}, + {"proof height is zero", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"invalid channel order", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.Order(4), connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection hops more than 1 ", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, invalidConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too short connection id", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, invalidShortConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long connection id", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, invalidLongConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection id contains non-alpha", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, []string{invalidConnection}, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, chanid, chanid, "", types.UNORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"invalid counterparty port id", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, connHops, invalidPort, cpchanid, version, suite.proof, height, addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, connHops, cpportid, invalidChannel, version, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelOpenTry(portid, chanid, chanid, version, types.UNORDERED, connHops, cpportid, cpchanid, version, emptyProof, height, addr), false}, + {"valid empty proved channel id", types.NewMsgChannelOpenTry(portid, chanid, "", version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"invalid proved channel id, doesn't match channel id", types.NewMsgChannelOpenTry(portid, chanid, "differentchannel", version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() -// TestMsgChannelOpenAck tests ValidateBasic for MsgChannelOpenAck -func (suite *TypesTestSuite) TestMsgChannelOpenAck() { - testMsgs := []*types.MsgChannelOpenAck{ - types.NewMsgChannelOpenAck("testportid", "testchannel", chanid, "1.0", suite.proof, height, addr), // valid msg - types.NewMsgChannelOpenAck(invalidShortPort, "testchannel", chanid, "1.0", suite.proof, height, addr), // too short port id - types.NewMsgChannelOpenAck(invalidLongPort, "testchannel", chanid, "1.0", suite.proof, height, addr), // too long port id - types.NewMsgChannelOpenAck(invalidPort, "testchannel", chanid, "1.0", suite.proof, height, addr), // port id contains non-alpha - types.NewMsgChannelOpenAck("testportid", invalidShortChannel, chanid, "1.0", suite.proof, height, addr), // too short channel id - types.NewMsgChannelOpenAck("testportid", invalidLongChannel, chanid, "1.0", suite.proof, height, addr), // too long channel id - types.NewMsgChannelOpenAck("testportid", invalidChannel, chanid, "1.0", suite.proof, height, addr), // channel id contains non-alpha - types.NewMsgChannelOpenAck("testportid", "testchannel", chanid, "", suite.proof, height, addr), // empty counterparty version - types.NewMsgChannelOpenAck("testportid", "testchannel", chanid, "1.0", emptyProof, height, addr), // empty proof - types.NewMsgChannelOpenAck("testportid", "testchannel", chanid, "1.0", suite.proof, clienttypes.ZeroHeight(), addr), // proof height is zero - types.NewMsgChannelOpenAck("testportid", "testchannel", invalidShortChannel, "1.0", suite.proof, height, addr), // invalid counterparty channel id + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } +} +func (suite *TypesTestSuite) TestMsgChannelOpenAckValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelOpenAck expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, - {testMsgs[7], true, ""}, - {testMsgs[8], false, "empty proof"}, - {testMsgs[9], false, "proof height is zero"}, - {testMsgs[10], false, "invalid counterparty channel id"}, + {"", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenAck(invalidShortPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenAck(invalidLongPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenAck(invalidPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenAck(portid, invalidShortChannel, chanid, version, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenAck(portid, invalidLongChannel, chanid, version, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenAck(portid, invalidChannel, chanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenAck(portid, chanid, chanid, "", suite.proof, height, addr), true}, + {"empty proof", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenAck(portid, chanid, invalidShortChannel, version, suite.proof, height, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} + for _, tc := range testCases { + tc := tc -// TestMsgChannelOpenConfirm tests ValidateBasic for MsgChannelOpenConfirm -func (suite *TypesTestSuite) TestMsgChannelOpenConfirm() { - testMsgs := []*types.MsgChannelOpenConfirm{ - types.NewMsgChannelOpenConfirm("testportid", "testchannel", suite.proof, height, addr), // valid msg - types.NewMsgChannelOpenConfirm(invalidShortPort, "testchannel", suite.proof, height, addr), // too short port id - types.NewMsgChannelOpenConfirm(invalidLongPort, "testchannel", suite.proof, height, addr), // too long port id - types.NewMsgChannelOpenConfirm(invalidPort, "testchannel", suite.proof, height, addr), // port id contains non-alpha - types.NewMsgChannelOpenConfirm("testportid", invalidShortChannel, suite.proof, height, addr), // too short channel id - types.NewMsgChannelOpenConfirm("testportid", invalidLongChannel, suite.proof, height, addr), // too long channel id - types.NewMsgChannelOpenConfirm("testportid", invalidChannel, suite.proof, height, addr), // channel id contains non-alpha - types.NewMsgChannelOpenConfirm("testportid", "testchannel", emptyProof, height, addr), // empty proof - types.NewMsgChannelOpenConfirm("testportid", "testchannel", suite.proof, clienttypes.ZeroHeight(), addr), // proof height is zero + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } +} +func (suite *TypesTestSuite) TestMsgChannelOpenConfirmValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelOpenConfirm expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, - {testMsgs[7], false, "empty proof"}, - {testMsgs[8], false, "proof height is zero"}, + {"", types.NewMsgChannelOpenConfirm(portid, chanid, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenConfirm(invalidShortPort, chanid, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenConfirm(invalidLongPort, chanid, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenConfirm(invalidPort, chanid, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenConfirm(portid, invalidShortChannel, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenConfirm(portid, invalidChannel, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelOpenConfirm(portid, chanid, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelOpenConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} + for _, tc := range testCases { + tc := tc -// TestMsgChannelCloseInit tests ValidateBasic for MsgChannelCloseInit -func (suite *TypesTestSuite) TestMsgChannelCloseInit() { - testMsgs := []*types.MsgChannelCloseInit{ - types.NewMsgChannelCloseInit("testportid", "testchannel", addr), // valid msg - types.NewMsgChannelCloseInit(invalidShortPort, "testchannel", addr), // too short port id - types.NewMsgChannelCloseInit(invalidLongPort, "testchannel", addr), // too long port id - types.NewMsgChannelCloseInit(invalidPort, "testchannel", addr), // port id contains non-alpha - types.NewMsgChannelCloseInit("testportid", invalidShortChannel, addr), // too short channel id - types.NewMsgChannelCloseInit("testportid", invalidLongChannel, addr), // too long channel id - types.NewMsgChannelCloseInit("testportid", invalidChannel, addr), // channel id contains non-alpha + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } +} +func (suite *TypesTestSuite) TestMsgChannelCloseInitValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelCloseInit expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, + {"", types.NewMsgChannelCloseInit(portid, chanid, addr), true}, + {"too short port id", types.NewMsgChannelCloseInit(invalidShortPort, chanid, addr), false}, + {"too long port id", types.NewMsgChannelCloseInit(invalidLongPort, chanid, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelCloseInit(invalidPort, chanid, addr), false}, + {"too short channel id", types.NewMsgChannelCloseInit(portid, invalidShortChannel, addr), false}, + {"too long channel id", types.NewMsgChannelCloseInit(portid, invalidLongChannel, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelCloseInit(portid, invalidChannel, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } - } -} + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() -// TestMsgChannelCloseConfirm tests ValidateBasic for MsgChannelCloseConfirm -func (suite *TypesTestSuite) TestMsgChannelCloseConfirm() { - testMsgs := []*types.MsgChannelCloseConfirm{ - types.NewMsgChannelCloseConfirm("testportid", "testchannel", suite.proof, height, addr), // valid msg - types.NewMsgChannelCloseConfirm(invalidShortPort, "testchannel", suite.proof, height, addr), // too short port id - types.NewMsgChannelCloseConfirm(invalidLongPort, "testchannel", suite.proof, height, addr), // too long port id - types.NewMsgChannelCloseConfirm(invalidPort, "testchannel", suite.proof, height, addr), // port id contains non-alpha - types.NewMsgChannelCloseConfirm("testportid", invalidShortChannel, suite.proof, height, addr), // too short channel id - types.NewMsgChannelCloseConfirm("testportid", invalidLongChannel, suite.proof, height, addr), // too long channel id - types.NewMsgChannelCloseConfirm("testportid", invalidChannel, suite.proof, height, addr), // channel id contains non-alpha - types.NewMsgChannelCloseConfirm("testportid", "testchannel", emptyProof, height, addr), // empty proof - types.NewMsgChannelCloseConfirm("testportid", "testchannel", suite.proof, clienttypes.ZeroHeight(), addr), // proof height is zero + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } +} +func (suite *TypesTestSuite) TestMsgChannelCloseConfirmValidateBasic() { testCases := []struct { + name string msg *types.MsgChannelCloseConfirm expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "too short port id"}, - {testMsgs[2], false, "too long port id"}, - {testMsgs[3], false, "port id contains non-alpha"}, - {testMsgs[4], false, "too short channel id"}, - {testMsgs[5], false, "too long channel id"}, - {testMsgs[6], false, "channel id contains non-alpha"}, - {testMsgs[7], false, "empty proof"}, - {testMsgs[8], false, "proof height is zero"}, + {"", types.NewMsgChannelCloseConfirm(portid, chanid, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelCloseConfirm(invalidShortPort, chanid, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelCloseConfirm(invalidLongPort, chanid, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelCloseConfirm(invalidPort, chanid, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelCloseConfirm(portid, invalidShortChannel, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelCloseConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelCloseConfirm(portid, invalidChannel, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelCloseConfirm(portid, chanid, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelCloseConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } -// TestMsgRecvPacketType tests Type for MsgRecvPacket. func (suite *TypesTestSuite) TestMsgRecvPacketType() { - msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr1) + msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr) suite.Equal("recv_packet", msg.Type()) } -// TestMsgRecvPacketValidation tests ValidateBasic for MsgRecvPacket -func (suite *TypesTestSuite) TestMsgRecvPacketValidation() { - testMsgs := []*types.MsgRecvPacket{ - types.NewMsgRecvPacket(packet, suite.proof, height, addr1), // valid msg - types.NewMsgRecvPacket(packet, suite.proof, clienttypes.ZeroHeight(), addr1), // proof height is zero - types.NewMsgRecvPacket(packet, emptyProof, height, addr1), // empty proof - types.NewMsgRecvPacket(packet, suite.proof, height, emptyAddr), // missing signer address - types.NewMsgRecvPacket(invalidPacket, suite.proof, height, addr1), // unknown packet - } - +func (suite *TypesTestSuite) TestMsgRecvPacketValidateBasic() { testCases := []struct { + name string msg *types.MsgRecvPacket expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "proof height is zero"}, - {testMsgs[2], false, "proof contain empty proof"}, - {testMsgs[3], false, "missing signer address"}, - {testMsgs[4], false, "invalid packet"}, + {"", types.NewMsgRecvPacket(packet, suite.proof, height, addr), true}, + {"proof height is zero", types.NewMsgRecvPacket(packet, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"proof contain empty proof", types.NewMsgRecvPacket(packet, emptyProof, height, addr), false}, + {"missing signer address", types.NewMsgRecvPacket(packet, suite.proof, height, emptyAddr), false}, + {"invalid packet", types.NewMsgRecvPacket(invalidPacket, suite.proof, height, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.NoError(err, "Msg %d failed: %v", i, err) - } else { - suite.Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.NoError(err) + } else { + suite.Error(err) + } + }) } } -// TestMsgRecvPacketGetSignBytes tests GetSignBytes for MsgRecvPacket func (suite *TypesTestSuite) TestMsgRecvPacketGetSignBytes() { - msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr1) + msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr) res := msg.GetSignBytes() expected := fmt.Sprintf( `{"packet":{"data":%s,"destination_channel":"testcpchannel","destination_port":"testcpport","sequence":"1","source_channel":"testchannel","source_port":"testportid","timeout_height":{"version_height":"100","version_number":"0"},"timeout_timestamp":"100"},"proof":"Co0BCi4KCmljczIzOmlhdmwSA0tFWRobChkKA0tFWRIFVkFMVUUaCwgBGAEgASoDAAICClsKDGljczIzOnNpbXBsZRIMaWF2bFN0b3JlS2V5Gj0KOwoMaWF2bFN0b3JlS2V5EiAcIiDXSHQRSvh/Wa07MYpTK0B4XtbaXtzxBED76xk0WhoJCAEYASABKgEA","proof_height":{"version_height":"1","version_number":"0"},"signer":"%s"}`, string(msg.GetDataSignBytes()), - addr1.String(), + addr.String(), ) suite.Equal(expected, string(res)) } -// TestMsgRecvPacketGetSigners tests GetSigners for MsgRecvPacket func (suite *TypesTestSuite) TestMsgRecvPacketGetSigners() { - msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr1) + msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr) res := msg.GetSigners() expected := "[7465737461646472313131313131313131313131]" suite.Equal(expected, fmt.Sprintf("%v", res)) } -// TestMsgTimeout tests ValidateBasic for MsgTimeout -func (suite *TypesTestSuite) TestMsgTimeout() { - testMsgs := []*types.MsgTimeout{ - types.NewMsgTimeout(packet, 1, suite.proof, height, addr), - types.NewMsgTimeout(packet, 1, suite.proof, clienttypes.ZeroHeight(), addr), - types.NewMsgTimeout(packet, 1, suite.proof, height, emptyAddr), - types.NewMsgTimeout(packet, 1, emptyProof, height, addr), - types.NewMsgTimeout(invalidPacket, 1, suite.proof, height, addr), - } - +func (suite *TypesTestSuite) TestMsgTimeoutValidateBasic() { testCases := []struct { + name string msg *types.MsgTimeout expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "proof height must be > 0"}, - {testMsgs[2], false, "missing signer address"}, - {testMsgs[3], false, "cannot submit an empty proof"}, - {testMsgs[4], false, "invalid packet"}, + {"", types.NewMsgTimeout(packet, 1, suite.proof, height, addr), true}, + {"proof height must be > 0", types.NewMsgTimeout(packet, 1, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"missing signer address", types.NewMsgTimeout(packet, 1, suite.proof, height, emptyAddr), false}, + {"cannot submit an empty proof", types.NewMsgTimeout(packet, 1, emptyProof, height, addr), false}, + {"invalid packet", types.NewMsgTimeout(invalidPacket, 1, suite.proof, height, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } -// TestMsgTimeoutOnClose tests ValidateBasic for MsgTimeoutOnClose -func (suite *TypesTestSuite) TestMsgTimeoutOnClose() { +func (suite *TypesTestSuite) TestMsgTimeoutOnCloseValidateBasic() { testCases := []struct { name string msg sdk.Msg @@ -500,34 +423,30 @@ func (suite *TypesTestSuite) TestMsgTimeoutOnClose() { } } -// TestMsgAcknowledgement tests ValidateBasic for MsgAcknowledgement -func (suite *TypesTestSuite) TestMsgAcknowledgement() { - testMsgs := []*types.MsgAcknowledgement{ - types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, addr), - types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, clienttypes.ZeroHeight(), addr), - types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, emptyAddr), - types.NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, height, addr), - types.NewMsgAcknowledgement(invalidPacket, packet.GetData(), suite.proof, height, addr), - } - +func (suite *TypesTestSuite) TestMsgAcknowledgementValidateBasic() { testCases := []struct { + name string msg *types.MsgAcknowledgement expPass bool - errMsg string }{ - {testMsgs[0], true, ""}, - {testMsgs[1], false, "proof height must be > 0"}, - {testMsgs[2], false, "missing signer address"}, - {testMsgs[3], false, "cannot submit an empty proof"}, - {testMsgs[4], false, "invalid packet"}, + {"", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, addr), true}, + {"proof height must be > 0", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"missing signer address", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, emptyAddr), false}, + {"cannot submit an empty proof", types.NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, height, addr), false}, + {"invalid packet", types.NewMsgAcknowledgement(invalidPacket, packet.GetData(), suite.proof, height, addr), false}, } - for i, tc := range testCases { - err := tc.msg.ValidateBasic() - if tc.expPass { - suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) - } else { - suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) - } + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) } } diff --git a/x/ibc/core/genesis_test.go b/x/ibc/core/genesis_test.go index c5db6ae996df..ffc6912d20d2 100644 --- a/x/ibc/core/genesis_test.go +++ b/x/ibc/core/genesis_test.go @@ -21,6 +21,8 @@ import ( ) func (suite *IBCTestSuite) TestValidateGenesis() { + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + testCases := []struct { name string genState *types.GenesisState @@ -37,7 +39,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), clienttypes.NewIdentifiedClientState( exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), @@ -48,9 +50,9 @@ func (suite *IBCTestSuite) TestValidateGenesis() { clientID, []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( - suite.header.GetHeight().(clienttypes.Height), + header.GetHeight().(clienttypes.Height), ibctmtypes.NewConsensusState( - suite.header.GetTime(), commitmenttypes.NewMerkleRoot(suite.header.Header.AppHash), suite.header.Header.NextValidatorsHash, + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), }, @@ -70,8 +72,8 @@ func (suite *IBCTestSuite) TestValidateGenesis() { []channeltypes.IdentifiedChannel{ channeltypes.NewIdentifiedChannel( port1, channel1, channeltypes.NewChannel( - channeltypes.INIT, channelOrder, - channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, channelVersion, + channeltypes.INIT, channeltypes.ORDERED, + channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, ibctesting.DefaultChannelVersion, ), ), }, @@ -100,7 +102,7 @@ func (suite *IBCTestSuite) TestValidateGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), clienttypes.NewIdentifiedClientState( exported.Localhost, localhosttypes.NewClientState("(chaindID)", clienttypes.ZeroHeight()), @@ -155,6 +157,8 @@ func (suite *IBCTestSuite) TestValidateGenesis() { } func (suite *IBCTestSuite) TestInitGenesis() { + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + testCases := []struct { name string genState *types.GenesisState @@ -169,7 +173,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { ClientGenesis: clienttypes.NewGenesisState( []clienttypes.IdentifiedClientState{ clienttypes.NewIdentifiedClientState( - clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), ), clienttypes.NewIdentifiedClientState( exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), @@ -180,9 +184,9 @@ func (suite *IBCTestSuite) TestInitGenesis() { clientID, []clienttypes.ConsensusStateWithHeight{ clienttypes.NewConsensusStateWithHeight( - suite.header.GetHeight().(clienttypes.Height), + header.GetHeight().(clienttypes.Height), ibctmtypes.NewConsensusState( - suite.header.GetTime(), commitmenttypes.NewMerkleRoot(suite.header.Header.AppHash), suite.header.Header.NextValidatorsHash, + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, ), ), }, @@ -202,8 +206,8 @@ func (suite *IBCTestSuite) TestInitGenesis() { []channeltypes.IdentifiedChannel{ channeltypes.NewIdentifiedChannel( port1, channel1, channeltypes.NewChannel( - channeltypes.INIT, channelOrder, - channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, channelVersion, + channeltypes.INIT, channeltypes.ORDERED, + channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, ibctesting.DefaultChannelVersion, ), ), }, @@ -236,8 +240,7 @@ func (suite *IBCTestSuite) TestInitGenesis() { } } -// TODO: HandlerTestSuite should replace IBCTestSuite -func (suite *HandlerTestSuite) TestExportGenesis() { +func (suite *IBCTestSuite) TestExportGenesis() { testCases := []struct { msg string malleate func() diff --git a/x/ibc/core/handler.go b/x/ibc/core/handler.go index 3d3ea6280fd7..684a7c6a1ca8 100644 --- a/x/ibc/core/handler.go +++ b/x/ibc/core/handler.go @@ -2,6 +2,7 @@ package ibc import ( "github.com/armon/go-metrics" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" diff --git a/x/ibc/core/handler_test.go b/x/ibc/core/handler_test.go index 4616a726b660..712811dbe1e3 100644 --- a/x/ibc/core/handler_test.go +++ b/x/ibc/core/handler_test.go @@ -12,12 +12,28 @@ import ( ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) +const ( + connectionID = "connectionidone" + clientID = "clientidone" + connectionID2 = "connectionidtwo" + clientID2 = "clientidtwo" + + port1 = "firstport" + port2 = "secondport" + + channel1 = "firstchannel" + channel2 = "secondchannel" + + height = 10 +) + var ( timeoutHeight = clienttypes.NewHeight(0, 10000) maxSequence = uint64(10) + clientHeight = clienttypes.NewHeight(0, 10) ) -type HandlerTestSuite struct { +type IBCTestSuite struct { suite.Suite coordinator *ibctesting.Coordinator @@ -27,14 +43,15 @@ type HandlerTestSuite struct { } // SetupTest creates a coordinator with 2 test chains. -func (suite *HandlerTestSuite) SetupTest() { +func (suite *IBCTestSuite) SetupTest() { suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) } -func TestHandlerTestSuite(t *testing.T) { - suite.Run(t, new(HandlerTestSuite)) +func TestIBCTestSuite(t *testing.T) { + suite.Run(t, new(IBCTestSuite)) } // tests the IBC handler receiving a packet on ordered and unordered channels. @@ -42,7 +59,7 @@ func TestHandlerTestSuite(t *testing.T) { // tests high level properties like ordering and basic sanity checks. More // rigorous testing of 'RecvPacket' and 'WriteReceipt' can be found in the // 04-channel/keeper/packet_test.go. -func (suite *HandlerTestSuite) TestHandleRecvPacket() { +func (suite *IBCTestSuite) TestHandleRecvPacket() { var ( packet channeltypes.Packet ) @@ -162,7 +179,7 @@ func (suite *HandlerTestSuite) TestHandleRecvPacket() { // occurs. It test high level properties like ordering and basic sanity // checks. More rigorous testing of 'AcknowledgePacket' and 'AcknowledgementExecuted' // can be found in the 04-channel/keeper/packet_test.go. -func (suite *HandlerTestSuite) TestHandleAcknowledgePacket() { +func (suite *IBCTestSuite) TestHandleAcknowledgePacket() { var ( packet channeltypes.Packet ) @@ -320,7 +337,7 @@ func (suite *HandlerTestSuite) TestHandleAcknowledgePacket() { // high level properties like ordering and basic sanity checks. More // rigorous testing of 'TimeoutPacket' and 'TimeoutExecuted' can be found in // the 04-channel/keeper/timeout_test.go. -func (suite *HandlerTestSuite) TestHandleTimeoutPacket() { +func (suite *IBCTestSuite) TestHandleTimeoutPacket() { var ( packet channeltypes.Packet packetKey []byte @@ -443,7 +460,7 @@ func (suite *HandlerTestSuite) TestHandleTimeoutPacket() { // commitment occurs. It tests high level properties like ordering and basic // sanity checks. More rigorous testing of 'TimeoutOnClose' and //'TimeoutExecuted' can be found in the 04-channel/keeper/timeout_test.go. -func (suite *HandlerTestSuite) TestHandleTimeoutOnClosePacket() { +func (suite *IBCTestSuite) TestHandleTimeoutOnClosePacket() { var ( packet channeltypes.Packet packetKey []byte diff --git a/x/ibc/core/ibc_test.go b/x/ibc/core/ibc_test.go deleted file mode 100644 index ce61d15f26c5..000000000000 --- a/x/ibc/core/ibc_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package ibc_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmtypes "github.com/tendermint/tendermint/types" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" - channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" - ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" - ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" - ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" -) - -const ( - chainID = "chainID" - - connectionID = "connectionidone" - clientID = "clientidone" - connectionID2 = "connectionidtwo" - clientID2 = "clientidtwo" - - port1 = "firstport" - port2 = "secondport" - - channel1 = "firstchannel" - channel2 = "secondchannel" - - channelOrder = channeltypes.ORDERED - channelVersion = "1.0" - - height = 10 - - trustingPeriod time.Duration = time.Hour * 24 * 7 * 2 - ubdPeriod time.Duration = time.Hour * 24 * 7 * 3 - maxClockDrift time.Duration = time.Second * 10 -) - -var clientHeight = clienttypes.NewHeight(0, 10) - -type IBCTestSuite struct { - suite.Suite - - coordinator *ibctesting.Coordinator - - chainA *ibctesting.TestChain - chainB *ibctesting.TestChain - - ctx sdk.Context - app *simapp.SimApp - header *ibctmtypes.Header -} - -func (suite *IBCTestSuite) SetupTest() { - suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) - - suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) - suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) - - isCheckTx := false - suite.app = simapp.Setup(isCheckTx) - - privVal := ibctestingmock.NewPV() - pubKey, err := privVal.GetPubKey() - suite.Require().NoError(err) - - now := time.Now().UTC() - - val := tmtypes.NewValidator(pubKey.(cryptotypes.IntoTmPubKey).AsTmPubKey(), 10) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) - - clientHeightMinus1 := clienttypes.NewHeight(0, height-1) - - suite.header = ibctmtypes.CreateTestHeader(chainID, clientHeight, clientHeightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) - - suite.ctx = suite.app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) -} - -func TestIBCTestSuite(t *testing.T) { - suite.Run(t, new(IBCTestSuite)) -} diff --git a/x/ibc/core/spec/README.md b/x/ibc/core/spec/README.md index f08cc44c4eeb..432aca93e1a6 100644 --- a/x/ibc/core/spec/README.md +++ b/x/ibc/core/spec/README.md @@ -28,8 +28,8 @@ For the general specification please refer to the [Interchain Standards](https:/ As stated above, the IBC implementation on the Cosmos SDK introduces some changes to the general specification, in order to avoid code duplication and to take -advantage of the SDK architectural components such as the `AnteHandler` and -transaction routing through `Handlers`. +advantage of the SDK architectural components such as the transaction routing +through `Handlers`. ### Interchain Standards reference diff --git a/x/ibc/light-clients/06-solomachine/client/cli/tx.go b/x/ibc/light-clients/06-solomachine/client/cli/tx.go index dec55d1622b5..37db768a3025 100644 --- a/x/ibc/light-clients/06-solomachine/client/cli/tx.go +++ b/x/ibc/light-clients/06-solomachine/client/cli/tx.go @@ -6,7 +6,6 @@ import ( "strconv" "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -25,10 +24,11 @@ const ( // NewCreateClientCmd defines the command to create a new solo machine client. func NewCreateClientCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create [client-id] [sequence] [path/to/consensus_state.json]", - Short: "create new solo machine client", - Long: "create a new solo machine client with the specified identifier and consensus state", - Example: fmt.Sprintf("%s tx ibc %s create [client-id] [sequence] [path/to/consensus_state.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Use: "create [client-id] [sequence] [path/to/consensus_state.json]", + Short: "create new solo machine client", + Long: `create a new solo machine client with the specified identifier and public key + - ConsensusState json example: {"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/3SXL2ONYaOkxpdR5P8tHTlSlPv1AwQwSFxKRee5JQW"},"diversifier":"diversifier","timestamp":"10"}`, + Example: fmt.Sprintf("%s tx ibc %s create [client-id] [sequence] [path/to/consensus_state] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) @@ -46,13 +46,16 @@ func NewCreateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var consensusState *types.ConsensusState + // attempt to unmarshal consensus state argument + consensusState := &types.ConsensusState{} if err := cdc.UnmarshalJSON([]byte(args[2]), consensusState); err != nil { + // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(args[1]) + contents, err := ioutil.ReadFile(args[2]) if err != nil { - return errors.New("neither JSON input nor path to .json file were provided") + return errors.Wrap(err, "neither JSON input nor path to .json file for consensus state were provided") } + if err := cdc.UnmarshalJSON(contents, consensusState); err != nil { return errors.Wrap(err, "error unmarshalling consensus state file") } @@ -99,13 +102,15 @@ func NewUpdateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var header *types.Header + header := &types.Header{} if err := cdc.UnmarshalJSON([]byte(args[1]), header); err != nil { + // check for file path if JSON input is not provided contents, err := ioutil.ReadFile(args[1]) if err != nil { - return errors.New("neither JSON input nor path to .json file were provided") + return errors.Wrap(err, "neither JSON input nor path to .json file for header were provided") } + if err := cdc.UnmarshalJSON(contents, header); err != nil { return errors.Wrap(err, "error unmarshalling header file") } @@ -143,13 +148,15 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var m *types.Misbehaviour + m := &types.Misbehaviour{} if err := cdc.UnmarshalJSON([]byte(args[0]), m); err != nil { + // check for file path if JSON input is not provided contents, err := ioutil.ReadFile(args[0]) if err != nil { - return errors.New("neither JSON input nor path to .json file were provided") + return errors.Wrap(err, "neither JSON input nor path to .json file for misbehaviour were provided") } + if err := cdc.UnmarshalJSON(contents, m); err != nil { return errors.Wrap(err, "error unmarshalling misbehaviour file") } diff --git a/x/ibc/light-clients/06-solomachine/spec/01_concepts.md b/x/ibc/light-clients/06-solomachine/spec/01_concepts.md index ae816baaebed..1723c8740eb5 100644 --- a/x/ibc/light-clients/06-solomachine/spec/01_concepts.md +++ b/x/ibc/light-clients/06-solomachine/spec/01_concepts.md @@ -4,13 +4,46 @@ order: 1 # Concepts +## Client State + +The `ClientState` for a solo machine light client stores the latest sequence, the frozen sequence, +the latest consensus state, and client flag indicating if the client should be allowed to be updated +after a governance proposal. + +If the client is not frozen then the frozen sequence is 0. + +## Consensus State + +The consensus states stores the public key, diversifier, and timestamp of the solo machine light client. + +The diversifier is used to prevent accidental misbehaviour if the same public key is used across +different chains with the same client identifier. It should be unique to the chain the light client +is used on. + +## Public Key + +The public key can be a single public key or a multi-signature public key. The public key type used +must fulfill the tendermint public key interface (this will become the SDK public key interface in the +near future). The public key must be registered on the application codec otherwise encoding/decoding +errors will arise. The public key stored in the consensus state is represented as a protobuf `Any`. +This allows for flexibility in what other public key types can be supported in the future. + +## Counterparty Verification + +The solo machine light client can verify counterparty client state, consensus state, connection state, +channel state, packet commitments, packet acknowledgements, packet receipt absence, +and the next sequence receive. At the end of each successful verification call the light +client sequence number will be incremented. + +Successful verification requires the current public key to sign over the proof. + ## Proofs A solo machine proof should verify that the solomachine public key signed over some specified data. The format for generating marshaled proofs for the SDK's implementation of solo machine is as follows: -Construct the data using the associated protobuf definition and marshal it. +1. Construct the data using the associated protobuf definition and marshal it. For example: @@ -23,7 +56,10 @@ data := &ClientStateData{ dataBz, err := cdc.MarshalBinaryBare(data) ``` -Construct the `SignBytes` and marshal it. +The helper functions `...DataBytes()` in [proofs.go](../types/proofs.go) handle this +functionality. + +2. Construct the `SignBytes` and marshal it. For example: @@ -39,12 +75,12 @@ signBytes := &SignBytes{ signBz, err := cdc.MarshalBinaryBare(signBytes) ``` -The helper functions in [proofs.go](../types/proofs.go) handle the above actions. -The `DataType` field is used to disambiguate what type of data was signed because -of potential proto encoding overlap. +The helper functions `...SignBytes()` in [proofs.go](../types/proofs.go) handle this functionality. +The `DataType` field is used to disambiguate what type of data was signed to prevent potential +proto encoding overlap. -Sign the sign bytes. Embed the signatures into either `SingleSignatureData` or -`MultiSignatureData`. Convert the `SignatureData` to proto and marshal it +3. Sign the sign bytes. Embed the signatures into either `SingleSignatureData` or `MultiSignatureData`. +Convert the `SignatureData` to proto and marshal it. For example: @@ -58,8 +94,8 @@ protoSigData := signing.SignatureDataToProto(sigData) bz, err := cdc.MarshalBinaryBare(protoSigData) ``` -Construct a `TimestampedSignatureData` and marshal it. The marshaled result can be -passed in as the proof parameter to the verification functions. +4. Construct a `TimestampedSignatureData` and marshal it. The marshaled result can be passed in +as the proof parameter to the verification functions. For example: @@ -71,3 +107,54 @@ timestampedSignatureData := &types.TimestampedSignatureData{ proof, err := cdc.MarshalBinaryBare(timestampedSignatureData) ``` + +## Updates By Header + +An update by a header will only succeed if: + +- the header provided is parseable to solo machine header +- the header sequence matches the current sequence +- the header timestamp is greater than or equal to the consensus state timestamp +- the currently registered public key generated the proof + +If the update is successful: + +- the public key is updated +- the diversifier is updated +- the timestamp is updated +- the sequence is incremented by 1 +- the new consensus state is set in the client state + +## Updates By Proposal + +An update by a governance proposal will only succeed if: + +- the header provided is parseable to solo machine header +- the `AllowUpdateAfterProposal` client parameter is set to `true` +- the new header public key does not equal the consensus state public key + +If the update is successful: + +- the public key is updated +- the diversifier is updated +- the timestamp is updated +- the sequence is updated +- the new consensus state is set in the client state +- the client is unfrozen (if it was previously frozen) + +## Misbehaviour + +Misbehaviour handling will only succeed if: + +- the misbehaviour provided is parseable to solo machine misbehaviour +- the client is not already frozen +- the current public key signed over two unique data messages at the same sequence and diversifier. + +If the misbehaviour is successfully processed: + +- the client is frozen by setting the frozen sequence to the misbehaviour sequence + +## Upgrades + +Upgrades to solo machine light clients are not supported since an entirely different type of +public key can be set using normal client updates. diff --git a/x/ibc/light-clients/06-solomachine/spec/02_state.md b/x/ibc/light-clients/06-solomachine/spec/02_state.md new file mode 100644 index 000000000000..a9ff4ea5b47c --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/02_state.md @@ -0,0 +1,12 @@ + + +# State + +The solo machine light client will only store consensus states for each update by a header +or a governance proposal. The latest client state is also maintained in the store. + +These values can be found under the light client paths defined in the IBC +[core store specs](../../../core/spec/02_state.md). + diff --git a/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md b/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md new file mode 100644 index 000000000000..3ca4b7017acf --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md @@ -0,0 +1,42 @@ + + +# State Transitions + +## Client State Verification Functions + +Successful state verification by a solo machine light client will result in: + +- the sequence being incremented by 1. + +## Update By Header + +A successful update of a solo machine light client by a header will result in: + +- the public key being updated to the new public key provided by the header. +- the diversifier being updated to the new diviersifier provided by the header. +- the timestamp being updated to the new timestamp provided by the header. +- the sequence being incremented by 1 +- the consensus state being updated (consensus state stores the public key, diversifier, and timestamp) + +## Update By Governance Proposal + +A successful update of a solo machine light client by a governance proposal will result in: + +- the public key being updated to the new public key provided by the header. +- the diversifier being updated to the new diviersifier provided by the header. +- the timestamp being updated to the new timestamp provided by the header. +- the sequence being set to the new sequence provided by the header. +- the consensus state being updated (consensus state stores the public key, diversifier, and timestamp) +- the frozen sequence being set to zero (client is unfrozen if it was previously frozen). + +## Upgrade + +Client udgrades are not supported for the solo machine light client. No state transition occurs. + +## Misbehaviour + +Successful misbehaviour processing of a solo machine light client will result in: + +- the frozen sequence being set to the sequence the misbehaviour occurred at diff --git a/x/ibc/light-clients/06-solomachine/spec/04_messages.md b/x/ibc/light-clients/06-solomachine/spec/04_messages.md new file mode 100644 index 000000000000..465ea6229a77 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/04_messages.md @@ -0,0 +1,8 @@ + + +# Messages + +The messages used to initialize a solo machine light client are defined in the +core sub-module [02-client](../../../core/spec/04_messages.md). diff --git a/x/ibc/light-clients/06-solomachine/spec/README.md b/x/ibc/light-clients/06-solomachine/spec/README.md index 85dd17a30412..77db1bfeee14 100644 --- a/x/ibc/light-clients/06-solomachine/spec/README.md +++ b/x/ibc/light-clients/06-solomachine/spec/README.md @@ -9,10 +9,18 @@ parent: ## Abstract -This paper defines the implementation of the ICS06 protocol on the Cosmos SDK. +This paper defines the implementation of the ICS06 protocol on the Cosmos SDK. For the general +specification please refer to the [ICS06 Specification](https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client). -For the general specification please refer to the [ICS06 Specification](https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client). +This implementation of a solo machine light client supports single and multi-signature public +keys. The client is capable of handling public key updates by header and governance proposals. +The light client is capable of processing client misbehaviour. Proofs of the counterparty state +are generated by the solo machine client by signing over the desired state with a certain sequence, +diversifier, and timestamp. ## Contents 1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[State Transitions](03_state_transitions.md)** +4. **[Messages](04_messages.md)** diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go index 8a54e23b6c43..1f97f323ce81 100644 --- a/x/ibc/light-clients/06-solomachine/types/misbehaviour.go +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go @@ -85,6 +85,9 @@ func (sd SignatureAndData) ValidateBasic() error { if sd.DataType == UNSPECIFIED { return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data type cannot be UNSPECIFIED") } + if sd.Timestamp == 0 { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "timestamp cannot be 0") + } return nil } diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go index 45ae344cdcae..2fcbe75c7a5e 100644 --- a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -49,8 +49,15 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState( // verifySignatureAndData verifies that the currently registered public key has signed // over the provided data and that the data is valid. The data is valid if it can be -// unmarshaled into the specified data type. +// unmarshaled into the specified data type or the timestamp of the signature is less +// than the consensus state timestamp. func verifySignatureAndData(cdc codec.BinaryMarshaler, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { + // timestamp less than consensus state would always fail and not succeed in fooling the + // light client + if sigAndData.Timestamp < clientState.ConsensusState.Timestamp { + return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "timestamp is less than consensus state timestamp (%d < %d)", sigAndData.Timestamp, clientState.ConsensusState.Timestamp) + } + // ensure data can be unmarshaled to the specified data type if _, err := UnmarshalDataByType(cdc, sigAndData.DataType, sigAndData.Data); err != nil { return err @@ -58,7 +65,7 @@ func verifySignatureAndData(cdc codec.BinaryMarshaler, clientState ClientState, data, err := MisbehaviourSignBytes( cdc, - misbehaviour.Sequence, clientState.ConsensusState.Timestamp, + misbehaviour.Sequence, sigAndData.Timestamp, clientState.ConsensusState.Diversifier, sigAndData.DataType, sigAndData.Data, diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go index bec4afaf277b..fd5871adc630 100644 --- a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go @@ -75,6 +75,34 @@ func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { misbehaviour = m }, false, }, + { + "invalid SignatureOne timestamp", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + misbehaviour = m + }, false, + }, + { + "invalid SignatureTwo timestamp", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + misbehaviour = m + }, false, + }, + { + "timestamp is less than consensus state timestamp", + func() { + clientState = solomachine.ClientState() + solomachine.Time = solomachine.Time - 5 + misbehaviour = solomachine.CreateMisbehaviour() + }, false, + }, { "invalid first signature data", func() { diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go index a1138d5d801a..311dc8d8c85d 100644 --- a/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go @@ -96,6 +96,18 @@ func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED }, false, }, + { + "timestamp for SignatureOne is zero", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Timestamp = 0 + }, false, + }, + { + "timestamp for SignatureTwo is zero", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Timestamp = 0 + }, false, + }, } for _, tc := range testCases { diff --git a/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go index 1d4f8cb2624a..833535da16ee 100644 --- a/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go +++ b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go @@ -267,6 +267,7 @@ type SignatureAndData struct { Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } @@ -819,93 +820,93 @@ func init() { } var fileDescriptor_6cc2ee18f7f86d4e = []byte{ - // 1361 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4f, 0x8f, 0xdb, 0x54, - 0x10, 0x5f, 0xa7, 0xe9, 0x76, 0x33, 0xd9, 0x66, 0x83, 0x9b, 0xb6, 0x59, 0xb7, 0x4a, 0x8c, 0x11, - 0x65, 0x41, 0x34, 0x61, 0x8b, 0xa8, 0x50, 0x85, 0x00, 0xc7, 0x31, 0x34, 0xed, 0xae, 0x37, 0x38, - 0x5e, 0xa0, 0x15, 0x92, 0xe5, 0xd8, 0x6f, 0x13, 0x6b, 0x13, 0x3b, 0xc4, 0x4e, 0xd2, 0x20, 0x21, - 0x21, 0x4e, 0x25, 0xe2, 0xc0, 0x91, 0x4b, 0x24, 0x04, 0x42, 0xe2, 0xa3, 0x20, 0x71, 0x29, 0x37, - 0x4e, 0x01, 0xb5, 0xdf, 0x20, 0x9f, 0x00, 0xd9, 0xef, 0x39, 0xb6, 0xb3, 0x6c, 0x56, 0xfc, 0x3b, - 0xe5, 0xbd, 0x99, 0xdf, 0xfc, 0x66, 0xde, 0xbc, 0xc9, 0xbc, 0x31, 0xec, 0x9a, 0x4d, 0xbd, 0xdc, - 0x31, 0x5b, 0x6d, 0x57, 0xef, 0x98, 0xc8, 0x72, 0x9d, 0xb2, 0x63, 0x77, 0xec, 0xae, 0xa6, 0xb7, - 0x4d, 0x0b, 0x95, 0x87, 0xbb, 0xd1, 0x6d, 0xa9, 0xd7, 0xb7, 0x5d, 0x9b, 0x2e, 0x9a, 0x4d, 0xbd, - 0x14, 0x35, 0x29, 0x45, 0x31, 0xc3, 0x5d, 0xe6, 0x25, 0x8f, 0x53, 0xb7, 0xfb, 0xa8, 0xac, 0xdb, - 0x96, 0x85, 0x74, 0xd7, 0xb4, 0x2d, 0x8f, 0x2a, 0xdc, 0x61, 0x26, 0xe6, 0xf9, 0x10, 0xd8, 0xd6, - 0x2c, 0x0b, 0x75, 0x7c, 0x14, 0x5e, 0x12, 0x48, 0xae, 0x65, 0xb7, 0x6c, 0x7f, 0x59, 0xf6, 0x56, - 0x44, 0xba, 0xdd, 0xb2, 0xed, 0x56, 0x07, 0x95, 0xfd, 0x5d, 0x73, 0x70, 0x54, 0xd6, 0xac, 0x31, - 0x56, 0x71, 0xbf, 0x26, 0x20, 0x2d, 0xf8, 0x71, 0x35, 0x5c, 0xcd, 0x45, 0x34, 0x03, 0x1b, 0x0e, - 0xfa, 0x74, 0x80, 0x2c, 0x1d, 0xe5, 0x29, 0x96, 0xda, 0x49, 0xca, 0x8b, 0x3d, 0x2d, 0xc0, 0xd6, - 0x51, 0xdf, 0xfe, 0x0c, 0x59, 0xea, 0x02, 0x92, 0xf0, 0x20, 0x15, 0x66, 0x3e, 0x2b, 0x5e, 0x19, - 0x6b, 0xdd, 0xce, 0x1d, 0x6e, 0x09, 0xc0, 0xc9, 0x19, 0x2c, 0x69, 0x04, 0x24, 0x2e, 0x6c, 0xe9, - 0xb6, 0xe5, 0x20, 0xcb, 0x19, 0x38, 0xaa, 0xe3, 0xf9, 0xcc, 0x9f, 0x63, 0xa9, 0x9d, 0xf4, 0xad, - 0x72, 0xe9, 0x8c, 0x44, 0x95, 0x84, 0xc0, 0xce, 0x0f, 0x35, 0xea, 0x75, 0x89, 0x91, 0x93, 0x33, - 0x7a, 0x0c, 0x4b, 0x23, 0xb8, 0xa6, 0x75, 0x3a, 0xf6, 0x48, 0x1d, 0xf4, 0x0c, 0xcd, 0x45, 0xaa, - 0x76, 0xe4, 0xa2, 0xbe, 0xda, 0xeb, 0xdb, 0x3d, 0xdb, 0xd1, 0x3a, 0xf9, 0x24, 0x4b, 0xed, 0x6c, - 0x54, 0x6e, 0xcc, 0x67, 0x45, 0x0e, 0x13, 0xae, 0x00, 0x73, 0x72, 0xde, 0xd7, 0x1e, 0xfa, 0x4a, - 0xde, 0xd3, 0xd5, 0x89, 0xea, 0x4e, 0xf2, 0xf1, 0x77, 0xc5, 0x35, 0xee, 0x7b, 0x0a, 0x32, 0xf1, - 0x58, 0xe9, 0x7b, 0x00, 0xbd, 0x41, 0xb3, 0x63, 0xea, 0xea, 0x31, 0x1a, 0xfb, 0x89, 0x4d, 0xdf, - 0xca, 0x95, 0xf0, 0xb5, 0x94, 0x82, 0x6b, 0x29, 0xf1, 0xd6, 0xb8, 0x72, 0x79, 0x3e, 0x2b, 0x3e, - 0x87, 0x83, 0x08, 0x2d, 0x38, 0x39, 0x85, 0x37, 0xf7, 0xd1, 0x98, 0x66, 0x21, 0x6d, 0x98, 0x43, - 0xd4, 0x77, 0xcc, 0x23, 0x13, 0xf5, 0xfd, 0x2b, 0x48, 0xc9, 0x51, 0x11, 0x7d, 0x1d, 0x52, 0xae, - 0xd9, 0x45, 0x8e, 0xab, 0x75, 0x7b, 0x7e, 0x76, 0x93, 0x72, 0x28, 0x20, 0x41, 0x7e, 0x99, 0x80, - 0xf5, 0xbb, 0x48, 0x33, 0x50, 0x7f, 0xe5, 0x9d, 0xc7, 0xa8, 0x12, 0x4b, 0x54, 0x9e, 0xd6, 0x31, - 0x5b, 0x96, 0xe6, 0x0e, 0xfa, 0xf8, 0x1a, 0x37, 0xe5, 0x50, 0x40, 0x1f, 0x42, 0xc6, 0x42, 0x23, - 0x35, 0x72, 0xf0, 0xe4, 0x8a, 0x83, 0x6f, 0xcf, 0x67, 0xc5, 0xcb, 0xf8, 0xe0, 0x71, 0x2b, 0x4e, - 0xde, 0xb4, 0xd0, 0xa8, 0xbe, 0x38, 0xbf, 0x00, 0x5b, 0x1e, 0x20, 0x9a, 0x83, 0xf3, 0x5e, 0x0e, - 0xa2, 0x05, 0xb1, 0x04, 0xe0, 0x64, 0x2f, 0x92, 0x6a, 0x28, 0x20, 0x49, 0xf8, 0x25, 0x01, 0x9b, - 0xfb, 0xa6, 0xd3, 0x44, 0x6d, 0x6d, 0x68, 0xda, 0x83, 0x3e, 0xbd, 0x0b, 0x29, 0x5c, 0x7c, 0xaa, - 0x69, 0xf8, 0xb9, 0x48, 0x55, 0x72, 0xf3, 0x59, 0x31, 0x4b, 0xca, 0x2c, 0x50, 0x71, 0xf2, 0x06, - 0x5e, 0xd7, 0x8c, 0x58, 0xf6, 0x12, 0x4b, 0xd9, 0xeb, 0xc1, 0xc5, 0x45, 0x3a, 0x54, 0xdb, 0x0a, - 0x4a, 0x7d, 0xf7, 0xcc, 0x52, 0x6f, 0x04, 0x56, 0xbc, 0x65, 0x54, 0x35, 0x57, 0xab, 0xe4, 0xe7, - 0xb3, 0x62, 0x0e, 0x47, 0x11, 0x63, 0xe4, 0xe4, 0xcd, 0xc5, 0xfe, 0xc0, 0x5a, 0xf2, 0xe8, 0x8e, - 0x6c, 0x92, 0xf2, 0xff, 0xca, 0xa3, 0x3b, 0xb2, 0xa3, 0x1e, 0x95, 0x91, 0x7d, 0x67, 0xc3, 0xcb, - 0xe4, 0xb7, 0x5e, 0x36, 0x7f, 0xa2, 0x20, 0xbb, 0x4c, 0x13, 0x2f, 0x11, 0x6a, 0xb9, 0x44, 0x3e, - 0x81, 0x94, 0xa1, 0xb9, 0x9a, 0xea, 0x8e, 0x7b, 0x38, 0x7b, 0x99, 0x5b, 0x2f, 0x9f, 0x19, 0xaa, - 0xc7, 0xab, 0x8c, 0x7b, 0x28, 0x7a, 0x35, 0x0b, 0x16, 0x4e, 0xde, 0x30, 0x88, 0x9e, 0xa6, 0x21, - 0xe9, 0xad, 0x49, 0x65, 0xfa, 0x6b, 0x72, 0xf1, 0x5f, 0x50, 0x90, 0x57, 0x82, 0x32, 0x46, 0xc6, - 0x22, 0x6a, 0x3f, 0xe4, 0x77, 0x21, 0x13, 0x9e, 0xd8, 0x27, 0xf0, 0xe3, 0x8e, 0x56, 0x68, 0x5c, - 0xcf, 0xc9, 0x61, 0xd2, 0x83, 0x43, 0x9f, 0xfe, 0xaf, 0x21, 0x21, 0xfc, 0x4e, 0x41, 0xca, 0xf3, - 0x5b, 0x19, 0xbb, 0xc8, 0xf9, 0x17, 0xff, 0xc1, 0xa5, 0x76, 0x70, 0xee, 0x64, 0x3b, 0x88, 0x25, - 0x39, 0xf9, 0x7f, 0x25, 0xf9, 0xfc, 0x89, 0x24, 0xff, 0x48, 0x01, 0xe0, 0x16, 0xe3, 0x27, 0x65, - 0x0f, 0xd2, 0xe4, 0x8f, 0x7d, 0x66, 0x13, 0xbc, 0x32, 0x9f, 0x15, 0xe9, 0x58, 0x2f, 0x20, 0x5d, - 0x10, 0x37, 0x82, 0x53, 0xba, 0x40, 0xe2, 0x1f, 0x76, 0x81, 0xcf, 0x61, 0x2b, 0xf2, 0x04, 0xfa, - 0xb1, 0xd2, 0x90, 0xec, 0x69, 0x6e, 0x9b, 0x14, 0xac, 0xbf, 0xa6, 0xeb, 0xb0, 0x49, 0x1a, 0x00, - 0x7e, 0xb6, 0x12, 0x2b, 0x0e, 0x70, 0x75, 0x3e, 0x2b, 0x5e, 0x8a, 0x35, 0x0d, 0xf2, 0x30, 0xa5, - 0xf5, 0xd0, 0x13, 0x71, 0xff, 0x15, 0x05, 0x74, 0xfc, 0xb9, 0x38, 0x35, 0x84, 0x07, 0x27, 0x1f, - 0xcf, 0x55, 0x51, 0xfc, 0x8d, 0x17, 0x92, 0xc4, 0x32, 0x84, 0x4b, 0xc2, 0x62, 0xec, 0x58, 0x1d, - 0x8b, 0x08, 0x10, 0x4e, 0x28, 0x24, 0x8c, 0x17, 0xfd, 0xb2, 0xf2, 0x46, 0x94, 0x52, 0x64, 0x7a, - 0xc1, 0x4f, 0x37, 0xd9, 0x89, 0x96, 0x21, 0x47, 0x0c, 0x89, 0x5f, 0x03, 0xb2, 0x02, 0x1e, 0x64, - 0x56, 0x3b, 0xbd, 0x0d, 0x17, 0xc8, 0xc0, 0x43, 0x3c, 0x5e, 0x8f, 0x78, 0x24, 0x93, 0x90, 0xe7, - 0x0e, 0x2f, 0xe5, 0x00, 0x4c, 0xbc, 0xdc, 0x83, 0x5c, 0x5d, 0xd3, 0x8f, 0x91, 0x2b, 0xd8, 0xdd, - 0xae, 0xe9, 0x76, 0x91, 0xe5, 0x9e, 0xea, 0xa9, 0xe0, 0x1d, 0x2f, 0x40, 0xf9, 0xce, 0x36, 0xe5, - 0x88, 0x84, 0x7b, 0x00, 0xdb, 0x98, 0x8b, 0xd7, 0x8f, 0x2d, 0x7b, 0xd4, 0x41, 0x46, 0x0b, 0xad, - 0x24, 0xdc, 0x81, 0x2d, 0x2d, 0x0e, 0x25, 0xac, 0xcb, 0x62, 0xae, 0x04, 0x79, 0x4c, 0x2d, 0x23, - 0x1d, 0x99, 0x3d, 0x97, 0x6f, 0x3a, 0x5e, 0x1f, 0x38, 0x8d, 0x99, 0x6b, 0x43, 0x4e, 0x42, 0x8f, - 0xdc, 0x60, 0xc4, 0x92, 0x91, 0x3e, 0x3c, 0x35, 0x8a, 0xb7, 0xe0, 0xa2, 0x85, 0x1e, 0xb9, 0xde, - 0x80, 0xa6, 0xf6, 0x91, 0x3e, 0x24, 0x13, 0x5c, 0xa4, 0xd9, 0xc7, 0xd4, 0x9c, 0x9c, 0xb6, 0x30, - 0xb5, 0xc7, 0xfa, 0xca, 0xd7, 0x49, 0xd8, 0x08, 0x1a, 0x03, 0xfd, 0x26, 0xbc, 0x50, 0xe5, 0x15, - 0x5e, 0x55, 0x1e, 0xd4, 0x45, 0xf5, 0x50, 0xaa, 0x49, 0x35, 0xa5, 0xc6, 0xef, 0xd5, 0x1e, 0x8a, - 0x55, 0xf5, 0x50, 0x6a, 0xd4, 0x45, 0xa1, 0xf6, 0x5e, 0x4d, 0xac, 0x66, 0xd7, 0x98, 0xad, 0xc9, - 0x94, 0x4d, 0x47, 0x44, 0xf4, 0x0d, 0xb8, 0x12, 0x5a, 0x0a, 0x7b, 0x35, 0x51, 0x52, 0xd4, 0x86, - 0xc2, 0x2b, 0x62, 0x96, 0x62, 0x60, 0x32, 0x65, 0xd7, 0xb1, 0x8c, 0x7e, 0x15, 0xb6, 0x23, 0xb8, - 0x03, 0xa9, 0x21, 0x4a, 0x8d, 0xc3, 0x06, 0x81, 0x26, 0x98, 0x8b, 0x93, 0x29, 0x9b, 0x5a, 0x88, - 0xe9, 0x12, 0x30, 0x31, 0xb4, 0x24, 0x0a, 0x4a, 0xed, 0x40, 0x22, 0xf0, 0x73, 0x4c, 0x66, 0x32, - 0x65, 0x21, 0x94, 0xd3, 0x3b, 0x70, 0x35, 0x82, 0xbf, 0xcb, 0x4b, 0x92, 0xb8, 0x47, 0xc0, 0x49, - 0x26, 0x3d, 0x99, 0xb2, 0x17, 0x88, 0x90, 0x7e, 0x03, 0xae, 0x85, 0xc8, 0x3a, 0x2f, 0xdc, 0x17, - 0x15, 0x55, 0x38, 0xd8, 0xdf, 0xaf, 0x29, 0xfb, 0xa2, 0xa4, 0x64, 0xcf, 0x33, 0xb9, 0xc9, 0x94, - 0xcd, 0x62, 0x45, 0x28, 0xa7, 0xdf, 0x01, 0xf6, 0x84, 0x19, 0x2f, 0xdc, 0x97, 0x0e, 0x3e, 0xda, - 0x13, 0xab, 0xef, 0x8b, 0xbe, 0xed, 0x3a, 0xb3, 0x3d, 0x99, 0xb2, 0x97, 0xb1, 0x76, 0x49, 0x49, - 0xbf, 0xfd, 0x17, 0x04, 0xb2, 0x28, 0x88, 0xb5, 0xba, 0xa2, 0xf2, 0x95, 0x86, 0x28, 0x09, 0x62, - 0xf6, 0x02, 0x93, 0x9f, 0x4c, 0xd9, 0x1c, 0xd6, 0x12, 0x25, 0xd1, 0xd1, 0xb7, 0xe1, 0x7a, 0x68, - 0x2f, 0x89, 0x1f, 0x2b, 0x6a, 0x43, 0xfc, 0xe0, 0xd0, 0x53, 0x79, 0x34, 0x1f, 0x66, 0x37, 0x70, - 0xe0, 0x9e, 0x26, 0x50, 0x78, 0x72, 0x9a, 0x85, 0x6c, 0x68, 0x77, 0x57, 0xe4, 0xab, 0xa2, 0x9c, - 0x4d, 0xe1, 0x9b, 0xc1, 0x3b, 0x26, 0xf9, 0xf8, 0x87, 0xc2, 0x5a, 0x45, 0xfd, 0xf9, 0x69, 0x81, - 0x7a, 0xf2, 0xb4, 0x40, 0xfd, 0xf1, 0xb4, 0x40, 0x7d, 0xf3, 0xac, 0xb0, 0xf6, 0xe4, 0x59, 0x61, - 0xed, 0xb7, 0x67, 0x85, 0xb5, 0x87, 0x62, 0xcb, 0x74, 0xdb, 0x83, 0x66, 0x49, 0xb7, 0xbb, 0x65, - 0xdd, 0x76, 0xba, 0xb6, 0x43, 0x7e, 0x6e, 0x3a, 0xc6, 0x71, 0xf9, 0x51, 0x79, 0xf1, 0x19, 0x75, - 0x33, 0xf8, 0x8e, 0x7a, 0xed, 0xf6, 0xcd, 0xe8, 0xa7, 0x94, 0xf7, 0xca, 0x38, 0xcd, 0x75, 0xbf, - 0x9d, 0xbd, 0xfe, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, 0xb2, 0xd0, 0x69, 0x77, 0x0d, 0x00, - 0x00, + // 1368 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdf, 0x8f, 0xdb, 0xc4, + 0x13, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xe6, 0xf2, 0x75, 0xd3, 0x36, 0xe7, 0x56, 0x89, 0xbf, + 0x46, 0x94, 0x03, 0xd1, 0x84, 0x2b, 0xa2, 0x42, 0x15, 0x02, 0x1c, 0xc7, 0xd0, 0xb4, 0x77, 0xbe, + 0xe0, 0xf8, 0x80, 0x56, 0x48, 0x96, 0xe3, 0xec, 0x25, 0xd6, 0x25, 0x76, 0x88, 0x9d, 0xa4, 0x41, + 0x42, 0x42, 0x3c, 0x95, 0x88, 0x07, 0x1e, 0x79, 0x89, 0x84, 0x40, 0xfc, 0x2b, 0x08, 0x89, 0x97, + 0xf2, 0xc6, 0x53, 0x40, 0xed, 0x7f, 0x90, 0xbf, 0x00, 0xad, 0x77, 0x13, 0xdb, 0xb9, 0x5e, 0x4e, + 0xfc, 0x7a, 0xca, 0xee, 0xcc, 0x67, 0x3e, 0x33, 0x3b, 0x33, 0x99, 0x5d, 0xc3, 0xae, 0x55, 0x37, + 0x8b, 0x6d, 0xab, 0xd9, 0xf2, 0xcc, 0xb6, 0x85, 0x6c, 0xcf, 0x2d, 0xba, 0x4e, 0xdb, 0xe9, 0x18, + 0x66, 0xcb, 0xb2, 0x51, 0x71, 0xb0, 0x1b, 0xde, 0x16, 0xba, 0x3d, 0xc7, 0x73, 0xd8, 0xbc, 0x55, + 0x37, 0x0b, 0x61, 0x93, 0x42, 0x18, 0x33, 0xd8, 0xe5, 0x5e, 0xc2, 0x9c, 0xa6, 0xd3, 0x43, 0x45, + 0xd3, 0xb1, 0x6d, 0x64, 0x7a, 0x96, 0x63, 0x63, 0xaa, 0x60, 0x47, 0x98, 0xb8, 0xff, 0x07, 0xc0, + 0x96, 0x61, 0xdb, 0xa8, 0xed, 0xa3, 0xc8, 0x92, 0x42, 0x32, 0x4d, 0xa7, 0xe9, 0xf8, 0xcb, 0x22, + 0x5e, 0x51, 0xe9, 0x76, 0xd3, 0x71, 0x9a, 0x6d, 0x54, 0xf4, 0x77, 0xf5, 0xfe, 0x51, 0xd1, 0xb0, + 0x47, 0x44, 0x25, 0xfc, 0x1a, 0x83, 0xa4, 0xe4, 0xc7, 0x55, 0xf3, 0x0c, 0x0f, 0xb1, 0x1c, 0x6c, + 0xb8, 0xe8, 0xd3, 0x3e, 0xb2, 0x4d, 0x94, 0x65, 0x78, 0x66, 0x27, 0xae, 0x2e, 0xf6, 0xac, 0x04, + 0x5b, 0x47, 0x3d, 0xe7, 0x33, 0x64, 0xeb, 0x0b, 0x48, 0x0c, 0x43, 0x4a, 0xdc, 0x6c, 0x9a, 0xbf, + 0x32, 0x32, 0x3a, 0xed, 0x3b, 0xc2, 0x12, 0x40, 0x50, 0x53, 0x44, 0x52, 0x9b, 0x93, 0x78, 0xb0, + 0x65, 0x3a, 0xb6, 0x8b, 0x6c, 0xb7, 0xef, 0xea, 0x2e, 0xf6, 0x99, 0x3d, 0xc7, 0x33, 0x3b, 0xc9, + 0x5b, 0xc5, 0xc2, 0x19, 0x89, 0x2a, 0x48, 0x73, 0x3b, 0x3f, 0xd4, 0xb0, 0xd7, 0x25, 0x46, 0x41, + 0x4d, 0x99, 0x11, 0x2c, 0x8b, 0xe0, 0x9a, 0xd1, 0x6e, 0x3b, 0x43, 0xbd, 0xdf, 0x6d, 0x18, 0x1e, + 0xd2, 0x8d, 0x23, 0x0f, 0xf5, 0xf4, 0x6e, 0xcf, 0xe9, 0x3a, 0xae, 0xd1, 0xce, 0xc6, 0x79, 0x66, + 0x67, 0xa3, 0x74, 0x63, 0x36, 0xcd, 0x0b, 0x84, 0x70, 0x05, 0x58, 0x50, 0xb3, 0xbe, 0xf6, 0xd0, + 0x57, 0x8a, 0x58, 0x57, 0xa5, 0xaa, 0x3b, 0xf1, 0xc7, 0xdf, 0xe5, 0xd7, 0x84, 0xef, 0x19, 0x48, + 0x45, 0x63, 0x65, 0xef, 0x01, 0x74, 0xfb, 0xf5, 0xb6, 0x65, 0xea, 0xc7, 0x68, 0xe4, 0x27, 0x36, + 0x79, 0x2b, 0x53, 0x20, 0x65, 0x29, 0xcc, 0xcb, 0x52, 0x10, 0xed, 0x51, 0xe9, 0xf2, 0x6c, 0x9a, + 0xff, 0x1f, 0x09, 0x22, 0xb0, 0x10, 0xd4, 0x04, 0xd9, 0xdc, 0x47, 0x23, 0x96, 0x87, 0x64, 0xc3, + 0x1a, 0xa0, 0x9e, 0x6b, 0x1d, 0x59, 0xa8, 0xe7, 0x97, 0x20, 0xa1, 0x86, 0x45, 0xec, 0x75, 0x48, + 0x78, 0x56, 0x07, 0xb9, 0x9e, 0xd1, 0xe9, 0xfa, 0xd9, 0x8d, 0xab, 0x81, 0x80, 0x06, 0xf9, 0x65, + 0x0c, 0xd6, 0xef, 0x22, 0xa3, 0x81, 0x7a, 0x2b, 0x6b, 0x1e, 0xa1, 0x8a, 0x2d, 0x51, 0x61, 0xad, + 0x6b, 0x35, 0x6d, 0xc3, 0xeb, 0xf7, 0x48, 0x19, 0x37, 0xd5, 0x40, 0xc0, 0x1e, 0x42, 0xca, 0x46, + 0x43, 0x3d, 0x74, 0xf0, 0xf8, 0x8a, 0x83, 0x6f, 0xcf, 0xa6, 0xf9, 0xcb, 0xe4, 0xe0, 0x51, 0x2b, + 0x41, 0xdd, 0xb4, 0xd1, 0xb0, 0xba, 0x38, 0xbf, 0x04, 0x5b, 0x18, 0x10, 0xce, 0xc1, 0x79, 0x9c, + 0x83, 0x70, 0x43, 0x2c, 0x01, 0x04, 0x15, 0x47, 0x52, 0x0e, 0x04, 0x34, 0x09, 0xbf, 0xc4, 0x60, + 0x73, 0xdf, 0x72, 0xeb, 0xa8, 0x65, 0x0c, 0x2c, 0xa7, 0xdf, 0x63, 0x77, 0x21, 0x41, 0x9a, 0x4f, + 0xb7, 0x1a, 0x7e, 0x2e, 0x12, 0xa5, 0xcc, 0x6c, 0x9a, 0x4f, 0xd3, 0x36, 0x9b, 0xab, 0x04, 0x75, + 0x83, 0xac, 0x2b, 0x8d, 0x48, 0xf6, 0x62, 0x4b, 0xd9, 0xeb, 0xc2, 0xc5, 0x45, 0x3a, 0x74, 0xc7, + 0x9e, 0xb7, 0xfa, 0xee, 0x99, 0xad, 0x5e, 0x9b, 0x5b, 0x89, 0x76, 0xa3, 0x6c, 0x78, 0x46, 0x29, + 0x3b, 0x9b, 0xe6, 0x33, 0x24, 0x8a, 0x08, 0xa3, 0xa0, 0x6e, 0x2e, 0xf6, 0x07, 0xf6, 0x92, 0x47, + 0x6f, 0xe8, 0xd0, 0x94, 0xff, 0x5b, 0x1e, 0xbd, 0xa1, 0x13, 0xf6, 0xa8, 0x0d, 0x9d, 0x3b, 0x1b, + 0x38, 0x93, 0xdf, 0xe2, 0x6c, 0xfe, 0xc4, 0x40, 0x7a, 0x99, 0x26, 0xda, 0x22, 0xcc, 0x72, 0x8b, + 0x7c, 0x02, 0x89, 0x86, 0xe1, 0x19, 0xba, 0x37, 0xea, 0x92, 0xec, 0xa5, 0x6e, 0xbd, 0x7c, 0x66, + 0xa8, 0x98, 0x57, 0x1b, 0x75, 0x51, 0xb8, 0x34, 0x0b, 0x16, 0x41, 0xdd, 0x68, 0x50, 0x3d, 0xcb, + 0x42, 0x1c, 0xaf, 0x69, 0x67, 0xfa, 0xeb, 0x68, 0x43, 0xc7, 0x9f, 0xff, 0xdf, 0xf8, 0x82, 0x81, + 0xac, 0x36, 0x97, 0xa1, 0xc6, 0xe2, 0x4c, 0xfe, 0x81, 0xde, 0x85, 0x54, 0x90, 0x0f, 0x9f, 0xde, + 0x3f, 0x55, 0xb8, 0x7f, 0xa3, 0x7a, 0x41, 0x0d, 0x4a, 0x52, 0x3e, 0x11, 0x42, 0xec, 0xf9, 0x21, + 0xfc, 0xce, 0x40, 0x02, 0xfb, 0x2d, 0x8d, 0x3c, 0xe4, 0xfe, 0x83, 0x7f, 0xe8, 0xd2, 0xb0, 0x38, + 0x77, 0x72, 0x58, 0x44, 0x4a, 0x10, 0xff, 0xaf, 0x4a, 0x70, 0x3e, 0x28, 0x01, 0x3d, 0xe1, 0x8f, + 0x0c, 0x00, 0x19, 0x40, 0x7e, 0x52, 0xf6, 0x20, 0x49, 0xff, 0xf6, 0x67, 0x8e, 0xc8, 0x2b, 0xb3, + 0x69, 0x9e, 0x8d, 0x4c, 0x0a, 0x3a, 0x23, 0xc9, 0x98, 0x38, 0x65, 0x46, 0xc4, 0xfe, 0xe6, 0x8c, + 0xf8, 0x1c, 0xb6, 0x42, 0x17, 0xa4, 0x1f, 0x2b, 0x0b, 0xf1, 0xae, 0xe1, 0xb5, 0x68, 0x3b, 0xfb, + 0x6b, 0xb6, 0x0a, 0x9b, 0x74, 0x3c, 0x90, 0x4b, 0x2d, 0xb6, 0xe2, 0x00, 0x57, 0x67, 0xd3, 0xfc, + 0xa5, 0xc8, 0x48, 0xa1, 0xd7, 0x56, 0xd2, 0x0c, 0x3c, 0x51, 0xf7, 0x5f, 0x31, 0xc0, 0x46, 0x2f, + 0x93, 0x53, 0x43, 0x78, 0x70, 0xf2, 0x6a, 0x5d, 0x15, 0xc5, 0x5f, 0xb8, 0x3f, 0x69, 0x2c, 0x03, + 0xb8, 0x24, 0x2d, 0x1e, 0x25, 0xab, 0x63, 0x91, 0x01, 0x82, 0xf7, 0x0b, 0x0d, 0xe3, 0x45, 0xbf, + 0xad, 0xf0, 0x03, 0xa6, 0x10, 0x7a, 0xdb, 0x90, 0x8b, 0x9d, 0xee, 0x64, 0xbb, 0xa1, 0x86, 0x0c, + 0xa9, 0xdf, 0x06, 0xa4, 0x25, 0xf2, 0xcc, 0x59, 0xed, 0xf4, 0x36, 0x5c, 0xa0, 0xcf, 0x21, 0xea, + 0xf1, 0x7a, 0xc8, 0x23, 0x7d, 0x27, 0x61, 0x77, 0x64, 0xa9, 0xce, 0xc1, 0xd4, 0xcb, 0x3d, 0xc8, + 0x54, 0x0d, 0xf3, 0x18, 0x79, 0x92, 0xd3, 0xe9, 0x58, 0x5e, 0x07, 0xd9, 0xde, 0xa9, 0x9e, 0x72, + 0xf8, 0x78, 0x73, 0x94, 0xef, 0x6c, 0x53, 0x0d, 0x49, 0x84, 0x07, 0xb0, 0x4d, 0xb8, 0x44, 0xf3, + 0xd8, 0x76, 0x86, 0x6d, 0xd4, 0x68, 0xa2, 0x95, 0x84, 0x3b, 0xb0, 0x65, 0x44, 0xa1, 0x94, 0x75, + 0x59, 0x2c, 0x14, 0x20, 0x4b, 0xa8, 0x55, 0x64, 0x22, 0xab, 0xeb, 0x89, 0x75, 0x17, 0xcf, 0x81, + 0xd3, 0x98, 0x85, 0x16, 0x64, 0x14, 0xf4, 0xc8, 0x9b, 0x3f, 0xc0, 0x54, 0x64, 0x0e, 0x4e, 0x8d, + 0xe2, 0x2d, 0xb8, 0x68, 0xa3, 0x47, 0x1e, 0x7e, 0xbe, 0xe9, 0x3d, 0x64, 0x0e, 0xe8, 0xfb, 0x2e, + 0x74, 0x15, 0x44, 0xd4, 0x82, 0x9a, 0xb4, 0x09, 0x35, 0x66, 0x7d, 0xe5, 0xeb, 0x38, 0x6c, 0xcc, + 0x07, 0x03, 0xfb, 0x26, 0xbc, 0x50, 0x16, 0x35, 0x51, 0xd7, 0x1e, 0x54, 0x65, 0xfd, 0x50, 0xa9, + 0x28, 0x15, 0xad, 0x22, 0xee, 0x55, 0x1e, 0xca, 0x65, 0xfd, 0x50, 0xa9, 0x55, 0x65, 0xa9, 0xf2, + 0x5e, 0x45, 0x2e, 0xa7, 0xd7, 0xb8, 0xad, 0xf1, 0x84, 0x4f, 0x86, 0x44, 0xec, 0x0d, 0xb8, 0x12, + 0x58, 0x4a, 0x7b, 0x15, 0x59, 0xd1, 0xf4, 0x9a, 0x26, 0x6a, 0x72, 0x9a, 0xe1, 0x60, 0x3c, 0xe1, + 0xd7, 0x89, 0x8c, 0x7d, 0x15, 0xb6, 0x43, 0xb8, 0x03, 0xa5, 0x26, 0x2b, 0xb5, 0xc3, 0x1a, 0x85, + 0xc6, 0xb8, 0x8b, 0xe3, 0x09, 0x9f, 0x58, 0x88, 0xd9, 0x02, 0x70, 0x11, 0xb4, 0x22, 0x4b, 0x5a, + 0xe5, 0x40, 0xa1, 0xf0, 0x73, 0x5c, 0x6a, 0x3c, 0xe1, 0x21, 0x90, 0xb3, 0x3b, 0x70, 0x35, 0x84, + 0xbf, 0x2b, 0x2a, 0x8a, 0xbc, 0x47, 0xc1, 0x71, 0x2e, 0x39, 0x9e, 0xf0, 0x17, 0xa8, 0x90, 0x7d, + 0x03, 0xae, 0x05, 0xc8, 0xaa, 0x28, 0xdd, 0x97, 0x35, 0x5d, 0x3a, 0xd8, 0xdf, 0xaf, 0x68, 0xfb, + 0xb2, 0xa2, 0xa5, 0xcf, 0x73, 0x99, 0xf1, 0x84, 0x4f, 0x13, 0x45, 0x20, 0x67, 0xdf, 0x01, 0xfe, + 0x84, 0x99, 0x28, 0xdd, 0x57, 0x0e, 0x3e, 0xda, 0x93, 0xcb, 0xef, 0xcb, 0xbe, 0xed, 0x3a, 0xb7, + 0x3d, 0x9e, 0xf0, 0x97, 0x89, 0x76, 0x49, 0xc9, 0xbe, 0xfd, 0x1c, 0x02, 0x55, 0x96, 0xe4, 0x4a, + 0x55, 0xd3, 0xc5, 0x52, 0x4d, 0x56, 0x24, 0x39, 0x7d, 0x81, 0xcb, 0x8e, 0x27, 0x7c, 0x86, 0x68, + 0xa9, 0x92, 0xea, 0xd8, 0xdb, 0x70, 0x3d, 0xb0, 0x57, 0xe4, 0x8f, 0x35, 0xbd, 0x26, 0x7f, 0x70, + 0x88, 0x55, 0x98, 0xe6, 0xc3, 0xf4, 0x06, 0x09, 0x1c, 0x6b, 0xe6, 0x0a, 0x2c, 0x67, 0x79, 0x48, + 0x07, 0x76, 0x77, 0x65, 0xb1, 0x2c, 0xab, 0xe9, 0x04, 0xa9, 0x0c, 0xd9, 0x71, 0xf1, 0xc7, 0x3f, + 0xe4, 0xd6, 0x4a, 0xfa, 0xcf, 0x4f, 0x73, 0xcc, 0x93, 0xa7, 0x39, 0xe6, 0x8f, 0xa7, 0x39, 0xe6, + 0x9b, 0x67, 0xb9, 0xb5, 0x27, 0xcf, 0x72, 0x6b, 0xbf, 0x3d, 0xcb, 0xad, 0x3d, 0x94, 0x9b, 0x96, + 0xd7, 0xea, 0xd7, 0x0b, 0xa6, 0xd3, 0x29, 0x9a, 0x8e, 0xdb, 0x71, 0x5c, 0xfa, 0x73, 0xd3, 0x6d, + 0x1c, 0x17, 0x1f, 0x15, 0x17, 0x1f, 0x59, 0x37, 0xe7, 0x5f, 0x59, 0xaf, 0xdd, 0xbe, 0x19, 0xfe, + 0xd0, 0xc2, 0xb7, 0x8c, 0x5b, 0x5f, 0xf7, 0xc7, 0xd9, 0xeb, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, + 0x20, 0x84, 0x51, 0xf7, 0x95, 0x0d, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -1148,6 +1149,11 @@ func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } if len(m.Data) > 0 { i -= len(m.Data) copy(dAtA[i:], m.Data) @@ -1727,6 +1733,9 @@ func (m *SignatureAndData) Size() (n int) { if l > 0 { n += 1 + l + sovSolomachine(uint64(l)) } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } return n } @@ -2698,6 +2707,25 @@ func (m *SignatureAndData) Unmarshal(dAtA []byte) error { m.Data = []byte{} } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSolomachine(dAtA[iNdEx:]) diff --git a/x/ibc/light-clients/06-solomachine/types/update.go b/x/ibc/light-clients/06-solomachine/types/update.go index a7c288cfc83a..4deb8833d5c4 100644 --- a/x/ibc/light-clients/06-solomachine/types/update.go +++ b/x/ibc/light-clients/06-solomachine/types/update.go @@ -10,7 +10,7 @@ import ( // CheckHeaderAndUpdateState checks if the provided header is valid and updates // the consensus state if appropriate. It returns an error if: -// - the client or header provided are not parseable to solo machine types +// - the header provided is not parseable to a solo machine header // - the header sequence does not match the current sequence // - the header timestamp is less than the consensus state timestamp // - the currently registered public key did not provide the update signature diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go index 5ca74030eea6..21336118603a 100644 --- a/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -33,7 +33,7 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { _, suiteVal := suite.valSet.GetByIndex(0) // Create signer array and ensure it is in same order as bothValSet - bothSigners := types.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) altSigners := []tmtypes.PrivValidator{altPrivVal} @@ -60,8 +60,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -76,8 +76,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), heightMinus1, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -92,8 +92,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -108,8 +108,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainIDVersion0, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainIDVersion0, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDVersion0, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDVersion0, int64(height.VersionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainIDVersion0, ClientId: chainID, }, @@ -124,8 +124,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainIDVersion0, clienttypes.NewHeight(1, 3), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainIDVersion0, clienttypes.NewHeight(1, 3), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDVersion0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDVersion0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainIDVersion0, ClientId: chainID, }, @@ -140,8 +140,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainIDVersion1, clienttypes.NewHeight(1, 1), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainIDVersion1, clienttypes.NewHeight(1, 1), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainIDVersion1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDVersion1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainIDVersion1, ClientId: chainID, }, @@ -156,8 +156,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -172,8 +172,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader("ethermint", height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader("ethermint", height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: "ethermint", ClientId: chainID, }, @@ -188,8 +188,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -204,8 +204,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), heightMinus3, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -220,8 +220,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -236,8 +236,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -263,8 +263,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -279,8 +279,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -295,8 +295,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -311,8 +311,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -327,8 +327,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, suite.valSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -343,8 +343,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), ChainId: chainID, ClientId: chainID, }, @@ -359,8 +359,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, bothValSet, bothValSet, bothSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), ChainId: chainID, ClientId: chainID, }, @@ -375,8 +375,8 @@ func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), height, &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, height, suite.now, altValSet, bothValSet, altSigners), - Header2: types.CreateTestHeader(chainID, height, height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), ChainId: chainID, ClientId: chainID, }, diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go index de2bd3842205..22bd8674fb8e 100644 --- a/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go @@ -10,6 +10,7 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" ) @@ -19,7 +20,7 @@ func (suite *TendermintTestSuite) TestMisbehaviour() { misbehaviour := &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: clientID, } @@ -47,7 +48,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { // Create signer array and ensure it is in same order as bothValSet _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := types.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) altSigners := []tmtypes.PrivValidator{altPrivVal} @@ -63,7 +64,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "valid misbehaviour", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: clientID, }, @@ -86,7 +87,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "valid misbehaviour with different trusted headers", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, clienttypes.NewHeight(0, height.VersionHeight-3), suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), clienttypes.NewHeight(0, height.VersionHeight-3), suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), ChainId: chainID, ClientId: clientID, }, @@ -96,7 +97,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "trusted height is 0 in Header1", &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), Header2: suite.header, ChainId: chainID, ClientId: clientID, @@ -108,7 +109,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "trusted height is 0 in Header2", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: clientID, }, @@ -118,7 +119,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "trusted valset is nil in Header1", &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), Header2: suite.header, ChainId: chainID, ClientId: clientID, @@ -130,7 +131,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "trusted valset is nil in Header2", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), ChainId: chainID, ClientId: clientID, }, @@ -141,7 +142,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "invalid client ID ", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: "GAIA", }, @@ -152,7 +153,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "wrong chainID on header1", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader("ethermint", height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.VersionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), ChainId: "ethermint", ClientId: clientID, }, @@ -163,7 +164,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "wrong chainID on header2", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader("ethermint", height, heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.VersionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: clientID, }, @@ -174,7 +175,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "wrong chainID in misbehaviour", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), ChainId: "ethermint", ClientId: clientID, }, @@ -185,7 +186,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "mismatched heights", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, clienttypes.NewHeight(0, 6), clienttypes.NewHeight(0, 4), suite.now, suite.valSet, suite.valSet, signers), + Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, 4), suite.now, suite.valSet, suite.valSet, signers), ChainId: chainID, ClientId: clientID, }, @@ -206,7 +207,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { { "header 1 doesn't have 2/3 majority", &types.Misbehaviour{ - Header1: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), Header2: suite.header, ChainId: chainID, ClientId: clientID, @@ -229,7 +230,7 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "header 2 doesn't have 2/3 majority", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: clientID, }, @@ -251,12 +252,12 @@ func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { "validators sign off on wrong commit", &types.Misbehaviour{ Header1: suite.header, - Header2: types.CreateTestHeader(chainID, height, heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), ChainId: chainID, ClientId: clientID, }, func(misbehaviour *types.Misbehaviour) error { - tmBlockID := types.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) + tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto() return nil }, diff --git a/x/ibc/light-clients/07-tendermint/types/tendermint_test.go b/x/ibc/light-clients/07-tendermint/types/tendermint_test.go index 4c1f4d2ecc36..b175bfd5a588 100644 --- a/x/ibc/light-clients/07-tendermint/types/tendermint_test.go +++ b/x/ibc/light-clients/07-tendermint/types/tendermint_test.go @@ -85,7 +85,7 @@ func (suite *TendermintTestSuite) SetupTest() { val := tmtypes.NewValidator(pubKey.(cryptotypes.IntoTmPubKey).AsTmPubKey(), 10) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) suite.valsHash = suite.valSet.Hash() - suite.header = ibctmtypes.CreateTestHeader(chainID, height, heightMinus1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.header = suite.chainA.CreateTMClientHeader(chainID, int64(height.VersionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, Time: suite.now}) } diff --git a/x/ibc/light-clients/07-tendermint/types/test_utils.go b/x/ibc/light-clients/07-tendermint/types/test_utils.go deleted file mode 100644 index 5e1984905315..000000000000 --- a/x/ibc/light-clients/07-tendermint/types/test_utils.go +++ /dev/null @@ -1,105 +0,0 @@ -package types - -import ( - "bytes" - "time" - - "github.com/tendermint/tendermint/crypto/tmhash" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmprotoversion "github.com/tendermint/tendermint/proto/tendermint/version" - tmtypes "github.com/tendermint/tendermint/types" - tmversion "github.com/tendermint/tendermint/version" - - clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" -) - -// MakeBlockID is a copied unimported test function from tmtypes to use here -func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.BlockID { - return tmtypes.BlockID{ - Hash: hash, - PartSetHeader: tmtypes.PartSetHeader{ - Total: partSetSize, - Hash: partSetHash, - }, - } -} - -// CreateTestHeader creates a mock header for testing only. -func CreateTestHeader(chainID string, height, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *Header { - var ( - valSet *tmproto.ValidatorSet - trustedVals *tmproto.ValidatorSet - ) - vsetHash := tmValSet.Hash() - blockHeight := int64(height.VersionHeight) - tmHeader := tmtypes.Header{ - Version: tmprotoversion.Consensus{Block: tmversion.BlockProtocol, App: 2}, - ChainID: chainID, - Height: blockHeight, - Time: timestamp, - LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), - LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), - DataHash: tmhash.Sum([]byte("data_hash")), - ValidatorsHash: vsetHash, - NextValidatorsHash: vsetHash, - ConsensusHash: tmhash.Sum([]byte("consensus_hash")), - AppHash: tmhash.Sum([]byte("app_hash")), - LastResultsHash: tmhash.Sum([]byte("last_results_hash")), - EvidenceHash: tmhash.Sum([]byte("evidence_hash")), - ProposerAddress: tmValSet.Proposer.Address, - } - - hhash := tmHeader.Hash() - blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) - voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet) - commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp) - if err != nil { - panic(err) - } - - signedHeader := tmproto.SignedHeader{ - Header: tmHeader.ToProto(), - Commit: commit.ToProto(), - } - - if tmValSet != nil { - valSet, err = tmValSet.ToProto() - if err != nil { - panic(err) - } - } - - if tmTrustedVals != nil { - trustedVals, err = tmTrustedVals.ToProto() - if err != nil { - panic(err) - } - } - - return &Header{ - SignedHeader: &signedHeader, - ValidatorSet: valSet, - TrustedHeight: trustedHeight, - TrustedValidators: trustedVals, - } -} - -// CreateSortedSignerArray takes two PrivValidators, and the corresponding Validator structs -// (including voting power). It returns a signer array of PrivValidators that matches the -// sorting of ValidatorSet. -// The sorting is first by .VotingPower (descending), with secondary index of .Address (ascending). -func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, - altVal, suiteVal *tmtypes.Validator) []tmtypes.PrivValidator { - - switch { - case altVal.VotingPower > suiteVal.VotingPower: - return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} - case altVal.VotingPower < suiteVal.VotingPower: - return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} - default: - if bytes.Compare(altVal.Address, suiteVal.Address) == -1 { - return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} - } - return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} - } -} diff --git a/x/ibc/light-clients/07-tendermint/types/update_test.go b/x/ibc/light-clients/07-tendermint/types/update_test.go index 358548e85464..2d075ad0a5cf 100644 --- a/x/ibc/light-clients/07-tendermint/types/update_test.go +++ b/x/ibc/light-clients/07-tendermint/types/update_test.go @@ -46,7 +46,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { // Create signer array and ensure it is in same order as bothValSet _, suiteVal := suite.valSet.GetByIndex(0) - bothSigners := types.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) altSigners := []tmtypes.PrivValidator{altPrivVal} @@ -60,7 +60,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: true, @@ -70,7 +70,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.VersionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -80,7 +80,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, bothValSet, bothValSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -91,7 +91,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) consStateHeight = heightMinus3 - newHeader = types.CreateTestHeader(chainID, heightMinus1, heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.VersionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -101,7 +101,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainIDVersion1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainIDVersion0, height, heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainIDVersion0, int64(height.VersionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: true, @@ -111,7 +111,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader("ethermint", heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.VersionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -121,7 +121,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainIDVersion0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainIDVersion1, clienttypes.NewHeight(1, 1), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainIDVersion1, 1, height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -131,7 +131,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainIDVersion1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainIDVersion1, clienttypes.NewHeight(1, 3), height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainIDVersion1, 3, height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -141,7 +141,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) currentTime = suite.now }, expPass: false, @@ -151,7 +151,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, bothValSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, suite.valSet, bothValSet, signers) currentTime = suite.now }, expPass: false, @@ -161,7 +161,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, altValSet, suite.valSet, altSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.VersionHeight), height, suite.headerTime, altValSet, suite.valSet, altSigners) currentTime = suite.now }, expPass: false, @@ -171,7 +171,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus5, height, suite.headerTime, bothValSet, bothValSet, bothSigners) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.VersionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) currentTime = suite.now }, expPass: false, @@ -181,7 +181,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) // make current time pass trusting period from last timestamp on clientstate currentTime = suite.now.Add(trustingPeriod) }, @@ -192,7 +192,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -202,7 +202,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.clientTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.clientTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, @@ -212,7 +212,7 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { setup: func() { clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) - newHeader = types.CreateTestHeader(chainID, heightPlus1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.VersionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) // cause new header to fail validatebasic by changing commit height to mismatch header height newHeader.SignedHeader.Commit.Height = versionHeight - 1 currentTime = suite.now @@ -222,10 +222,10 @@ func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { { name: "header height < consensus height", setup: func() { - clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, heightPlus5, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(height.VersionNumber, heightPlus5.VersionHeight), ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) // Make new header at height less than latest client state - newHeader = types.CreateTestHeader(chainID, heightMinus1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.VersionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) currentTime = suite.now }, expPass: false, diff --git a/x/ibc/light-clients/07-tendermint/types/upgrade_test.go b/x/ibc/light-clients/07-tendermint/types/upgrade_test.go index bad73aaba431..f5debb01a6ef 100644 --- a/x/ibc/light-clients/07-tendermint/types/upgrade_test.go +++ b/x/ibc/light-clients/07-tendermint/types/upgrade_test.go @@ -104,7 +104,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { expPass: false, }, { - name: "unsuccessful upgrade: chain-specified paramaters do not match committed client", + name: "unsuccessful upgrade: chain-specified parameters do not match committed client", setup: func() { upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, ibctesting.DefaultConsensusParams, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) diff --git a/x/ibc/light-clients/09-localhost/types/localhost.pb.go b/x/ibc/light-clients/09-localhost/types/localhost.pb.go index a01f162419a0..28f7f74d3879 100644 --- a/x/ibc/light-clients/09-localhost/types/localhost.pb.go +++ b/x/ibc/light-clients/09-localhost/types/localhost.pb.go @@ -27,12 +27,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // ClientState defines a loopback (localhost) client. It requires (read-only) // access to keys outside the client prefix. type ClientState struct { - // client id - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // self chain ID - ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` // self latest block height - Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` + Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` } func (m *ClientState) Reset() { *m = ClientState{} } @@ -77,26 +75,25 @@ func init() { } var fileDescriptor_acd9f5b22d41bf6d = []byte{ - // 294 bytes of a gzipped FileDescriptorProto + // 279 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xcd, 0x4c, 0x4a, 0xd6, 0xcf, 0xc9, 0x4c, 0xcf, 0x28, 0x49, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x29, 0xd6, 0xcf, 0xc9, 0x4f, 0x4e, 0xcc, 0xc9, 0xc8, 0x2f, 0x2e, 0xd1, 0x2f, 0x33, 0x44, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x64, 0x33, 0x93, 0x92, 0xf5, 0x90, 0x95, 0xeb, 0x21, 0x54, 0x94, 0x19, 0x4a, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x55, 0xea, 0x83, 0x58, 0x10, 0x4d, 0x52, 0xf2, 0x20, 0x3b, 0x92, - 0xf3, 0x8b, 0x52, 0xf5, 0x21, 0x9a, 0x40, 0x06, 0x43, 0x58, 0x10, 0x05, 0x4a, 0xbd, 0x8c, 0x5c, - 0xdc, 0xce, 0x60, 0x81, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x3e, 0x2e, 0xa6, 0xcc, 0x14, 0x09, - 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0xa6, 0xcc, 0x14, 0x21, 0x3d, 0x2e, 0x8e, 0xe4, 0x8c, 0xc4, - 0xcc, 0xbc, 0xf8, 0xcc, 0x14, 0x09, 0x26, 0x90, 0xa8, 0x93, 0xf0, 0xa7, 0x7b, 0xf2, 0xfc, 0x95, - 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x30, 0x19, 0xa5, 0x20, 0x76, 0x30, 0xd3, 0x33, 0x45, 0xc8, 0x82, - 0x8b, 0x2d, 0x23, 0x15, 0xe4, 0x48, 0x09, 0x66, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x29, 0x3d, 0x90, - 0xb3, 0x41, 0x2e, 0xd0, 0x83, 0xda, 0x5b, 0x66, 0xa8, 0xe7, 0x01, 0x56, 0xe1, 0xc4, 0x72, 0xe2, - 0x9e, 0x3c, 0x43, 0x10, 0x54, 0xbd, 0x15, 0x4b, 0xc7, 0x02, 0x79, 0x06, 0xa7, 0xd8, 0x13, 0x8f, - 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, - 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x72, 0x4e, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, - 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0x86, 0x52, 0xba, 0xc5, 0x29, 0xd9, - 0xfa, 0x15, 0xfa, 0xf0, 0xd0, 0xd4, 0x85, 0x05, 0xa7, 0x81, 0xa5, 0x2e, 0x22, 0x44, 0x4b, 0x2a, - 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0xbe, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xec, - 0x4b, 0x6c, 0x7c, 0x01, 0x00, 0x00, + 0xf3, 0x8b, 0x52, 0xf5, 0x21, 0x9a, 0x40, 0x06, 0x43, 0x58, 0x10, 0x05, 0x4a, 0xb5, 0x5c, 0xdc, + 0xce, 0x60, 0x7e, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x1e, 0x17, 0x47, 0x72, 0x46, 0x62, 0x66, + 0x5e, 0x7c, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x93, 0xf0, 0xa7, 0x7b, 0xf2, 0xfc, + 0x95, 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x30, 0x19, 0xa5, 0x20, 0x76, 0x30, 0xd3, 0x33, 0x45, 0xc8, + 0x82, 0x8b, 0x2d, 0x23, 0x15, 0xe4, 0x26, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x29, 0x3d, + 0x90, 0x2b, 0x41, 0x16, 0xea, 0x41, 0xad, 0x29, 0x33, 0xd4, 0xf3, 0x00, 0xab, 0x70, 0x62, 0x39, + 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xde, 0x8a, 0xa5, 0x63, 0x81, 0x3c, 0x83, 0x53, 0xec, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, + 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x39, 0xa7, 0x67, 0x96, 0x64, 0x94, 0x26, + 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0x43, 0x29, 0xdd, 0xe2, 0x94, + 0x6c, 0xfd, 0x0a, 0x7d, 0x78, 0xe0, 0xe9, 0xc2, 0x42, 0xcf, 0xc0, 0x52, 0x17, 0x11, 0x80, 0x25, + 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x4f, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xcd, + 0x7d, 0x91, 0x77, 0x6b, 0x01, 0x00, 0x00, } func (m *ClientState) Marshal() (dAtA []byte, err error) { @@ -128,19 +125,12 @@ func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintLocalhost(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x1a + dAtA[i] = 0x12 if len(m.ChainId) > 0 { i -= len(m.ChainId) copy(dAtA[i:], m.ChainId) i = encodeVarintLocalhost(dAtA, i, uint64(len(m.ChainId))) i-- - dAtA[i] = 0x12 - } - if len(m.Id) > 0 { - i -= len(m.Id) - copy(dAtA[i:], m.Id) - i = encodeVarintLocalhost(dAtA, i, uint64(len(m.Id))) - i-- dAtA[i] = 0xa } return len(dAtA) - i, nil @@ -163,10 +153,6 @@ func (m *ClientState) Size() (n int) { } var l int _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovLocalhost(uint64(l)) - } l = len(m.ChainId) if l > 0 { n += 1 + l + sovLocalhost(uint64(l)) @@ -212,38 +198,6 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalhost - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthLocalhost - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthLocalhost - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) } @@ -275,7 +229,7 @@ func (m *ClientState) Unmarshal(dAtA []byte) error { } m.ChainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) } diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go index 107300fc7d65..9331ef405c29 100644 --- a/x/ibc/testing/chain.go +++ b/x/ibc/testing/chain.go @@ -1,6 +1,7 @@ package ibctesting import ( + "bytes" "fmt" "strconv" "testing" @@ -266,7 +267,8 @@ func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, clien // CONTRACT: this function must only be called after app.Commit() occurs func (chain *TestChain) NextBlock() { // set the last header to the current header - chain.LastHeader = chain.CreateTMClientHeader() + // use nil trusted fields + chain.LastHeader = chain.CurrentTMClientHeader() // increment the current header chain.CurrentHeader = tmproto.Header{ @@ -518,14 +520,28 @@ func (chain *TestChain) ExpireClient(amount time.Duration) { chain.CurrentHeader.Time = chain.CurrentHeader.Time.Add(amount) } -// CreateTMClientHeader creates a TM header to update the TM client. -func (chain *TestChain) CreateTMClientHeader() *ibctmtypes.Header { - vsetHash := chain.Vals.Hash() +// CurrentTMClientHeader creates a TM header using the current header parameters +// on the chain. The trusted fields in the header are set to nil. +func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header { + return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, nil, chain.Signers) +} + +// CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow +// caller flexibility to use params that differ from the chain. +func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *ibctmtypes.Header { + var ( + valSet *tmproto.ValidatorSet + trustedVals *tmproto.ValidatorSet + ) + require.NotNil(chain.t, tmValSet) + + vsetHash := tmValSet.Hash() + tmHeader := tmtypes.Header{ Version: tmprotoversion.Consensus{Block: tmversion.BlockProtocol, App: 2}, - ChainID: chain.ChainID, - Height: chain.CurrentHeader.Height, - Time: chain.CurrentHeader.Time, + ChainID: chainID, + Height: blockHeight, + Time: timestamp, LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), LastCommitHash: chain.App.LastCommitID().Hash, DataHash: tmhash.Sum([]byte("data_hash")), @@ -535,15 +551,13 @@ func (chain *TestChain) CreateTMClientHeader() *ibctmtypes.Header { AppHash: chain.CurrentHeader.AppHash, LastResultsHash: tmhash.Sum([]byte("last_results_hash")), EvidenceHash: tmhash.Sum([]byte("evidence_hash")), - ProposerAddress: chain.Vals.Proposer.Address, + ProposerAddress: tmValSet.Proposer.Address, //nolint:staticcheck } hhash := tmHeader.Hash() - blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) + voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet) - voteSet := tmtypes.NewVoteSet(chain.ChainID, chain.CurrentHeader.Height, 1, tmproto.PrecommitType, chain.Vals) - - commit, err := tmtypes.MakeCommit(blockID, chain.CurrentHeader.Height, 1, voteSet, chain.Signers, chain.CurrentHeader.Time) + commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp) require.NoError(chain.t, err) signedHeader := &tmproto.SignedHeader{ @@ -551,16 +565,27 @@ func (chain *TestChain) CreateTMClientHeader() *ibctmtypes.Header { Commit: commit.ToProto(), } - valSet, err := chain.Vals.ToProto() - if err != nil { - panic(err) + if tmValSet != nil { + valSet, err = tmValSet.ToProto() + if err != nil { + panic(err) + } } - // Do not set trusted field here, these fields can be inserted before relaying messages to a client. + if tmTrustedVals != nil { + trustedVals, err = tmTrustedVals.ToProto() + if err != nil { + panic(err) + } + } + + // The trusted fields may be nil. They may be filled before relaying messages to a client. // The relayer is responsible for querying client and injecting appropriate trusted fields. return &ibctmtypes.Header{ - SignedHeader: signedHeader, - ValidatorSet: valSet, + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: trustedHeight, + TrustedValidators: trustedVals, } } @@ -575,6 +600,26 @@ func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.Bl } } +// CreateSortedSignerArray takes two PrivValidators, and the corresponding Validator structs +// (including voting power). It returns a signer array of PrivValidators that matches the +// sorting of ValidatorSet. +// The sorting is first by .VotingPower (descending), with secondary index of .Address (ascending). +func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, + altVal, suiteVal *tmtypes.Validator) []tmtypes.PrivValidator { + + switch { + case altVal.VotingPower > suiteVal.VotingPower: + return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} + case altVal.VotingPower < suiteVal.VotingPower: + return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} + default: + if bytes.Compare(altVal.Address, suiteVal.Address) == -1 { + return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} + } + return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} + } +} + // ConnectionOpenInit will construct and execute a MsgConnectionOpenInit. func (chain *TestChain) ConnectionOpenInit( counterparty *TestChain, diff --git a/x/ibc/testing/chain_test.go b/x/ibc/testing/chain_test.go new file mode 100644 index 000000000000..243eb064479e --- /dev/null +++ b/x/ibc/testing/chain_test.go @@ -0,0 +1,47 @@ +package ibctesting_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmtypes "github.com/tendermint/tendermint/types" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +func TestCreateSortedSignerArray(t *testing.T) { + privVal1 := mock.NewPV() + pubKey1, err := privVal1.GetPubKey() + require.NoError(t, err) + + privVal2 := mock.NewPV() + pubKey2, err := privVal2.GetPubKey() + require.NoError(t, err) + + validator1 := tmtypes.NewValidator(pubKey1.(cryptotypes.IntoTmPubKey).AsTmPubKey(), 1) + validator2 := tmtypes.NewValidator(pubKey2.(cryptotypes.IntoTmPubKey).AsTmPubKey(), 2) + + expected := []tmtypes.PrivValidator{privVal2, privVal1} + + actual := ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) + require.Equal(t, expected, actual) + + // swap order + actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) + require.Equal(t, expected, actual) + + // smaller address + validator1.Address = []byte{1} + validator2.VotingPower = 1 + + expected = []tmtypes.PrivValidator{privVal1, privVal2} + + actual = ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) + require.Equal(t, expected, actual) + + // swap order + actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) + require.Equal(t, expected, actual) +} diff --git a/x/ibc/testing/solomachine.go b/x/ibc/testing/solomachine.go index c0f9befaa0a6..21b02516e9e2 100644 --- a/x/ibc/testing/solomachine.go +++ b/x/ibc/testing/solomachine.go @@ -181,8 +181,12 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { Signature: sig, DataType: solomachinetypes.CLIENT, Data: dataOne, + Timestamp: solo.Time, } + // misbehaviour signaturess can have different timestamps + solo.Time++ + signBytes = &solomachinetypes.SignBytes{ Sequence: solo.Sequence, Timestamp: solo.Time, @@ -199,6 +203,7 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { Signature: sig, DataType: solomachinetypes.CONSENSUS, Data: dataTwo, + Timestamp: solo.Time, } return &solomachinetypes.Misbehaviour{ diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index 55009706efa5..f74b0db96525 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -41,7 +41,7 @@ func GetCmdQuerySigningInfo() *cobra.Command { Short: "Query a validator's signing information", Long: strings.TrimSpace(`Use a validators' consensus public key to find the signing-info for that validator: -$ query slashing signing-info cosmosvalconspub1zcjduepqfhvwcmt7p06fvdgexxhmz0l8c7sgswl7ulv7aulk364x4g5xsw7sr0k2g5 +$ query slashing signing-info cosmosvalconspub1zcjduepqfhvwcmt7p06fvdgexxhmz0l8c7sgswl7ulv7aulk364x4g5xsw7sr0k2g5 `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -81,7 +81,7 @@ func GetCmdQuerySigningInfos() *cobra.Command { Short: "Query signing information of all validators", Long: strings.TrimSpace(`signing infos of validators: -$ query slashing signing-infos +$ query slashing signing-infos `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -122,7 +122,7 @@ func GetCmdQueryParams() *cobra.Command { Args: cobra.NoArgs, Long: strings.TrimSpace(`Query genesis parameters for the slashing module: -$ query slashing params +$ query slashing params `), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 842c5a508a47..298a44e14025 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -31,7 +31,7 @@ func NewUnjailTxCmd() *cobra.Command { Short: "unjail validator previously jailed for downtime", Long: `unjail a jailed validator: -$ tx slashing unjail --from mykey +$ tx slashing unjail --from mykey `, RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index 447cb86aad08..e30dd900ed10 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -101,7 +101,7 @@ func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, res, err := app.Deliver(tx) + _, res, err := app.Deliver(txGen.TxEncoder(), tx) // result should fail if: // - validator cannot be unjailed due to tombstone diff --git a/x/slashing/spec/01_concepts.md b/x/slashing/spec/01_concepts.md index b38a6edbed8c..9505706f90d3 100644 --- a/x/slashing/spec/01_concepts.md +++ b/x/slashing/spec/01_concepts.md @@ -8,8 +8,8 @@ order: 1 At any given time, there are any number of validators registered in the state machine. Each block, the top `MaxValidators` (defined by `x/staking`) validators -who are not jailed become *bonded*, meaning that they may propose and vote on -blocks. Validators who are *bonded* are *at stake*, meaning that part or all of +who are not jailed become _bonded_, meaning that they may propose and vote on +blocks. Validators who are _bonded_ are _at stake_, meaning that part or all of their stake and their delegators' stake is at risk if they commit a protocol fault. For each of these validators we keep a `ValidatorSigningInfo` record that contains @@ -20,26 +20,26 @@ attributes. In order to mitigate the impact of initially likely categories of non-malicious protocol faults, the Cosmos Hub implements for each validator -a *tombstone* cap, which only allows a validator to be slashed once for a double +a _tombstone_ cap, which only allows a validator to be slashed once for a double sign fault. For example, if you misconfigure your HSM and double-sign a bunch of old blocks, you'll only be punished for the first double-sign (and then immediately tombstombed). This will still be quite expensive and desirable to avoid, but tombstone caps somewhat blunt the economic impact of unintentional misconfiguration. -Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between. +Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between. ## Infraction Timelines To illustrate how the `x/slashing` module handles submitted evidence through Tendermint consensus, consider the following examples: -__Definitions__: +**Definitions**: -*[* : timeline start -*]* : timeline end -*Cn* : infraction `n` committed -*Dn* : infraction `n` discovered -*Vb* : validator bonded -*Vu* : validator unbonded +_[_ : timeline start +_]_ : timeline end +_Cn_ : infraction `n` committed +_Dn_ : infraction `n` discovered +_Vb_ : validator bonded +_Vu_ : validator unbonded ### Single Double Sign Infraction diff --git a/x/slashing/spec/02_state.md b/x/slashing/spec/02_state.md index 867fa7998374..17931ce866a0 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -40,27 +40,35 @@ bonded validator. The `SignedBlocksWindow` parameter defines the size The information stored for tracking validator liveness is as follows: -```go -type ValidatorSigningInfo struct { - Address sdk.ConsAddress - StartHeight int64 - IndexOffset int64 - JailedUntil time.Time - Tombstoned bool - MissedBlocksCounter int64 +```protobuf +// ValidatorSigningInfo defines a validator's signing info for monitoring their +// liveness activity. +message ValidatorSigningInfo { + string address = 1; + // height at which validator was first a candidate OR was unjailed + int64 start_height = 2; + // index offset into signed block bit array + int64 index_offset = 3; + // timestamp validator cannot be unjailed until + google.protobuf.Timestamp jailed_until = 4; + // whether or not a validator has been tombstoned (killed out of validator + // set) + bool tombstoned = 5; + // missed blocks counter (to avoid scanning the array every time) + int64 missed_blocks_counter = 6; } ``` Where: -- __Address__: The validator's consensus address. -- __StartHeight__: The height that the candidate became an active validator +- **Address**: The validator's consensus address. +- **StartHeight**: The height that the candidate became an active validator (with non-zero voting power). -- __IndexOffset__: Index which is incremented each time the validator was a bonded +- **IndexOffset**: Index which is incremented each time the validator was a bonded in a block and may have signed a precommit or not. This in conjunction with the `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`. -- __JailedUntil__: Time for which the validator is jailed until due to liveness downtime. -- __Tombstoned__: Desribes if the validator is tombstoned or not. It is set once the +- **JailedUntil**: Time for which the validator is jailed until due to liveness downtime. +- **Tombstoned**: Desribes if the validator is tombstoned or not. It is set once the validator commits an equivocation or for any other configured misbehiavor. -- __MissedBlocksCounter__: A counter kept to avoid unnecessary array reads. Note +- **MissedBlocksCounter**: A counter kept to avoid unnecessary array reads. Note that `Sum(MissedBlocksBitArray)` equals `MissedBlocksCounter` always. diff --git a/x/slashing/spec/03_messages.md b/x/slashing/spec/03_messages.md index d7825bfd0b8f..6e7d168ffdb5 100644 --- a/x/slashing/spec/03_messages.md +++ b/x/slashing/spec/03_messages.md @@ -9,15 +9,21 @@ In this section we describe the processing of messages for the `slashing` module ## Unjail If a validator was automatically unbonded due to downtime and wishes to come back online & -possibly rejoin the bonded set, it must send `TxUnjail`: - -``` -type TxUnjail struct { - ValidatorAddr sdk.AccAddress +possibly rejoin the bonded set, it must send `MsgUnjail`: + +```protobuf +// MsgUnjail is an sdk.Msg used for unjailing a jailed validator, thus returning +// them into the bonded validator set, so they can begin receiving provisions +// and rewards again. +message MsgUnjail { + string validator_addr = 1; } +``` -handleMsgUnjail(tx TxUnjail) +And below is its corresponding handler: +``` +handleMsgUnjail(tx MsgUnjail) validator = getValidator(tx.ValidatorAddr) if validator == nil fail with "No validator found" diff --git a/x/slashing/spec/04_begin_block.md b/x/slashing/spec/04_begin_block.md index 96d1217f1945..99572c419f20 100644 --- a/x/slashing/spec/04_begin_block.md +++ b/x/slashing/spec/04_begin_block.md @@ -23,7 +23,7 @@ greater than `minHeight` and the validator's `MissedBlocksCounter` is greater th for `DowntimeJailDuration`, and have the following values reset: `MissedBlocksBitArray`, `MissedBlocksCounter`, and `IndexOffset`. -__Note__: Liveness slashes do **NOT** lead to a tombstombing. +**Note**: Liveness slashes do **NOT** lead to a tombstombing. ```go height := block.Height diff --git a/x/slashing/spec/05_hooks.md b/x/slashing/spec/05_hooks.md index adb8da39b01f..8f78cdff01b0 100644 --- a/x/slashing/spec/05_hooks.md +++ b/x/slashing/spec/05_hooks.md @@ -25,6 +25,6 @@ onValidatorBonded(address sdk.ValAddress) } setValidatorSigningInfo(signingInfo) } - + return ``` diff --git a/x/slashing/spec/07_tombstone.md b/x/slashing/spec/07_tombstone.md index a062278cec9d..4759a89b7190 100644 --- a/x/slashing/spec/07_tombstone.md +++ b/x/slashing/spec/07_tombstone.md @@ -15,18 +15,18 @@ and evidence of the infraction reaching the state machine (this is one of the primary reasons for the existence of the unbonding period). > Note: The tombstone concept, only applies to faults that have a delay between -the infraction occurring and evidence reaching the state machine. For example, -evidence of a validator double signing may take a while to reach the state machine -due to unpredictable evidence gossip layer delays and the ability of validators to -selectively reveal double-signatures (e.g. to infrequently-online light clients). -Liveness slashing, on the other hand, is detected immediately as soon as the -infraction occurs, and therefore no slashing period is needed. A validator is -immediately put into jail period, and they cannot commit another liveness fault -until they unjail. In the future, there may be other types of byzantine faults -that have delays (for example, submitting evidence of an invalid proposal as a transaction). -When implemented, it will have to be decided whether these future types of -byzantine faults will result in a tombstoning (and if not, the slash amounts -will not be capped by a slashing period). +> the infraction occurring and evidence reaching the state machine. For example, +> evidence of a validator double signing may take a while to reach the state machine +> due to unpredictable evidence gossip layer delays and the ability of validators to +> selectively reveal double-signatures (e.g. to infrequently-online light clients). +> Liveness slashing, on the other hand, is detected immediately as soon as the +> infraction occurs, and therefore no slashing period is needed. A validator is +> immediately put into jail period, and they cannot commit another liveness fault +> until they unjail. In the future, there may be other types of byzantine faults +> that have delays (for example, submitting evidence of an invalid proposal as a transaction). +> When implemented, it will have to be decided whether these future types of +> byzantine faults will result in a tombstoning (and if not, the slash amounts +> will not be capped by a slashing period). In the current system design, once a validator is put in the jail for a consensus fault, after the `JailPeriod` they are allowed to send a transaction to `unjail` @@ -72,10 +72,10 @@ As the number of slashing periods increase, it creates more complexity as we hav to keep track of the highest infraction amount for every single slashing period. > Note: Currently, according to the `slashing` module spec, a new slashing period -is created every time a validator is unbonded then rebonded. This should probably -be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) -for further details. For the remainder of this, I will assume that we only start -a new slashing period when a validator gets unjailed. +> is created every time a validator is unbonded then rebonded. This should probably +> be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) +> for further details. For the remainder of this, I will assume that we only start +> a new slashing period when a validator gets unjailed. The maximum number of slashing periods is the `len(UnbondingPeriod) / len(JailPeriod)`. The current defaults in Gaia for the `UnbondingPeriod` and `JailPeriod` are 3 weeks @@ -85,7 +85,7 @@ we only have to track 1 slashing period (i.e not have to track slashing periods) Currently, in the jail period implementation, once a validator unjails, all of their delegators who are delegated to them (haven't unbonded / redelegated away), -stay with them. Given that consensus safety faults are so egregious +stay with them. Given that consensus safety faults are so egregious (way more so than liveness faults), it is probably prudent to have delegators not "auto-rebond" to the validator. Thus, we propose setting the "jail time" for a validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). @@ -93,7 +93,7 @@ This essentially kicks the validator out of the validator set and does not allow them to re-enter the validator set. All of their delegators (including the operator themselves) have to either unbond or redelegate away. The validator operator can create a new validator if they would like, with a new operator key and consensus key, but they -have to "re-earn" their delegations back. To put the validator in the tombstone +have to "re-earn" their delegations back. To put the validator in the tombstone state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum time supported by Amino. @@ -106,7 +106,7 @@ of the hooks defined in the `slashing` module consumed by the `staking` module Another optimization that can be made is that if we assume that all ABCI faults for Tendermint consensus are slashed at the same level, we don't have to keep -track of "max slash". Once an ABCI fault happens, we don't have to worry about +track of "max slash". Once an ABCI fault happens, we don't have to worry about comparing potential future ones to find the max. Currently the only Tendermint ABCI fault is: @@ -121,5 +121,5 @@ Given that these faults are both attributable byzantine faults, we will likely want to slash them equally, and thus we can enact the above change. > Note: This change may make sense for current Tendermint consensus, but maybe -not for a different consensus algorithm or future versions of Tendermint that -may want to punish at different levels (for example, partial slashing). +> not for a different consensus algorithm or future versions of Tendermint that +> may want to punish at different levels (for example, partial slashing). diff --git a/x/slashing/spec/README.md b/x/slashing/spec/README.md index 7f694d03d36f..226306562333 100644 --- a/x/slashing/spec/README.md +++ b/x/slashing/spec/README.md @@ -5,7 +5,7 @@ parent: title: "slashing" --> -# `slashing` +# `x/slashing` ## Abstract @@ -25,21 +25,21 @@ This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosyste ## Contents 1. **[Concepts](01_concepts.md)** - - [States](01_concepts.md#states) - - [Tombstone Caps](01_concepts.md#tombstone-caps) - - [ASCII timelines](01_concepts.md#ascii-timelines) + - [States](01_concepts.md#states) + - [Tombstone Caps](01_concepts.md#tombstone-caps) + - [ASCII timelines](01_concepts.md#ascii-timelines) 2. **[State](02_state.md)** - - [Signing Info](02_state.md#signing-info) + - [Signing Info](02_state.md#signing-info) 3. **[Messages](03_messages.md)** - - [Unjail](03_messages.md#unjail) + - [Unjail](03_messages.md#unjail) 4. **[Begin-Block](04_begin_block.md)** - - [Evidence handling](04_begin_block.md#evidence-handling) - - [Uptime tracking](04_begin_block.md#uptime-tracking) + - [Evidence handling](04_begin_block.md#evidence-handling) + - [Uptime tracking](04_begin_block.md#uptime-tracking) 5. **[05_hooks.md](05_hooks.md)** - - [Hooks](05_hooks.md#hooks) + - [Hooks](05_hooks.md#hooks) 6. **[Events](06_events.md)** - - [BeginBlocker](06_events.md#beginblocker) - - [Handlers](06_events.md#handlers) + - [BeginBlocker](06_events.md#beginblocker) + - [Handlers](06_events.md#handlers) 7. **[Staking Tombstone](07_tombstone.md)** - - [Abstract](07_tombstone.md#abstract) + - [Abstract](07_tombstone.md#abstract) 8. **[Parameters](08_params.md)** diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index ff54822aa339..183f11202f5d 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -165,7 +165,7 @@ func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -236,7 +236,7 @@ func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k kee return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -309,7 +309,7 @@ func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.K return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -399,7 +399,7 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } @@ -512,7 +512,7 @@ func SimulateMsgBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, k k return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err }