From 60a2be5089ace634878c8ce16a26740d6f9d6adb Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Thu, 11 May 2023 07:54:42 +0200 Subject: [PATCH 1/9] Add wasm e2e test (#380) * Add wasm e2e test --- test/e2e/README.md | 8 +++- test/e2e/bytecode/storage_contract.wasm | Bin 0 -> 161032 bytes test/e2e/configurer/chain/commands.go | 28 +++++++++++ test/e2e/configurer/chain/node.go | 8 ++++ test/e2e/configurer/chain/queries.go | 61 ++++++++++++++++++++++++ test/e2e/containers/containers.go | 7 +++ test/e2e/e2e_test.go | 45 +++++++++++++++++ 7 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 test/e2e/bytecode/storage_contract.wasm diff --git a/test/e2e/README.md b/test/e2e/README.md index 0a09a5c72..fb9f1751d 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -1,7 +1,5 @@ # End-to-end Tests -## Structure - ### `e2e` Package The `e2e` package defines an integration testing suite used for full @@ -9,6 +7,12 @@ end-to-end testing functionality. The package is copy of Osmosis e2e testing approach. +### Wasm contract used for e2e testing + +Wasm contract located in `bytecode/storage_contract.wasm` is compiled from most recent commit `main` branch - https://github.com/babylonchain/storage-contract + +This contract uses feature specific to Babylon, through Babylon bindings library. + ### Common Problems Please note that if the tests are stopped mid-way, the e2e framework might fail to start again due to duplicated containers. Make sure that diff --git a/test/e2e/bytecode/storage_contract.wasm b/test/e2e/bytecode/storage_contract.wasm new file mode 100644 index 0000000000000000000000000000000000000000..229a10d89df69191d0d6d6e97126923fa6296c4b GIT binary patch literal 161032 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 z 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 D43zb~r=?n8ik|zxn}- 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 D!Sq%} literal 0 HcmV?d00001 diff --git a/test/e2e/configurer/chain/commands.go b/test/e2e/configurer/chain/commands.go index 6b1364fcb..945f85a20 100644 --- a/test/e2e/configurer/chain/commands.go +++ b/test/e2e/configurer/chain/commands.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math/rand" + "strings" "time" "github.com/cosmos/cosmos-sdk/types/bech32" @@ -165,3 +166,30 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) { } } } + +func (n *NodeConfig) StoreWasmCode(wasmFile, from string) { + n.LogActionF("storing wasm code from file %s", wasmFile) + cmd := []string{"babylond", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from), "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3"} + n.LogActionF(strings.Join(cmd, " ")) + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully stored") +} + +func (n *NodeConfig) InstantiateWasmContract(codeId, initMsg, from string) { + n.LogActionF("instantiating wasm contract %s with %s", codeId, initMsg) + cmd := []string{"babylond", "tx", "wasm", "instantiate", codeId, initMsg, fmt.Sprintf("--from=%s", from), "--no-admin", "--label=contract", "--gas=auto", "--gas-prices=1ubbn", "--gas-adjustment=1.3"} + n.LogActionF(strings.Join(cmd, " ")) + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully initialized") +} + +func (n *NodeConfig) WasmExecute(contract, execMsg, from string) { + n.LogActionF("executing %s on wasm contract %s from %s", execMsg, contract, from) + cmd := []string{"babylond", "tx", "wasm", "execute", contract, execMsg, fmt.Sprintf("--from=%s", from)} + n.LogActionF(strings.Join(cmd, " ")) + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully executed") +} diff --git a/test/e2e/configurer/chain/node.go b/test/e2e/configurer/chain/node.go index ac7c27f0b..bb51f3a8c 100644 --- a/test/e2e/configurer/chain/node.go +++ b/test/e2e/configurer/chain/node.go @@ -139,6 +139,14 @@ func (n *NodeConfig) WaitUntilBtcHeight(height uint64) { }, fmt.Sprintf("Timed out waiting for btc height %d", height)) } +func (n *NodeConfig) WaitForNextBlock() { + latest := n.LatestBlockNumber() + n.WaitForCondition(func() bool { + newLatest := n.LatestBlockNumber() + return newLatest > latest + }, fmt.Sprintf("Timed out waiting for next block. Current height is: %d", latest)) +} + func (n *NodeConfig) extractOperatorAddressIfValidator() error { if !n.IsValidator { n.t.Logf("node (%s) is not a validator, skipping", n.Name) diff --git a/test/e2e/configurer/chain/queries.go b/test/e2e/configurer/chain/queries.go index c832f5135..176c9da5b 100644 --- a/test/e2e/configurer/chain/queries.go +++ b/test/e2e/configurer/chain/queries.go @@ -19,6 +19,7 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/require" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/babylonchain/babylon/test/e2e/util" blc "github.com/babylonchain/babylon/x/btclightclient/types" ct "github.com/babylonchain/babylon/x/checkpointing/types" @@ -291,3 +292,63 @@ func (n *NodeConfig) QueryLightClientHeightCheckpointReported(ckptHash []byte) ( } return mResponse.BtcLightClientHeight, nil } + +func (n *NodeConfig) QueryLatestWasmCodeID() uint64 { + path := "/cosmwasm/wasm/v1/code" + + bz, err := n.QueryGRPCGateway(path, url.Values{}) + require.NoError(n.t, err) + + var response wasmtypes.QueryCodesResponse + err = util.Cdc.UnmarshalJSON(bz, &response) + require.NoError(n.t, err) + if len(response.CodeInfos) == 0 { + return 0 + } + return response.CodeInfos[len(response.CodeInfos)-1].CodeID +} + +func (n *NodeConfig) QueryContractsFromId(codeId int) ([]string, error) { + path := fmt.Sprintf("/cosmwasm/wasm/v1/code/%d/contracts", codeId) + bz, err := n.QueryGRPCGateway(path, url.Values{}) + + require.NoError(n.t, err) + + var contractsResponse wasmtypes.QueryContractsByCodeResponse + if err := util.Cdc.UnmarshalJSON(bz, &contractsResponse); err != nil { + return nil, err + } + + return contractsResponse.Contracts, nil +} + +func (n *NodeConfig) QueryWasmSmart(contract string, msg string, result any) error { + // base64-encode the msg + encodedMsg := base64.StdEncoding.EncodeToString([]byte(msg)) + path := fmt.Sprintf("/cosmwasm/wasm/v1/contract/%s/smart/%s", contract, encodedMsg) + + bz, err := n.QueryGRPCGateway(path, url.Values{}) + if err != nil { + return err + } + + var response wasmtypes.QuerySmartContractStateResponse + err = util.Cdc.UnmarshalJSON(bz, &response) + if err != nil { + return err + } + + err = json.Unmarshal(response.Data, &result) + if err != nil { + return err + } + return nil +} + +func (n *NodeConfig) QueryWasmSmartObject(contract string, msg string) (resultObject map[string]interface{}, err error) { + err = n.QueryWasmSmart(contract, msg, &resultObject) + if err != nil { + return nil, err + } + return resultObject, nil +} diff --git a/test/e2e/containers/containers.go b/test/e2e/containers/containers.go index 7806d6bda..e681753b5 100644 --- a/test/e2e/containers/containers.go +++ b/test/e2e/containers/containers.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "os" "regexp" "strings" "testing" @@ -201,6 +202,11 @@ func (m *Manager) RunHermesResource(chainAID, osmoARelayerNodeName, osmoAValMnem // RunNodeResource runs a node container. Assings containerName to the container. // Mounts the container on valConfigDir volume on the running host. Returns the container resource and error if any. func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir string) (*dockertest.Resource, error) { + pwd, err := os.Getwd() + if err != nil { + return nil, err + } + runOpts := &dockertest.RunOptions{ Name: containerName, Repository: BabylonContainerName, @@ -215,6 +221,7 @@ func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir st Platform: "linux/x86_64", Mounts: []string{ fmt.Sprintf("%s/:/home/babylon/babylondata", valCondifDir), + fmt.Sprintf("%s/bytecode:/bytecode", pwd), }, } diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index b5e69cf9a..1b9411895 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -4,10 +4,14 @@ package e2e import ( + "crypto/sha256" + "encoding/hex" "fmt" + "strconv" "github.com/babylonchain/babylon/test/e2e/initialization" ct "github.com/babylonchain/babylon/x/checkpointing/types" + "github.com/stretchr/testify/require" ) // Most simple test, just checking that two chains are up and connected through @@ -74,3 +78,44 @@ func (s *IntegrationTestSuite) TestIbcCheckpointing() { _, err = chainB.GetDefaultNode() s.NoError(err) } + +func (s *IntegrationTestSuite) TestWasm() { + contractPath := "/bytecode/storage_contract.wasm" + chainA := s.configurer.GetChainConfig(0) + nonValidatorNode, err := chainA.GetNodeAtIndex(2) + require.NoError(s.T(), err) + nonValidatorNode.StoreWasmCode(contractPath, initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + latestWasmId := int(nonValidatorNode.QueryLatestWasmCodeID()) + nonValidatorNode.InstantiateWasmContract( + strconv.Itoa(latestWasmId), + `{}`, + initialization.ValidatorWalletName, + ) + nonValidatorNode.WaitForNextBlock() + contracts, err := nonValidatorNode.QueryContractsFromId(1) + s.NoError(err) + s.Require().Len(contracts, 1, "Wrong number of contracts for the counter") + contractAddr := contracts[0] + + data := []byte{1, 2, 3, 4, 5} + dataHex := hex.EncodeToString(data) + dataHash := sha256.Sum256(data) + dataHashHex := hex.EncodeToString(dataHash[:]) + + storeMsg := fmt.Sprintf(`{"save_data":{"data":"%s"}}`, dataHex) + nonValidatorNode.WasmExecute(contractAddr, storeMsg, initialization.ValidatorWalletName) + nonValidatorNode.WaitForNextBlock() + queryMsg := fmt.Sprintf(`{"check_data": {"data_hash":"%s"}}`, dataHashHex) + queryResult, err := nonValidatorNode.QueryWasmSmartObject(contractAddr, queryMsg) + require.NoError(s.T(), err) + finalized := queryResult["finalized"].(bool) + latestFinalizedEpoch := int(queryResult["latest_finalized_epoch"].(float64)) + saveEpoch := int(queryResult["save_epoch"].(float64)) + + require.False(s.T(), finalized) + // in previous test we already finalized epoch 3 + require.Equal(s.T(), 3, latestFinalizedEpoch) + // data is not finalized yet, so save epoch should be strictly greater than latest finalized epoch + require.Greater(s.T(), saveEpoch, latestFinalizedEpoch) +} From a80b9501a95cadcc09c2635dbe2c1bce4a0666e3 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Tue, 16 May 2023 09:43:11 +0200 Subject: [PATCH 2/9] Bump wasmd to rc2 (#381) --- app/app.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/app.go b/app/app.go index ddef2f6c4..364a18645 100644 --- a/app/app.go +++ b/app/app.go @@ -635,6 +635,7 @@ func NewBabylonApp( app.StakingKeeper, distrkeeper.NewQuerier(app.DistrKeeper), app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, scopedWasmKeeper, app.TransferKeeper, diff --git a/go.mod b/go.mod index d71a4bd01..c64eb86b1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ go 1.20 module github.com/babylonchain/babylon require ( - github.com/CosmWasm/wasmd v0.40.0-rc.1 + github.com/CosmWasm/wasmd v0.40.0-rc.2 github.com/btcsuite/btcd v0.23.4 github.com/cometbft/cometbft v0.37.1 github.com/cometbft/cometbft-db v0.7.0 diff --git a/go.sum b/go.sum index f9161abe8..515e46f33 100644 --- a/go.sum +++ b/go.sum @@ -209,8 +209,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/CosmWasm/wasmd v0.40.0-rc.1 h1:prIM2vP1jNh0zgs9seua5BgKdayBgp3FiHtwxFcZSGs= -github.com/CosmWasm/wasmd v0.40.0-rc.1/go.mod h1:uacdue6EGn9JA1TqBNHB3iCe4PCIChuFT23AzIl2VME= +github.com/CosmWasm/wasmd v0.40.0-rc.2 h1:UgOr8CaitJ8C8Y80viKLT6mL2Xh4yg2X4szCdTVr6xg= +github.com/CosmWasm/wasmd v0.40.0-rc.2/go.mod h1:l2s42GHKp1CHcR0N6J8P6p02b5RMWFCpcmRjyKMtqqg= github.com/CosmWasm/wasmvm v1.2.3 h1:OKYlobwmVGbl0eSn0mXoAAjE5hIuXnQCLPjbNd91sVY= github.com/CosmWasm/wasmvm v1.2.3/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8J7KFtkc= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= From a2ae974d9f8501221a9ea0f1aa99aa1fbc27acf0 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Tue, 16 May 2023 14:23:34 +0300 Subject: [PATCH 3/9] Bump vulnerable docker/distribution (#382) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c64eb86b1..b7f3020c7 100644 --- a/go.mod +++ b/go.mod @@ -147,7 +147,7 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/docker/cli v20.10.21+incompatible // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v20.10.24+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect diff --git a/go.sum b/go.sum index 515e46f33..1ddfd2685 100644 --- a/go.sum +++ b/go.sum @@ -408,8 +408,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= From 76f53a548815dd1dff51e369076cf09eda5e1563 Mon Sep 17 00:00:00 2001 From: KonradStaniec Date: Wed, 17 May 2023 09:02:08 +0200 Subject: [PATCH 4/9] Use fee module (#383) --- app/app.go | 61 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/app/app.go b/app/app.go index 364a18645..f1140a1e4 100644 --- a/app/app.go +++ b/app/app.go @@ -122,6 +122,9 @@ import ( govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" extendedkeeper "github.com/babylonchain/babylon/x/zoneconcierge/extended-client-keeper" + ibcfee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" @@ -204,6 +207,7 @@ var ( ibctm.AppModuleBasic{}, transfer.AppModuleBasic{}, zoneconcierge.AppModuleBasic{}, + ibcfee.AppModuleBasic{}, ) // module account permissions @@ -215,6 +219,7 @@ var ( stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ibcfeetypes.ModuleName: nil, // TODO: decide ZonConcierge's permissions here zctypes.ModuleName: {authtypes.Minter, authtypes.Burner}, } @@ -309,6 +314,7 @@ type BabylonApp struct { // IBC-related modules IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCFeeKeeper ibcfeekeeper.Keeper // for relayer incentivization - https://github.com/cosmos/ibc/tree/main/spec/app/ics-029-fee-payment TransferKeeper ibctransferkeeper.Keeper // for cross-chain fungible token transfers ZoneConciergeKeeper zckeeper.Keeper // for cross-chain fungible token transfers @@ -378,6 +384,7 @@ func NewBabylonApp( // IBC-related modules ibcexported.StoreKey, ibctransfertypes.StoreKey, + ibcfeetypes.StoreKey, zctypes.StoreKey, wasm.StoreKey, ) @@ -512,11 +519,19 @@ func NewBabylonApp( if !ok { panic(errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries")) } + + app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + appCodec, keys[ibcfeetypes.StoreKey], + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + ) + zcKeeper := zckeeper.NewKeeper( appCodec, keys[zctypes.StoreKey], keys[zctypes.MemStoreKey], - app.IBCKeeper.ChannelKeeper, + app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, @@ -542,20 +557,9 @@ func NewBabylonApp( // Create Transfer Keepers app.TransferKeeper = ibctransferkeeper.NewKeeper( appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), - app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, ) - transferModule := transfer.NewAppModule(app.TransferKeeper) - - // Create static IBC router, add ibc-tranfer module route, then set and seal it - ibcRouter := porttypes.NewRouter() - transferIBCModule := transfer.NewIBCModule(app.TransferKeeper) - ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule) - zcIBCModule := zoneconcierge.NewIBCModule(app.ZoneConciergeKeeper) - ibcRouter.AddRoute(zctypes.ModuleName, zcIBCModule) - // Setting Router will finalize all routes by sealing router - // No more routes can be added - app.IBCKeeper.SetRouter(ibcRouter) btclightclientKeeper := *btclightclientkeeper.NewKeeper( appCodec, @@ -634,7 +638,7 @@ func NewBabylonApp( app.BankKeeper, app.StakingKeeper, distrkeeper.NewQuerier(app.DistrKeeper), - app.IBCKeeper.ChannelKeeper, + app.IBCFeeKeeper, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, scopedWasmKeeper, @@ -648,6 +652,29 @@ func NewBabylonApp( wasmOpts..., ) + // Create all supported IBC routes + var transferStack porttypes.IBCModule + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper) + + var zoneConciergeStack porttypes.IBCModule + zoneConciergeStack = zoneconcierge.NewIBCModule(app.ZoneConciergeKeeper) + zoneConciergeStack = ibcfee.NewIBCMiddleware(zoneConciergeStack, app.IBCFeeKeeper) + + var wasmStack porttypes.IBCModule + wasmStack = wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper, app.IBCFeeKeeper) + wasmStack = ibcfee.NewIBCMiddleware(wasmStack, app.IBCFeeKeeper) + + // Create static IBC router, add ibc-tranfer module route, then set and seal it + ibcRouter := porttypes.NewRouter(). + AddRoute(ibctransfertypes.ModuleName, transferStack). + AddRoute(zctypes.ModuleName, zoneConciergeStack). + AddRoute(wasm.ModuleName, wasmStack) + + // Setting Router will finalize all routes by sealing router + // No more routes can be added + app.IBCKeeper.SetRouter(ibcRouter) + // The gov proposal types can be individually enabled if len(wasmEnabledProposals) != 0 { govRouter.AddRoute(wasm.RouterKey, wasm.NewWasmProposalHandler(app.WasmKeeper, wasmEnabledProposals)) @@ -694,8 +721,9 @@ func NewBabylonApp( monitor.NewAppModule(appCodec, app.MonitorKeeper, app.AccountKeeper, app.BankKeeper), // IBC-related modules ibc.NewAppModule(app.IBCKeeper), - transferModule, + transfer.NewAppModule(app.TransferKeeper), zoneconcierge.NewAppModule(appCodec, app.ZoneConciergeKeeper, app.AccountKeeper, app.BankKeeper), + ibcfee.NewAppModule(app.IBCFeeKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -719,6 +747,7 @@ func NewBabylonApp( ibcexported.ModuleName, ibctransfertypes.ModuleName, zctypes.ModuleName, + ibcfeetypes.ModuleName, wasm.ModuleName, ) // TODO: there will be an architecture design on whether to modify slashing/evidence, specifically @@ -743,6 +772,7 @@ func NewBabylonApp( ibcexported.ModuleName, ibctransfertypes.ModuleName, zctypes.ModuleName, + ibcfeetypes.ModuleName, wasm.ModuleName, ) // Babylon does not want EndBlock processing in staking @@ -769,6 +799,7 @@ func NewBabylonApp( ibcexported.ModuleName, ibctransfertypes.ModuleName, zctypes.ModuleName, + ibcfeetypes.ModuleName, wasm.ModuleName, ) From 1ecfd70f63f47af91ce21997486bc31153f6e314 Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Wed, 17 May 2023 20:09:15 +1000 Subject: [PATCH 5/9] dockerfile: opt out version when building (#384) --- contrib/images/babylond/Dockerfile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/images/babylond/Dockerfile b/contrib/images/babylond/Dockerfile index 11bb49450..71a148639 100644 --- a/contrib/images/babylond/Dockerfile +++ b/contrib/images/babylond/Dockerfile @@ -4,8 +4,8 @@ FROM golang:1.20-alpine AS build-env # TARGETPLATFORM should be one of linux/amd64 or linux/arm64. ARG TARGETPLATFORM="linux/amd64" -# Version to build. Default is the Git HEAD. -ARG VERSION="HEAD" +# Version to build. Default is empty +ARG VERSION # Use muslc for static libs ARG BUILD_TAGS="muslc" @@ -24,7 +24,10 @@ COPY go.mod go.sum /go/src/github.com/babylonchain/babylon/ RUN go mod download # Then copy everything else COPY ./ /go/src/github.com/babylonchain/babylon/ -RUN git checkout -f ${VERSION} +# If version is set, then checkout this version +RUN if [ -n "${VERSION}" ]; then \ + git checkout -f ${VERSION}; \ + fi # Cosmwasm - Download correct libwasmvm version RUN WASMVM_VERSION=$(go list -m github.com/CosmWasm/wasmvm | cut -d ' ' -f 2) && \ From da02a88f0bc3c3f3257a5e35ddc2927da5473478 Mon Sep 17 00:00:00 2001 From: Filippos Malandrakis <35352222+filippos47@users.noreply.github.com> Date: Thu, 18 May 2023 15:43:00 +0300 Subject: [PATCH 6/9] CI: Push images for dev branch commits (#385) --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 35cf66ca0..f2b451306 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -127,3 +127,4 @@ workflows: branches: only: - main + - dev From 3def43d9c73072f3926b6f3804b1dc32c000db1e Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Tue, 23 May 2023 16:37:41 +1000 Subject: [PATCH 7/9] phase2: IBC packet and logic (#368) --- app/app.go | 68 ++- proto/babylon/zoneconcierge/v1/packet.proto | 46 +- test/e2e/configurer/base.go | 12 +- testutil/keeper/btclightclient.go | 2 +- testutil/keeper/zoneconcierge.go | 6 +- x/btclightclient/keeper/keeper.go | 21 +- x/btclightclient/keeper/state.go | 3 +- x/zoneconcierge/genesis_test.go | 2 +- x/zoneconcierge/keeper/epochs.go | 21 + x/zoneconcierge/keeper/grpc_query_test.go | 6 +- x/zoneconcierge/keeper/hooks.go | 8 + x/zoneconcierge/keeper/ibc_channels.go | 39 ++ .../keeper/ibc_packet_btc_timestamp.go | 228 ++++++++ x/zoneconcierge/keeper/keeper.go | 16 +- x/zoneconcierge/keeper/msg_server_test.go | 2 +- .../keeper/proof_epoch_sealed_test.go | 2 +- x/zoneconcierge/module_ibc.go | 81 +-- x/zoneconcierge/module_ibc_packet_test.go | 18 +- x/zoneconcierge/module_test.go | 3 +- x/zoneconcierge/types/errors.go | 33 +- x/zoneconcierge/types/events_ibc.go | 1 + x/zoneconcierge/types/expected_keepers.go | 13 + x/zoneconcierge/types/keys.go | 21 +- x/zoneconcierge/types/mocked_keepers.go | 258 ++++++++-- x/zoneconcierge/types/packet.pb.go | 486 +++++++++++++++--- x/zoneconcierge/types/zoneconcierge.go | 8 + 26 files changed, 1143 insertions(+), 261 deletions(-) create mode 100644 x/zoneconcierge/keeper/ibc_channels.go create mode 100644 x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go diff --git a/app/app.go b/app/app.go index f1140a1e4..9ce46ebb4 100644 --- a/app/app.go +++ b/app/app.go @@ -509,6 +509,31 @@ func NewBabylonApp( ), ) + btclightclientKeeper := btclightclientkeeper.NewKeeper( + appCodec, + keys[btclightclienttypes.StoreKey], + keys[btclightclienttypes.MemStoreKey], + btcConfig, + ) + checkpointingKeeper := checkpointingkeeper.NewKeeper( + appCodec, + keys[checkpointingtypes.StoreKey], + keys[checkpointingtypes.MemStoreKey], + privSigner.WrappedPV, + &epochingKeeper, + privSigner.ClientCtx, + ) + btcCheckpointKeeper := btccheckpointkeeper.NewKeeper( + appCodec, + keys[btccheckpointtypes.StoreKey], + tkeys[btccheckpointtypes.TStoreKey], + keys[btccheckpointtypes.MemStoreKey], + &btclightclientKeeper, + &checkpointingKeeper, + &powLimit, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // create Tendermint client tmClient, err := client.NewClientFromNode(privSigner.ClientCtx.NodeURI) // create a Tendermint client for ZoneConcierge if err != nil { @@ -536,8 +561,9 @@ func NewBabylonApp( &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, - nil, // CheckpointingKeeper is set later (TODO: figure out a proper way for this) - nil, // BTCCheckpoint is set later (TODO: figure out a proper way for this) + &btclightclientKeeper, + &checkpointingKeeper, + &btcCheckpointKeeper, epochingKeeper, tmClient, storeQuerier, @@ -561,13 +587,6 @@ func NewBabylonApp( app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, ) - btclightclientKeeper := *btclightclientkeeper.NewKeeper( - appCodec, - keys[btclightclienttypes.StoreKey], - keys[btclightclienttypes.MemStoreKey], - btcConfig, - ) - app.MonitorKeeper = monitorkeeper.NewKeeper( appCodec, keys[monitortypes.StoreKey], @@ -578,40 +597,13 @@ func NewBabylonApp( // add msgServiceRouter so that the epoching module can forward unwrapped messages to the staking module epochingKeeper.SetMsgServiceRouter(app.BaseApp.MsgServiceRouter()) // make ZoneConcierge to subscribe to the epoching's hooks - epochingKeeper.SetHooks( + app.EpochingKeeper = *epochingKeeper.SetHooks( epochingtypes.NewMultiEpochingHooks(app.ZoneConciergeKeeper.Hooks(), app.MonitorKeeper.Hooks()), ) - app.EpochingKeeper = epochingKeeper - - checkpointingKeeper := - checkpointingkeeper.NewKeeper( - appCodec, - keys[checkpointingtypes.StoreKey], - keys[checkpointingtypes.MemStoreKey], - privSigner.WrappedPV, - app.EpochingKeeper, - privSigner.ClientCtx, - ) app.CheckpointingKeeper = *checkpointingKeeper.SetHooks( checkpointingtypes.NewMultiCheckpointingHooks(app.EpochingKeeper.Hooks(), app.ZoneConciergeKeeper.Hooks(), app.MonitorKeeper.Hooks()), ) - app.ZoneConciergeKeeper.SetCheckpointingKeeper(app.CheckpointingKeeper) - - // TODO for now use mocks, as soon as Checkpoining and lightClient will have correct interfaces - // change to correct implementations - app.BtcCheckpointKeeper = - btccheckpointkeeper.NewKeeper( - appCodec, - keys[btccheckpointtypes.StoreKey], - tkeys[btccheckpointtypes.TStoreKey], - keys[btccheckpointtypes.MemStoreKey], - &btclightclientKeeper, - app.CheckpointingKeeper, - &powLimit, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) - app.ZoneConciergeKeeper.SetBtcCheckpointKeeper(app.BtcCheckpointKeeper) - + app.BtcCheckpointKeeper = btcCheckpointKeeper app.BTCLightClientKeeper = *btclightclientKeeper.SetHooks( btclightclienttypes.NewMultiBTCLightClientHooks(app.BtcCheckpointKeeper.Hooks()), ) diff --git a/proto/babylon/zoneconcierge/v1/packet.proto b/proto/babylon/zoneconcierge/v1/packet.proto index a330e0141..a9b4ef163 100644 --- a/proto/babylon/zoneconcierge/v1/packet.proto +++ b/proto/babylon/zoneconcierge/v1/packet.proto @@ -1,15 +1,53 @@ syntax = "proto3"; package babylon.zoneconcierge.v1; +import "babylon/btccheckpoint/v1/btccheckpoint.proto"; +import "babylon/checkpointing/v1/checkpoint.proto"; +import "babylon/btclightclient/v1/btclightclient.proto"; +import "babylon/epoching/v1/epoching.proto"; +import "babylon/zoneconcierge/v1/zoneconcierge.proto"; + option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; // ZoneconciergePacketData is the message that defines the IBC packets of // ZoneConcierge message ZoneconciergePacketData { // packet is the actual message carried in the IBC packet - oneof packet { Heartbeat heartbeart = 1; } + oneof packet { + BTCTimestamp btc_timestamp = 1; + } } -// Heartbeat is a heartbeat message that can be carried in IBC packets of -// ZoneConcierge -message Heartbeat { string msg = 1; } +// BTCTimestamp is a BTC timestamp that carries information of a BTC-finalised epoch +// It includes a number of BTC headers, a raw checkpoint, an epoch metadata, and +// a CZ header if there exists CZ headers checkpointed to this epoch. +// Upon a newly finalised epoch in Babylon, Babylon will send a BTC timestamp to each +// Cosmos zone that has phase-2 integration with Babylon via IBC. +message BTCTimestamp { + // header is the last CZ header in the finalized Babylon epoch + babylon.zoneconcierge.v1.IndexedHeader header = 1; + + /* + Data for BTC light client + */ + // btc_headers is BTC headers between + // - the block AFTER the common ancestor of BTC tip at epoch `lastFinalizedEpoch-1` and BTC tip at epoch `lastFinalizedEpoch` + // - BTC tip at epoch `lastFinalizedEpoch` + // where `lastFinalizedEpoch` is the last finalised epoch in Babylon + repeated babylon.btclightclient.v1.BTCHeaderInfo btc_headers = 2; + + /* + Data for Babylon epoch chain + */ + // epoch_info is the metadata of the sealed epoch + babylon.epoching.v1.Epoch epoch_info = 3; + // raw_checkpoint is the raw checkpoint that seals this epoch + babylon.checkpointing.v1.RawCheckpoint raw_checkpoint = 4; + // btc_submission_key is position of two BTC txs that include the raw checkpoint of this epoch + babylon.btccheckpoint.v1.SubmissionKey btc_submission_key = 5; + + /* + Proofs that the header is finalized + */ + babylon.zoneconcierge.v1.ProofFinalizedChainInfo proof = 6; +} \ No newline at end of file diff --git a/test/e2e/configurer/base.go b/test/e2e/configurer/base.go index 5e22941fc..f27ca0227 100644 --- a/test/e2e/configurer/base.go +++ b/test/e2e/configurer/base.go @@ -13,11 +13,11 @@ import ( "github.com/stretchr/testify/require" + "github.com/babylonchain/babylon/test/e2e/configurer/chain" "github.com/babylonchain/babylon/test/e2e/containers" "github.com/babylonchain/babylon/test/e2e/initialization" - - "github.com/babylonchain/babylon/test/e2e/configurer/chain" "github.com/babylonchain/babylon/test/e2e/util" + zctypes "github.com/babylonchain/babylon/x/zoneconcierge/types" ) // baseConfigurer is the base implementation for the @@ -168,7 +168,13 @@ func (bc *baseConfigurer) runIBCRelayer(chainConfigA *chain.Config, chainConfigB func (bc *baseConfigurer) connectIBCChains(chainA *chain.Config, chainB *chain.Config) error { bc.t.Logf("connecting %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) - cmd := []string{"hermes", "create", "channel", "--a-chain", chainA.ChainMeta.Id, "--b-chain", chainB.ChainMeta.Id, "--a-port", "zoneconcierge", "--b-port", "zoneconcierge", "--new-client-connection", "--yes"} + cmd := []string{"hermes", "create", "channel", + "--a-chain", chainA.ChainMeta.Id, "--b-chain", chainB.ChainMeta.Id, // channel ID + "--a-port", zctypes.PortID, "--b-port", zctypes.PortID, // port + "--order", zctypes.Ordering.String(), // ordering + "--channel-version", zctypes.Version, // version + "--new-client-connection", "--yes", + } _, _, err := bc.containerManager.ExecHermesCmd(bc.t, cmd, "SUCCESS") if err != nil { return err diff --git a/testutil/keeper/btclightclient.go b/testutil/keeper/btclightclient.go index 90d5ba44d..f6b887b3f 100644 --- a/testutil/keeper/btclightclient.go +++ b/testutil/keeper/btclightclient.go @@ -42,5 +42,5 @@ func BTCLightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) - return k, ctx + return &k, ctx } diff --git a/testutil/keeper/zoneconcierge.go b/testutil/keeper/zoneconcierge.go index d4b6e67b0..e300d8025 100644 --- a/testutil/keeper/zoneconcierge.go +++ b/testutil/keeper/zoneconcierge.go @@ -41,6 +41,9 @@ func (zoneconciergeChannelKeeper) ChanCloseInit(ctx sdk.Context, portID, channel func (zoneconciergeChannelKeeper) GetAllChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { return nil } +func (zoneconciergeChannelKeeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, ibcexported.ClientState, error) { + return "", nil, nil +} // zoneconciergeportKeeper is a stub of PortKeeper type zoneconciergePortKeeper struct{} @@ -61,7 +64,7 @@ func (zoneconciergeStoreQuerier) Query(req abci.RequestQuery) abci.ResponseQuery } } -func ZoneConciergeKeeper(t testing.TB, checkpointingKeeper types.CheckpointingKeeper, btccKeeper types.BtcCheckpointKeeper, epochingKeeper types.EpochingKeeper, tmClient types.TMClient) (*keeper.Keeper, sdk.Context) { +func ZoneConciergeKeeper(t testing.TB, btclcKeeper types.BTCLightClientKeeper, checkpointingKeeper types.CheckpointingKeeper, btccKeeper types.BtcCheckpointKeeper, epochingKeeper types.EpochingKeeper, tmClient types.TMClient) (*keeper.Keeper, sdk.Context) { logger := log.NewNopLogger() storeKey := sdk.NewKVStoreKey(types.StoreKey) @@ -85,6 +88,7 @@ func ZoneConciergeKeeper(t testing.TB, checkpointingKeeper types.CheckpointingKe zoneconciergePortKeeper{}, nil, // TODO: mock this keeper nil, // TODO: mock this keeper + btclcKeeper, checkpointingKeeper, btccKeeper, epochingKeeper, diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index da92aff54..8231ba260 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -27,9 +27,9 @@ func NewKeeper( storeKey, memKey storetypes.StoreKey, btcConfig bbn.BtcConfig, -) *Keeper { +) Keeper { - return &Keeper{ + return Keeper{ cdc: cdc, storeKey: storeKey, memKey: memKey, @@ -271,3 +271,20 @@ func (k Keeper) GetHeaderByHeight(ctx sdk.Context, height uint64) *types.BTCHead return info } + +// GetHighestCommonAncestor traverses the ancestors of both headers +// to identify the common ancestor with the highest height +func (k Keeper) GetHighestCommonAncestor(ctx sdk.Context, header1 *types.BTCHeaderInfo, header2 *types.BTCHeaderInfo) *types.BTCHeaderInfo { + return k.headersState(ctx).GetHighestCommonAncestor(header1, header2) +} + +// GetInOrderAncestorsUntil returns the list of nodes starting from the block *after* the `ancestor` and ending with the `descendant`. +func (k Keeper) GetInOrderAncestorsUntil(ctx sdk.Context, descendant *types.BTCHeaderInfo, ancestor *types.BTCHeaderInfo) []*types.BTCHeaderInfo { + return k.headersState(ctx).GetInOrderAncestorsUntil(descendant, ancestor) +} + +// GetMainChainUpTo returns the current canonical chain as a collection of block headers +// starting from the tip and ending on the header that has `depth` distance from it. +func (k Keeper) GetMainChainUpTo(ctx sdk.Context, depth uint64) []*types.BTCHeaderInfo { + return k.headersState(ctx).GetMainChainUpTo(depth) +} diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index cc3c19767..439d74356 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -210,8 +210,7 @@ func (s headersState) GetHeaderAncestryUpTo(currentHeader *types.BTCHeaderInfo, } // GetMainChainUpTo returns the current canonical chain as a collection of block headers -// -// starting from the tip and ending on the header that has a depth distance from it. +// starting from the tip and ending on the header that has `depth` distance from it. func (s headersState) GetMainChainUpTo(depth uint64) []*types.BTCHeaderInfo { // If there is no tip, there is no base header if !s.TipExists() { diff --git a/x/zoneconcierge/genesis_test.go b/x/zoneconcierge/genesis_test.go index 731765ab6..c0fb0bf36 100644 --- a/x/zoneconcierge/genesis_test.go +++ b/x/zoneconcierge/genesis_test.go @@ -15,7 +15,7 @@ func TestGenesis(t *testing.T) { PortId: types.PortID, } - k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil) + k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) zoneconcierge.InitGenesis(ctx, *k, genesisState) got := zoneconcierge.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/zoneconcierge/keeper/epochs.go b/x/zoneconcierge/keeper/epochs.go index 2fc919d99..ac0511a19 100644 --- a/x/zoneconcierge/keeper/epochs.go +++ b/x/zoneconcierge/keeper/epochs.go @@ -1,11 +1,32 @@ package keeper import ( + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" "github.com/babylonchain/babylon/x/zoneconcierge/types" sdk "github.com/cosmos/cosmos-sdk/types" ) +// GetFinalizingBTCTip gets the BTC tip when the last epoch is finalised +func (k Keeper) GetFinalizingBTCTip(ctx sdk.Context) *btclctypes.BTCHeaderInfo { + store := ctx.KVStore(k.storeKey) + if !store.Has(types.FinalizingBTCTipKey) { + return nil + } + btcTipBytes := store.Get(types.FinalizingBTCTipKey) + var btcTip btclctypes.BTCHeaderInfo + k.cdc.MustUnmarshal(btcTipBytes, &btcTip) + return &btcTip +} + +// setFinalizingBTCTip sets the last finalised BTC tip +// called upon each AfterRawCheckpointFinalized hook invocation +func (k Keeper) setFinalizingBTCTip(ctx sdk.Context, btcTip *btclctypes.BTCHeaderInfo) { + store := ctx.KVStore(k.storeKey) + btcTipBytes := k.cdc.MustMarshal(btcTip) + store.Set(types.FinalizingBTCTipKey, btcTipBytes) +} + // GetFinalizedEpoch gets the last finalised epoch // used upon querying the last BTC-finalised chain info for CZs func (k Keeper) GetFinalizedEpoch(ctx sdk.Context) (uint64, error) { diff --git a/x/zoneconcierge/keeper/grpc_query_test.go b/x/zoneconcierge/keeper/grpc_query_test.go index 08ad7f6cf..4a5139e0f 100644 --- a/x/zoneconcierge/keeper/grpc_query_test.go +++ b/x/zoneconcierge/keeper/grpc_query_test.go @@ -395,6 +395,10 @@ func FuzzFinalizedChainInfo(f *testing.F) { epochingKeeper.EXPECT().GetEpoch(gomock.Any()).Return(epoch).AnyTimes() epochingKeeper.EXPECT().GetHistoricalEpoch(gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return(epoch, nil).AnyTimes() epochingKeeper.EXPECT().ProveAppHashInEpoch(gomock.Any(), gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return(&tmcrypto.Proof{}, nil).AnyTimes() + // mock btclc keeper + btclcKeeper := zctypes.NewMockBTCLightClientKeeper(ctrl) + mockBTCHeaderInfo := datagen.GenRandomBTCHeaderInfo(r) + btclcKeeper.EXPECT().GetTipInfo(gomock.Any()).Return(mockBTCHeaderInfo).AnyTimes() // mock Tendermint client // TODO: integration tests with Tendermint @@ -404,7 +408,7 @@ func FuzzFinalizedChainInfo(f *testing.F) { } tmClient.EXPECT().Tx(gomock.Any(), gomock.Any(), true).Return(resTx, nil).AnyTimes() - zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, checkpointingKeeper, btccKeeper, epochingKeeper, tmClient) + zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, btclcKeeper, checkpointingKeeper, btccKeeper, epochingKeeper, tmClient) hooks := zcKeeper.Hooks() var ( diff --git a/x/zoneconcierge/keeper/hooks.go b/x/zoneconcierge/keeper/hooks.go index 95b52a65f..ff6335eae 100644 --- a/x/zoneconcierge/keeper/hooks.go +++ b/x/zoneconcierge/keeper/hooks.go @@ -92,6 +92,14 @@ func (h Hooks) AfterEpochEnds(ctx sdk.Context, epoch uint64) { func (h Hooks) AfterRawCheckpointFinalized(ctx sdk.Context, epoch uint64) error { // upon an epoch has been finalised, update the last finalised epoch h.k.setFinalizedEpoch(ctx, epoch) + + // send BTC timestamp to all open channels with ZoneConcierge + h.k.BroadcastBTCTimestamps(ctx, epoch) + + // retrieve and update the last finalising BTC tip + btcTip := h.k.btclcKeeper.GetTipInfo(ctx) + h.k.setFinalizingBTCTip(ctx, btcTip) + return nil } diff --git a/x/zoneconcierge/keeper/ibc_channels.go b/x/zoneconcierge/keeper/ibc_channels.go new file mode 100644 index 000000000..a3709caee --- /dev/null +++ b/x/zoneconcierge/keeper/ibc_channels.go @@ -0,0 +1,39 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +func (k Keeper) GetAllChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { + return k.channelKeeper.GetAllChannels(ctx) +} + +// GetAllOpenZCChannels returns all open channels that are connected to ZoneConcierge's port +func (k Keeper) GetAllOpenZCChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { + zcPort := k.GetPort(ctx) + channels := k.GetAllChannels(ctx) + + openZCChannels := []channeltypes.IdentifiedChannel{} + for _, channel := range channels { + if channel.State != channeltypes.OPEN { + continue + } + if channel.PortId != zcPort { + continue + } + openZCChannels = append(openZCChannels, channel) + } + + return openZCChannels +} + +// isChannelUninitialized checks whether the channel is not initilialised yet +// it's done by checking whether the packet sequence number is 1 (the first sequence number) or not +func (k Keeper) isChannelUninitialized(ctx sdk.Context, channel channeltypes.IdentifiedChannel) bool { + portID := channel.PortId + channelID := channel.ChannelId + // NOTE: channeltypes.IdentifiedChannel object is guaranteed to exist, so guaranteed to be found + nextSeqSend, _ := k.channelKeeper.GetNextSequenceSend(ctx, portID, channelID) + return nextSeqSend == 1 +} diff --git a/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go new file mode 100644 index 000000000..1b44e9229 --- /dev/null +++ b/x/zoneconcierge/keeper/ibc_packet_btc_timestamp.go @@ -0,0 +1,228 @@ +package keeper + +import ( + "fmt" + + bbn "github.com/babylonchain/babylon/types" + btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" + checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" + epochingtypes "github.com/babylonchain/babylon/x/epoching/types" + "github.com/babylonchain/babylon/x/zoneconcierge/types" + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +// finalizedInfo is a private struct that stores metadata and proofs +// identical to all BTC timestamps in the same epoch +type finalizedInfo struct { + EpochInfo *epochingtypes.Epoch + RawCheckpoint *checkpointingtypes.RawCheckpoint + BTCSubmissionKey *btcctypes.SubmissionKey + ProofEpochSealed *types.ProofEpochSealed + ProofEpochSubmitted []*btcctypes.TransactionInfo + BTCHeaders []*btclctypes.BTCHeaderInfo +} + +// getChainID gets the ID of the counterparty chain under the given channel +func (k Keeper) getChainID(ctx sdk.Context, channel channeltypes.IdentifiedChannel) (string, error) { + // get clientState under this channel + _, clientState, err := k.channelKeeper.GetChannelClientState(ctx, channel.PortId, channel.ChannelId) + if err != nil { + return "", err + } + // cast clientState to tendermint clientState + // TODO: support for chains other than Cosmos zones + tmClient, ok := clientState.(*ibctmtypes.ClientState) + if !ok { + return "", fmt.Errorf("client must be a Tendermint client, expected: %T, got: %T", &ibctmtypes.ClientState{}, tmClient) + } + return tmClient.ChainId, nil +} + +// getBTCHeadersDuringLastFinalizedEpoch gets BTC headers between +// - the block AFTER the common ancestor of BTC tip at epoch `lastFinalizedEpoch-1` and BTC tip at epoch `lastFinalizedEpoch` +// - BTC tip at epoch `lastFinalizedEpoch` +// where `lastFinalizedEpoch` is the last finalised epoch +func (k Keeper) getBTCHeadersDuringLastFinalizedEpoch(ctx sdk.Context) []*btclctypes.BTCHeaderInfo { + oldBTCTip := k.GetFinalizingBTCTip(ctx) // NOTE: BTC tip in KVStore has not been updated yet + if oldBTCTip == nil { + // this happens upon the first finalised epoch. Use base header instead + oldBTCTip = k.btclcKeeper.GetBaseBTCHeader(ctx) + } + curBTCTip := k.btclcKeeper.GetTipInfo(ctx) + commonAncestor := k.btclcKeeper.GetHighestCommonAncestor(ctx, oldBTCTip, curBTCTip) + btcHeaders := k.btclcKeeper.GetInOrderAncestorsUntil(ctx, curBTCTip, commonAncestor) + + return btcHeaders +} + +// getFinalizedInfo returns metadata and proofs that are identical to all BTC timestamps in the same epoch +func (k Keeper) getFinalizedInfo(ctx sdk.Context, epochNum uint64) (*finalizedInfo, error) { + finalizedEpochInfo, err := k.epochingKeeper.GetHistoricalEpoch(ctx, epochNum) + if err != nil { + return nil, err + } + + // assign raw checkpoint + rawCheckpoint, err := k.checkpointingKeeper.GetRawCheckpoint(ctx, epochNum) + if err != nil { + return nil, err + } + + // assign BTC submission key + ed := k.btccKeeper.GetEpochData(ctx, epochNum) + bestSubmissionBtcInfo := k.btccKeeper.GetEpochBestSubmissionBtcInfo(ctx, ed) + if bestSubmissionBtcInfo == nil { + return nil, fmt.Errorf("empty bestSubmissionBtcInfo") + } + btcSubmissionKey := &bestSubmissionBtcInfo.SubmissionKey + + // proof that the epoch is sealed + proofEpochSealed, err := k.ProveEpochSealed(ctx, epochNum) + if err != nil { + return nil, err + } + + // proof that the epoch's checkpoint is submitted to BTC + // i.e., the two `TransactionInfo`s for the checkpoint + proofEpochSubmitted, err := k.ProveEpochSubmitted(ctx, btcSubmissionKey) + if err != nil { + return nil, err + } + + // get new BTC headers since the 2nd last finalised epoch and the last finalised epoch + btcHeaders := k.getBTCHeadersDuringLastFinalizedEpoch(ctx) + + // construct finalizedInfo + finalizedInfo := &finalizedInfo{ + EpochInfo: finalizedEpochInfo, + RawCheckpoint: rawCheckpoint.Ckpt, + BTCSubmissionKey: btcSubmissionKey, + ProofEpochSealed: proofEpochSealed, + ProofEpochSubmitted: proofEpochSubmitted, + BTCHeaders: btcHeaders, + } + + return finalizedInfo, nil +} + +// createBTCTimestamp creates a BTC timestamp from finalizedInfo for a given IBC channel +// where the counterparty is a Cosmos zone +func (k Keeper) createBTCTimestamp(ctx sdk.Context, chainID string, channel channeltypes.IdentifiedChannel, finalizedInfo *finalizedInfo) (*types.BTCTimestamp, error) { + // if the Babylon contract in this channel has not been initialised, get headers from + // the tip to (w+1+len(finalizedInfo.BTCHeaders))-deep header + var btcHeaders []*btclctypes.BTCHeaderInfo + if k.isChannelUninitialized(ctx, channel) { + w := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout + depth := w + 1 + uint64(len(finalizedInfo.BTCHeaders)) + + btcHeaders = k.btclcKeeper.GetMainChainUpTo(ctx, depth) + if btcHeaders == nil { + return nil, fmt.Errorf("failed to get Bitcoin main chain up to depth %d", depth) + } + bbn.Reverse(btcHeaders) + } else { + btcHeaders = finalizedInfo.BTCHeaders + } + + // get finalised chainInfo + // NOTE: it's possible that this chain does not have chain info at the moment + // In this case, skip sending BTC timestamp for this chain at this epoch + epochNum := finalizedInfo.EpochInfo.EpochNumber + finalizedChainInfo, err := k.GetEpochChainInfo(ctx, chainID, epochNum) + if err != nil { + return nil, fmt.Errorf("no finalizedChainInfo for chain %s at epoch %d", chainID, epochNum) + } + + // construct BTC timestamp from everything + // NOTE: it's possible that there is no header checkpointed in this epoch + btcTimestamp := &types.BTCTimestamp{ + Header: nil, + BtcHeaders: btcHeaders, + EpochInfo: finalizedInfo.EpochInfo, + RawCheckpoint: finalizedInfo.RawCheckpoint, + BtcSubmissionKey: finalizedInfo.BTCSubmissionKey, + Proof: &types.ProofFinalizedChainInfo{ + ProofTxInBlock: nil, + ProofHeaderInEpoch: nil, + ProofEpochSealed: finalizedInfo.ProofEpochSealed, + ProofEpochSubmitted: finalizedInfo.ProofEpochSubmitted, + }, + } + + // if there is a CZ header checkpointed in this finalised epoch, + // add this CZ header and corresponding proofs to the BTC timestamp + if finalizedChainInfo.LatestHeader.BabylonEpoch == epochNum { + // get proofTxInBlock + proofTxInBlock, err := k.ProveTxInBlock(ctx, finalizedChainInfo.LatestHeader.BabylonTxHash) + if err != nil { + return nil, fmt.Errorf("failed to generate proofTxInBlock for chain %s: %w", chainID, err) + } + + // get proofHeaderInEpoch + proofHeaderInEpoch, err := k.ProveHeaderInEpoch(ctx, finalizedChainInfo.LatestHeader.BabylonHeader, finalizedInfo.EpochInfo) + if err != nil { + return nil, fmt.Errorf("failed to generate proofHeaderInEpoch for chain %s: %w", chainID, err) + } + + btcTimestamp.Header = finalizedChainInfo.LatestHeader + btcTimestamp.Proof.ProofTxInBlock = proofTxInBlock + btcTimestamp.Proof.ProofHeaderInEpoch = proofHeaderInEpoch + } + + return btcTimestamp, nil +} + +// BroadcastBTCTimestamps sends an IBC packet of BTC timestamp to all open IBC channels to ZoneConcierge +func (k Keeper) BroadcastBTCTimestamps(ctx sdk.Context, epochNum uint64) { + // Babylon does not broadcast BTC timestamps until finalising epoch 1 + if epochNum < 1 { + k.Logger(ctx).Info("Babylon does not finalize epoch 1 yet, skip broadcasting BTC timestamps") + return + } + + // get all channels that are open and are connected to ZoneConcierge's port + openZCChannels := k.GetAllOpenZCChannels(ctx) + if len(openZCChannels) == 0 { + k.Logger(ctx).Info("no open IBC channel with ZoneConcierge, skip broadcasting BTC timestamps") + return + } + + k.Logger(ctx).Info("there exists open IBC channels with ZoneConcierge, generating BTC timestamps", "number of channels", len(openZCChannels)) + + // get all metadata shared across BTC timestamps in the same epoch + finalizedInfo, err := k.getFinalizedInfo(ctx, epochNum) + if err != nil { + k.Logger(ctx).Error("failed to generate metadata shared across BTC timestamps in the same epoch, skip broadcasting BTC timestamps", "error", err) + return + } + + // for each channel, construct and send BTC timestamp + for _, channel := range openZCChannels { + // get the ID of the chain under this channel + chainID, err := k.getChainID(ctx, channel) + if err != nil { + k.Logger(ctx).Error("failed to get chain ID, skip sending BTC timestamp for this chain", "channelID", channel.ChannelId, "error", err) + continue + } + + // generate timestamp for this channel + btcTimestamp, err := k.createBTCTimestamp(ctx, chainID, channel, finalizedInfo) + if err != nil { + k.Logger(ctx).Error("failed to generate BTC timestamp, skip sending BTC timestamp for this chain", "chainID", chainID, "error", err) + continue + } + + // wrap BTC timestamp to IBC packet + packet := types.NewBTCTimestampPacketData(btcTimestamp) + // send IBC packet + if err := k.SendIBCPacket(ctx, channel, packet); err != nil { + k.Logger(ctx).Error("failed to send BTC timestamp IBC packet, skip sending BTC timestamp for this chain", "chainID", chainID, "channelID", channel.ChannelId, "error", err) + continue + } + } +} + +// TODO: test case with at BTC headers and checkpoints diff --git a/x/zoneconcierge/keeper/keeper.go b/x/zoneconcierge/keeper/keeper.go index d2f928e8a..b7ce6ada1 100644 --- a/x/zoneconcierge/keeper/keeper.go +++ b/x/zoneconcierge/keeper/keeper.go @@ -7,7 +7,6 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" ) @@ -23,6 +22,7 @@ type ( portKeeper types.PortKeeper authKeeper types.AccountKeeper bankKeeper types.BankKeeper + btclcKeeper types.BTCLightClientKeeper checkpointingKeeper types.CheckpointingKeeper btccKeeper types.BtcCheckpointKeeper epochingKeeper types.EpochingKeeper @@ -41,6 +41,7 @@ func NewKeeper( portKeeper types.PortKeeper, authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, + btclcKeeper types.BTCLightClientKeeper, checkpointingKeeper types.CheckpointingKeeper, btccKeeper types.BtcCheckpointKeeper, epochingKeeper types.EpochingKeeper, @@ -57,6 +58,7 @@ func NewKeeper( portKeeper: portKeeper, authKeeper: authKeeper, bankKeeper: bankKeeper, + btclcKeeper: btclcKeeper, checkpointingKeeper: checkpointingKeeper, btccKeeper: btccKeeper, epochingKeeper: epochingKeeper, @@ -106,15 +108,3 @@ func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Cap func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { return k.scopedKeeper.ClaimCapability(ctx, cap, name) } - -func (k Keeper) GetAllChannels(ctx sdk.Context) []channeltypes.IdentifiedChannel { - return k.channelKeeper.GetAllChannels(ctx) -} - -func (k *Keeper) SetBtcCheckpointKeeper(btccKeeper types.BtcCheckpointKeeper) { - k.btccKeeper = btccKeeper -} - -func (k *Keeper) SetCheckpointingKeeper(checkpointingKeeper types.CheckpointingKeeper) { - k.checkpointingKeeper = checkpointingKeeper -} diff --git a/x/zoneconcierge/keeper/msg_server_test.go b/x/zoneconcierge/keeper/msg_server_test.go index 092ab042c..d7e4e1502 100644 --- a/x/zoneconcierge/keeper/msg_server_test.go +++ b/x/zoneconcierge/keeper/msg_server_test.go @@ -12,6 +12,6 @@ import ( //nolint:unused func setupMsgServer(t testing.TB) (types.MsgServer, context.Context) { - k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil) + k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) return keeper.NewMsgServerImpl(*k), sdk.WrapSDKContext(ctx) } diff --git a/x/zoneconcierge/keeper/proof_epoch_sealed_test.go b/x/zoneconcierge/keeper/proof_epoch_sealed_test.go index e29c6eae6..8f6dded9f 100644 --- a/x/zoneconcierge/keeper/proof_epoch_sealed_test.go +++ b/x/zoneconcierge/keeper/proof_epoch_sealed_test.go @@ -82,7 +82,7 @@ func FuzzProofEpochSealed_BLSSig(f *testing.F) { epochingKeeper.EXPECT().GetEpoch(gomock.Any()).Return(epoch).AnyTimes() epochingKeeper.EXPECT().GetHistoricalEpoch(gomock.Any(), gomock.Eq(epoch.EpochNumber)).Return(epoch, nil).AnyTimes() // create zcKeeper and ctx - zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, checkpointingKeeper, nil, epochingKeeper, nil) + zcKeeper, ctx := testkeeper.ZoneConciergeKeeper(t, nil, checkpointingKeeper, nil, epochingKeeper, nil) // prove proof, err := zcKeeper.ProveEpochSealed(ctx, epoch.EpochNumber) diff --git a/x/zoneconcierge/module_ibc.go b/x/zoneconcierge/module_ibc.go index 758cc6451..074cbc9ad 100644 --- a/x/zoneconcierge/module_ibc.go +++ b/x/zoneconcierge/module_ibc.go @@ -1,8 +1,7 @@ package zoneconcierge import ( - "fmt" - + errorsmod "cosmossdk.io/errors" "github.com/babylonchain/babylon/x/zoneconcierge/keeper" "github.com/babylonchain/babylon/x/zoneconcierge/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,7 +11,6 @@ import ( porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" - errorsmod "cosmossdk.io/errors" ) type IBCModule struct { @@ -36,12 +34,22 @@ func (im IBCModule) OnChanOpenInit( counterparty channeltypes.Counterparty, version string, ) (string, error) { + // the IBC channel has to be ordered + if order != channeltypes.ORDERED { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.ORDERED, order) + } + // Require portID to be the one that ZoneConcierge is bound to boundPort := im.keeper.GetPort(ctx) if boundPort != portID { return "", errorsmod.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) } + // ensure consistency of the protocol version + if version != types.Version { + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "got %s, expected %s", version, types.Version) + } + // Claim channel capability passed back by IBC module if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { return "", err @@ -61,12 +69,22 @@ func (im IBCModule) OnChanOpenTry( counterparty channeltypes.Counterparty, counterpartyVersion string, ) (string, error) { + // the IBC channel has to be ordered + if order != channeltypes.ORDERED { + return "", errorsmod.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.ORDERED, order) + } + // Require portID to be the one that ZoneConcierge is bound to boundPort := im.keeper.GetPort(ctx) if boundPort != portID { return "", errorsmod.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) } + // ensure consistency of the protocol version + if counterpartyVersion != types.Version { + return "", errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version) + } + // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) // If module can already authenticate the capability then module already owns it so we don't need to claim @@ -89,10 +107,11 @@ func (im IBCModule) OnChanOpenAck( _, counterpartyVersion string, ) error { - // // TODO (Babylon): check version consistency (this requires modifying CZ code) - // if counterpartyVersion != types.Version { - // return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) - // } + // check version consistency + if counterpartyVersion != types.Version { + return errorsmod.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) + } + return nil } @@ -130,22 +149,9 @@ func (im IBCModule) OnRecvPacket( modulePacket channeltypes.Packet, relayer sdk.AccAddress, ) ibcexported.Acknowledgement { - var ack channeltypes.Acknowledgement - - var modulePacketData types.ZoneconciergePacketData - if err := modulePacketData.Unmarshal(modulePacket.GetData()); err != nil { - return channeltypes.NewErrorAcknowledgement(errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet data: %s", err.Error())) - } - - // // TODO (Babylon): Dispatch and process packet - // switch packet := modulePacketData.Packet.(type) { - // default: - // err := fmt.Errorf("unrecognized %s packet type: %T", types.ModuleName, packet) - // return channeltypes.NewErrorAcknowledgement(err) - // } - + // Babylon is supposed to not take any IBC packet // NOTE: acknowledgement will be written synchronously during IBC handler execution. - return ack + return channeltypes.NewErrorAcknowledgement(errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "Babylon is supposed to not take any IBC packet")) } // OnAcknowledgementPacket implements the IBCModule interface @@ -156,12 +162,17 @@ func (im IBCModule) OnAcknowledgementPacket( relayer sdk.AccAddress, ) error { var ack channeltypes.Acknowledgement - if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { - return errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet acknowledgement: %v", err) + // `x/wasm` uses both protobuf and json to encoded acknowledgement, so we need to try both here + // - for acknowledgment message with errors defined in `x/wasm`, it uses json + // - for all other acknowledgement messages, it uses protobuf + if errProto := types.ModuleCdc.Unmarshal(acknowledgement, &ack); errProto != nil { + im.keeper.Logger(ctx).Error("cannot unmarshal packet acknowledgement with protobuf", "error", errProto) + if errJson := types.ModuleCdc.Unmarshal(acknowledgement, &ack); errJson != nil { + im.keeper.Logger(ctx).Error("cannot unmarshal packet acknowledgement with json", "error", errJson) + return errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet acknowledgement with protobuf (error: %v) or json (error: %v)", errProto, errJson) + } } - var eventType string - // // TODO (Babylon): Dispatch and process packet // switch packet := modulePacketData.Packet.(type) { // default: @@ -169,26 +180,22 @@ func (im IBCModule) OnAcknowledgementPacket( // return errorsmod.Wrap(sdkerrors.ErrUnknownRequest, errMsg) // } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - eventType, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), - sdk.NewAttribute(types.AttributeKeyAck, fmt.Sprintf("%v", ack)), - ), - ) - switch resp := ack.Response.(type) { case *channeltypes.Acknowledgement_Result: + im.keeper.Logger(ctx).Info("received an Acknowledgement message", "result", string(resp.Result)) ctx.EventManager().EmitEvent( sdk.NewEvent( - eventType, + types.EventTypeAck, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(types.AttributeKeyAckSuccess, string(resp.Result)), ), ) case *channeltypes.Acknowledgement_Error: + im.keeper.Logger(ctx).Error("received an Acknowledgement error message", "error", resp.Error) ctx.EventManager().EmitEvent( sdk.NewEvent( - eventType, + types.EventTypeAck, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(types.AttributeKeyAckError, resp.Error), ), ) @@ -215,5 +222,7 @@ func (im IBCModule) OnTimeoutPacket( // return errorsmod.Wrap(sdkerrors.ErrUnknownRequest, errMsg) // } + // TODO: close channel upon timeout + return nil } diff --git a/x/zoneconcierge/module_ibc_packet_test.go b/x/zoneconcierge/module_ibc_packet_test.go index 3dbcdf712..790ed1cbf 100644 --- a/x/zoneconcierge/module_ibc_packet_test.go +++ b/x/zoneconcierge/module_ibc_packet_test.go @@ -38,10 +38,14 @@ func (suite *ZoneConciergeTestSuite) TestSetChannel() { path := ibctesting.NewPath(suite.babylonChain, suite.czChain) - // set the port ID to be consistent with ZoneConcierge - // in practice, such negotiation is done by the handshake protocol driven by the relayer - path.EndpointA.ChannelConfig.PortID = zctypes.PortID - path.EndpointB.ChannelConfig.PortID = zctypes.PortID + // overwrite the channel config + channelCfg := &ibctesting.ChannelConfig{ + PortID: zctypes.PortID, + Version: zctypes.Version, + Order: zctypes.Ordering, + } + path.EndpointA.ChannelConfig = channelCfg + path.EndpointB.ChannelConfig = channelCfg // create client and connections on both chains suite.coordinator.SetupConnections(path) @@ -74,7 +78,7 @@ func (suite *ZoneConciergeTestSuite) TestSetChannel() { nextSeqSend, found := suite.babylonChain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(suite.babylonChain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.True(found) - // commit blocks to create some pending heartbeat packets + // commit some blocks numBlocks := rand.Intn(10) for i := 0; i < numBlocks; i++ { suite.coordinator.CommitBlock(suite.babylonChain) @@ -84,7 +88,9 @@ func (suite *ZoneConciergeTestSuite) TestSetChannel() { newNextSeqSend, found := suite.babylonChain.App.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(suite.babylonChain.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) suite.True(found) - // Assert the gap between two sequence numbers to be zero, since no packet is sent during these blocks + // Assert the gap between two sequence numbers to be zero + // No packet is supposed to be sent during these blocks. + // IBC packet (i.e., BTC timestamp) is sent only upon newly finalised epoch suite.Equal(newNextSeqSend, nextSeqSend) // update clients to ensure no panic happens diff --git a/x/zoneconcierge/module_test.go b/x/zoneconcierge/module_test.go index 27a531fc7..b0b96e45b 100644 --- a/x/zoneconcierge/module_test.go +++ b/x/zoneconcierge/module_test.go @@ -15,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clientexported "github.com/cosmos/ibc-go/v7/modules/core/02-client/exported" - "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -89,7 +88,7 @@ func (suite *ZoneConciergeTestSuite) SetupTest() { pubKey, err := suite.privVal.GetPubKey() suite.Require().NoError(err) - testClientHeightMinus1 := types.NewHeight(0, babylonChainHeight-1) + testClientHeightMinus1 := clienttypes.NewHeight(0, babylonChainHeight-1) validator := tmtypes.NewValidator(pubKey, 1) suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) diff --git a/x/zoneconcierge/types/errors.go b/x/zoneconcierge/types/errors.go index a9d28f4bd..88e178199 100644 --- a/x/zoneconcierge/types/errors.go +++ b/x/zoneconcierge/types/errors.go @@ -8,20 +8,21 @@ import ( // x/zoneconcierge module sentinel errors var ( - ErrSample = errorsmod.Register(ModuleName, 1100, "sample error") - ErrInvalidPacketTimeout = errorsmod.Register(ModuleName, 1101, "invalid packet timeout") - ErrInvalidVersion = errorsmod.Register(ModuleName, 1102, "invalid version") - ErrHeaderNotFound = errorsmod.Register(ModuleName, 1103, "no header exists at this height") - ErrInvalidHeader = errorsmod.Register(ModuleName, 1104, "input header is invalid") - ErrNoValidAncestorHeader = errorsmod.Register(ModuleName, 1105, "no valid ancestor for this header") - ErrForkNotFound = errorsmod.Register(ModuleName, 1106, "cannot find fork") - ErrInvalidForks = errorsmod.Register(ModuleName, 1107, "input forks is invalid") - ErrChainInfoNotFound = errorsmod.Register(ModuleName, 1108, "no chain info exists") - ErrEpochChainInfoNotFound = errorsmod.Register(ModuleName, 1109, "no chain info exists at this epoch") - ErrEpochHeadersNotFound = errorsmod.Register(ModuleName, 1110, "no timestamped header exists at this epoch") - ErrFinalizedEpochNotFound = errorsmod.Register(ModuleName, 1111, "cannot find a finalized epoch") - ErrInvalidProofEpochSealed = errorsmod.Register(ModuleName, 1112, "invalid ProofEpochSealed") - ErrInvalidMerkleProof = errorsmod.Register(ModuleName, 1113, "invalid Merkle inclusion proof") - ErrInvalidChainInfo = errorsmod.Register(ModuleName, 1114, "invalid chain info") - ErrInvalidChainIDs = errorsmod.Register(ModuleName, 1115, "chain ids contain duplicates or empty strings") + ErrSample = errorsmod.Register(ModuleName, 1100, "sample error") + ErrInvalidPacketTimeout = errorsmod.Register(ModuleName, 1101, "invalid packet timeout") + ErrInvalidVersion = errorsmod.Register(ModuleName, 1102, "invalid version") + ErrHeaderNotFound = errorsmod.Register(ModuleName, 1103, "no header exists at this height") + ErrInvalidHeader = errorsmod.Register(ModuleName, 1104, "input header is invalid") + ErrNoValidAncestorHeader = errorsmod.Register(ModuleName, 1105, "no valid ancestor for this header") + ErrForkNotFound = errorsmod.Register(ModuleName, 1106, "cannot find fork") + ErrInvalidForks = errorsmod.Register(ModuleName, 1107, "input forks is invalid") + ErrChainInfoNotFound = errorsmod.Register(ModuleName, 1108, "no chain info exists") + ErrEpochChainInfoNotFound = errorsmod.Register(ModuleName, 1109, "no chain info exists at this epoch") + ErrEpochHeadersNotFound = errorsmod.Register(ModuleName, 1110, "no timestamped header exists at this epoch") + ErrFinalizedEpochNotFound = errorsmod.Register(ModuleName, 1111, "cannot find a finalized epoch") + ErrInvalidProofEpochSealed = errorsmod.Register(ModuleName, 1112, "invalid ProofEpochSealed") + ErrInvalidMerkleProof = errorsmod.Register(ModuleName, 1113, "invalid Merkle inclusion proof") + ErrInvalidChainInfo = errorsmod.Register(ModuleName, 1114, "invalid chain info") + ErrFinalizingBTCTipNotFound = errorsmod.Register(ModuleName, 1115, "cannot find a finalizing BTC tip") + ErrInvalidChainIDs = errorsmod.Register(ModuleName, 1116, "chain ids contain duplicates or empty strings") ) diff --git a/x/zoneconcierge/types/events_ibc.go b/x/zoneconcierge/types/events_ibc.go index 733bc89fd..fd4e2bba8 100644 --- a/x/zoneconcierge/types/events_ibc.go +++ b/x/zoneconcierge/types/events_ibc.go @@ -2,6 +2,7 @@ package types // IBC events const ( + EventTypeAck = "acknowledgement" EventTypeTimeout = "timeout" AttributeKeyAckSuccess = "success" diff --git a/x/zoneconcierge/types/expected_keepers.go b/x/zoneconcierge/types/expected_keepers.go index 42d9701b1..69d22c16b 100644 --- a/x/zoneconcierge/types/expected_keepers.go +++ b/x/zoneconcierge/types/expected_keepers.go @@ -6,6 +6,7 @@ import ( clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" epochingtypes "github.com/babylonchain/babylon/x/epoching/types" tmcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" @@ -52,6 +53,7 @@ type ChannelKeeper interface { GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) GetAllChannels(ctx sdk.Context) (channels []channeltypes.IdentifiedChannel) + GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, ibcexported.ClientState, error) } // ClientKeeper defines the expected IBC client keeper @@ -77,9 +79,20 @@ type ScopedKeeper interface { ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error } +type BTCLightClientKeeper interface { + GetTipInfo(ctx sdk.Context) *btclctypes.BTCHeaderInfo + GetBaseBTCHeader(ctx sdk.Context) *btclctypes.BTCHeaderInfo + GetHighestCommonAncestor(ctx sdk.Context, header1 *btclctypes.BTCHeaderInfo, header2 *btclctypes.BTCHeaderInfo) *btclctypes.BTCHeaderInfo + GetInOrderAncestorsUntil(ctx sdk.Context, descendant *btclctypes.BTCHeaderInfo, ancestor *btclctypes.BTCHeaderInfo) []*btclctypes.BTCHeaderInfo + GetMainChainUpTo(ctx sdk.Context, depth uint64) []*btclctypes.BTCHeaderInfo +} + type BtcCheckpointKeeper interface { + GetParams(ctx sdk.Context) (p btcctypes.Params) + GetEpochData(ctx sdk.Context, e uint64) *btcctypes.EpochData GetBestSubmission(ctx sdk.Context, e uint64) (btcctypes.BtcStatus, *btcctypes.SubmissionKey, error) GetSubmissionData(ctx sdk.Context, sk btcctypes.SubmissionKey) *btcctypes.SubmissionData + GetEpochBestSubmissionBtcInfo(ctx sdk.Context, ed *btcctypes.EpochData) *btcctypes.SubmissionBtcInfo } type CheckpointingKeeper interface { diff --git a/x/zoneconcierge/types/keys.go b/x/zoneconcierge/types/keys.go index ce3e1c2e3..4968c8956 100644 --- a/x/zoneconcierge/types/keys.go +++ b/x/zoneconcierge/types/keys.go @@ -1,5 +1,9 @@ package types +import ( + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + const ( // ModuleName defines the module name ModuleName = "zoneconcierge" @@ -16,17 +20,22 @@ const ( // Version defines the current version the IBC module supports Version = "zoneconcierge-1" + // Ordering defines the ordering the IBC module supports + Ordering = channeltypes.ORDERED + // PortID is the default port id that module binds to PortID = "zoneconcierge" ) var ( - PortKey = []byte{0x11} // PortKey defines the key to store the port ID in store - ChainInfoKey = []byte{0x12} // ChainInfoKey defines the key to store the chain info for each CZ in store - CanonicalChainKey = []byte{0x13} // CanonicalChainKey defines the key to store the canonical chain for each CZ in store - ForkKey = []byte{0x14} // ForkKey defines the key to store the forks for each CZ in store - EpochChainInfoKey = []byte{0x15} // EpochChainInfoKey defines the key to store each epoch's latests chain info for each CZ in store - FinalizedEpochKey = []byte{0x16} // FinalizedEpochKey defines the key to store the last finalised epoch + PortKey = []byte{0x11} // PortKey defines the key to store the port ID in store + ChainInfoKey = []byte{0x12} // ChainInfoKey defines the key to store the chain info for each CZ in store + CanonicalChainKey = []byte{0x13} // CanonicalChainKey defines the key to store the canonical chain for each CZ in store + ForkKey = []byte{0x14} // ForkKey defines the key to store the forks for each CZ in store + EpochChainInfoKey = []byte{0x15} // EpochChainInfoKey defines the key to store each epoch's latests chain info for each CZ in store + FinalizedEpochKey = []byte{0x16} // FinalizedEpochKey defines the key to store the last finalised epoch + FinalizingBTCTipKey = []byte{0x17} // FinalizingBTCTipKey defines the key to store the BTC tip when the last epoch is finalised + IBCChannelsKey = []byte{0x18} // IBCChannelsKey defines the key to store the initialisation status of IBC channels ) func KeyPrefix(p string) []byte { diff --git a/x/zoneconcierge/types/mocked_keepers.go b/x/zoneconcierge/types/mocked_keepers.go index 1e3dbed9d..8cd850397 100644 --- a/x/zoneconcierge/types/mocked_keepers.go +++ b/x/zoneconcierge/types/mocked_keepers.go @@ -9,17 +9,19 @@ import ( reflect "reflect" types "github.com/babylonchain/babylon/x/btccheckpoint/types" - types0 "github.com/babylonchain/babylon/x/checkpointing/types" - types1 "github.com/babylonchain/babylon/x/epoching/types" - types2 "github.com/cosmos/cosmos-sdk/types" - types3 "github.com/cosmos/cosmos-sdk/x/auth/types" - types4 "github.com/cosmos/cosmos-sdk/x/capability/types" - types5 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - types6 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - exported "github.com/cosmos/ibc-go/v7/modules/core/exported" - gomock "github.com/golang/mock/gomock" + types0 "github.com/babylonchain/babylon/x/btclightclient/types" + types1 "github.com/babylonchain/babylon/x/checkpointing/types" + types2 "github.com/babylonchain/babylon/x/epoching/types" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" coretypes "github.com/cometbft/cometbft/rpc/core/types" + types3 "github.com/cosmos/cosmos-sdk/types" + types4 "github.com/cosmos/cosmos-sdk/x/auth/types" + types5 "github.com/cosmos/cosmos-sdk/x/capability/types" + types6 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + types7 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + types8 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + exported "github.com/cosmos/ibc-go/v7/modules/core/exported" + gomock "github.com/golang/mock/gomock" ) // MockAccountKeeper is a mock of AccountKeeper interface. @@ -46,10 +48,10 @@ func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { } // GetModuleAccount mocks base method. -func (m *MockAccountKeeper) GetModuleAccount(ctx types2.Context, name string) types3.ModuleAccountI { +func (m *MockAccountKeeper) GetModuleAccount(ctx types3.Context, name string) types4.ModuleAccountI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetModuleAccount", ctx, name) - ret0, _ := ret[0].(types3.ModuleAccountI) + ret0, _ := ret[0].(types4.ModuleAccountI) return ret0 } @@ -60,10 +62,10 @@ func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, name interface{}) } // GetModuleAddress mocks base method. -func (m *MockAccountKeeper) GetModuleAddress(name string) types2.AccAddress { +func (m *MockAccountKeeper) GetModuleAddress(name string) types3.AccAddress { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetModuleAddress", name) - ret0, _ := ret[0].(types2.AccAddress) + ret0, _ := ret[0].(types3.AccAddress) return ret0 } @@ -97,7 +99,7 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { } // BlockedAddr mocks base method. -func (m *MockBankKeeper) BlockedAddr(addr types2.AccAddress) bool { +func (m *MockBankKeeper) BlockedAddr(addr types3.AccAddress) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BlockedAddr", addr) ret0, _ := ret[0].(bool) @@ -111,7 +113,7 @@ func (mr *MockBankKeeperMockRecorder) BlockedAddr(addr interface{}) *gomock.Call } // BurnCoins mocks base method. -func (m *MockBankKeeper) BurnCoins(ctx types2.Context, moduleName string, amt types2.Coins) error { +func (m *MockBankKeeper) BurnCoins(ctx types3.Context, moduleName string, amt types3.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BurnCoins", ctx, moduleName, amt) ret0, _ := ret[0].(error) @@ -125,7 +127,7 @@ func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, moduleName, amt interface{} } // MintCoins mocks base method. -func (m *MockBankKeeper) MintCoins(ctx types2.Context, moduleName string, amt types2.Coins) error { +func (m *MockBankKeeper) MintCoins(ctx types3.Context, moduleName string, amt types3.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MintCoins", ctx, moduleName, amt) ret0, _ := ret[0].(error) @@ -139,7 +141,7 @@ func (mr *MockBankKeeperMockRecorder) MintCoins(ctx, moduleName, amt interface{} } // SendCoins mocks base method. -func (m *MockBankKeeper) SendCoins(ctx types2.Context, fromAddr, toAddr types2.AccAddress, amt types2.Coins) error { +func (m *MockBankKeeper) SendCoins(ctx types3.Context, fromAddr, toAddr types3.AccAddress, amt types3.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoins", ctx, fromAddr, toAddr, amt) ret0, _ := ret[0].(error) @@ -153,7 +155,7 @@ func (mr *MockBankKeeperMockRecorder) SendCoins(ctx, fromAddr, toAddr, amt inter } // SendCoinsFromAccountToModule mocks base method. -func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types2.Context, senderAddr types2.AccAddress, recipientModule string, amt types2.Coins) error { +func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types3.Context, senderAddr types3.AccAddress, recipientModule string, amt types3.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) ret0, _ := ret[0].(error) @@ -167,7 +169,7 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAd } // SendCoinsFromModuleToAccount mocks base method. -func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types2.Context, senderModule string, recipientAddr types2.AccAddress, amt types2.Coins) error { +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types3.Context, senderModule string, recipientAddr types3.AccAddress, amt types3.Coins) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) ret0, _ := ret[0].(error) @@ -204,17 +206,18 @@ func (m *MockICS4Wrapper) EXPECT() *MockICS4WrapperMockRecorder { } // SendPacket mocks base method. -func (m *MockICS4Wrapper) SendPacket(ctx types2.Context, channelCap *types4.Capability, packet exported.PacketI) error { +func (m *MockICS4Wrapper) SendPacket(ctx types3.Context, channelCap *types5.Capability, sourcePort, sourceChannel string, timeoutHeight types6.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendPacket", ctx, channelCap, packet) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "SendPacket", ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 } // SendPacket indicates an expected call of SendPacket. -func (mr *MockICS4WrapperMockRecorder) SendPacket(ctx, channelCap, packet interface{}) *gomock.Call { +func (mr *MockICS4WrapperMockRecorder) SendPacket(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPacket", reflect.TypeOf((*MockICS4Wrapper)(nil).SendPacket), ctx, channelCap, packet) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendPacket", reflect.TypeOf((*MockICS4Wrapper)(nil).SendPacket), ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) } // MockChannelKeeper is a mock of ChannelKeeper interface. @@ -241,10 +244,10 @@ func (m *MockChannelKeeper) EXPECT() *MockChannelKeeperMockRecorder { } // GetAllChannels mocks base method. -func (m *MockChannelKeeper) GetAllChannels(ctx types2.Context) []types6.IdentifiedChannel { +func (m *MockChannelKeeper) GetAllChannels(ctx types3.Context) []types8.IdentifiedChannel { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAllChannels", ctx) - ret0, _ := ret[0].([]types6.IdentifiedChannel) + ret0, _ := ret[0].([]types8.IdentifiedChannel) return ret0 } @@ -255,10 +258,10 @@ func (mr *MockChannelKeeperMockRecorder) GetAllChannels(ctx interface{}) *gomock } // GetChannel mocks base method. -func (m *MockChannelKeeper) GetChannel(ctx types2.Context, srcPort, srcChan string) (types6.Channel, bool) { +func (m *MockChannelKeeper) GetChannel(ctx types3.Context, srcPort, srcChan string) (types8.Channel, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChannel", ctx, srcPort, srcChan) - ret0, _ := ret[0].(types6.Channel) + ret0, _ := ret[0].(types8.Channel) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -269,8 +272,24 @@ func (mr *MockChannelKeeperMockRecorder) GetChannel(ctx, srcPort, srcChan interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannel", reflect.TypeOf((*MockChannelKeeper)(nil).GetChannel), ctx, srcPort, srcChan) } +// GetChannelClientState mocks base method. +func (m *MockChannelKeeper) GetChannelClientState(ctx types3.Context, portID, channelID string) (string, exported.ClientState, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetChannelClientState", ctx, portID, channelID) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(exported.ClientState) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetChannelClientState indicates an expected call of GetChannelClientState. +func (mr *MockChannelKeeperMockRecorder) GetChannelClientState(ctx, portID, channelID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannelClientState", reflect.TypeOf((*MockChannelKeeper)(nil).GetChannelClientState), ctx, portID, channelID) +} + // GetNextSequenceSend mocks base method. -func (m *MockChannelKeeper) GetNextSequenceSend(ctx types2.Context, portID, channelID string) (uint64, bool) { +func (m *MockChannelKeeper) GetNextSequenceSend(ctx types3.Context, portID, channelID string) (uint64, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetNextSequenceSend", ctx, portID, channelID) ret0, _ := ret[0].(uint64) @@ -308,7 +327,7 @@ func (m *MockClientKeeper) EXPECT() *MockClientKeeperMockRecorder { } // GetClientConsensusState mocks base method. -func (m *MockClientKeeper) GetClientConsensusState(ctx types2.Context, clientID string) (exported.ConsensusState, bool) { +func (m *MockClientKeeper) GetClientConsensusState(ctx types3.Context, clientID string) (exported.ConsensusState, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetClientConsensusState", ctx, clientID) ret0, _ := ret[0].(exported.ConsensusState) @@ -346,10 +365,10 @@ func (m *MockConnectionKeeper) EXPECT() *MockConnectionKeeperMockRecorder { } // GetConnection mocks base method. -func (m *MockConnectionKeeper) GetConnection(ctx types2.Context, connectionID string) (types5.ConnectionEnd, bool) { +func (m *MockConnectionKeeper) GetConnection(ctx types3.Context, connectionID string) (types7.ConnectionEnd, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConnection", ctx, connectionID) - ret0, _ := ret[0].(types5.ConnectionEnd) + ret0, _ := ret[0].(types7.ConnectionEnd) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -384,10 +403,10 @@ func (m *MockPortKeeper) EXPECT() *MockPortKeeperMockRecorder { } // BindPort mocks base method. -func (m *MockPortKeeper) BindPort(ctx types2.Context, portID string) *types4.Capability { +func (m *MockPortKeeper) BindPort(ctx types3.Context, portID string) *types5.Capability { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BindPort", ctx, portID) - ret0, _ := ret[0].(*types4.Capability) + ret0, _ := ret[0].(*types5.Capability) return ret0 } @@ -421,7 +440,7 @@ func (m *MockScopedKeeper) EXPECT() *MockScopedKeeperMockRecorder { } // AuthenticateCapability mocks base method. -func (m *MockScopedKeeper) AuthenticateCapability(ctx types2.Context, cap *types4.Capability, name string) bool { +func (m *MockScopedKeeper) AuthenticateCapability(ctx types3.Context, cap *types5.Capability, name string) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AuthenticateCapability", ctx, cap, name) ret0, _ := ret[0].(bool) @@ -435,7 +454,7 @@ func (mr *MockScopedKeeperMockRecorder) AuthenticateCapability(ctx, cap, name in } // ClaimCapability mocks base method. -func (m *MockScopedKeeper) ClaimCapability(ctx types2.Context, cap *types4.Capability, name string) error { +func (m *MockScopedKeeper) ClaimCapability(ctx types3.Context, cap *types5.Capability, name string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ClaimCapability", ctx, cap, name) ret0, _ := ret[0].(error) @@ -449,10 +468,10 @@ func (mr *MockScopedKeeperMockRecorder) ClaimCapability(ctx, cap, name interface } // GetCapability mocks base method. -func (m *MockScopedKeeper) GetCapability(ctx types2.Context, name string) (*types4.Capability, bool) { +func (m *MockScopedKeeper) GetCapability(ctx types3.Context, name string) (*types5.Capability, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCapability", ctx, name) - ret0, _ := ret[0].(*types4.Capability) + ret0, _ := ret[0].(*types5.Capability) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -464,11 +483,11 @@ func (mr *MockScopedKeeperMockRecorder) GetCapability(ctx, name interface{}) *go } // LookupModules mocks base method. -func (m *MockScopedKeeper) LookupModules(ctx types2.Context, name string) ([]string, *types4.Capability, error) { +func (m *MockScopedKeeper) LookupModules(ctx types3.Context, name string) ([]string, *types5.Capability, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupModules", ctx, name) ret0, _ := ret[0].([]string) - ret1, _ := ret[1].(*types4.Capability) + ret1, _ := ret[1].(*types5.Capability) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } @@ -479,6 +498,99 @@ func (mr *MockScopedKeeperMockRecorder) LookupModules(ctx, name interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupModules", reflect.TypeOf((*MockScopedKeeper)(nil).LookupModules), ctx, name) } +// MockBTCLightClientKeeper is a mock of BTCLightClientKeeper interface. +type MockBTCLightClientKeeper struct { + ctrl *gomock.Controller + recorder *MockBTCLightClientKeeperMockRecorder +} + +// MockBTCLightClientKeeperMockRecorder is the mock recorder for MockBTCLightClientKeeper. +type MockBTCLightClientKeeperMockRecorder struct { + mock *MockBTCLightClientKeeper +} + +// NewMockBTCLightClientKeeper creates a new mock instance. +func NewMockBTCLightClientKeeper(ctrl *gomock.Controller) *MockBTCLightClientKeeper { + mock := &MockBTCLightClientKeeper{ctrl: ctrl} + mock.recorder = &MockBTCLightClientKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBTCLightClientKeeper) EXPECT() *MockBTCLightClientKeeperMockRecorder { + return m.recorder +} + +// GetBaseBTCHeader mocks base method. +func (m *MockBTCLightClientKeeper) GetBaseBTCHeader(ctx types3.Context) *types0.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBaseBTCHeader", ctx) + ret0, _ := ret[0].(*types0.BTCHeaderInfo) + return ret0 +} + +// GetBaseBTCHeader indicates an expected call of GetBaseBTCHeader. +func (mr *MockBTCLightClientKeeperMockRecorder) GetBaseBTCHeader(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseBTCHeader", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetBaseBTCHeader), ctx) +} + +// GetHighestCommonAncestor mocks base method. +func (m *MockBTCLightClientKeeper) GetHighestCommonAncestor(ctx types3.Context, header1, header2 *types0.BTCHeaderInfo) *types0.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHighestCommonAncestor", ctx, header1, header2) + ret0, _ := ret[0].(*types0.BTCHeaderInfo) + return ret0 +} + +// GetHighestCommonAncestor indicates an expected call of GetHighestCommonAncestor. +func (mr *MockBTCLightClientKeeperMockRecorder) GetHighestCommonAncestor(ctx, header1, header2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHighestCommonAncestor", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetHighestCommonAncestor), ctx, header1, header2) +} + +// GetInOrderAncestorsUntil mocks base method. +func (m *MockBTCLightClientKeeper) GetInOrderAncestorsUntil(ctx types3.Context, descendant, ancestor *types0.BTCHeaderInfo) []*types0.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetInOrderAncestorsUntil", ctx, descendant, ancestor) + ret0, _ := ret[0].([]*types0.BTCHeaderInfo) + return ret0 +} + +// GetInOrderAncestorsUntil indicates an expected call of GetInOrderAncestorsUntil. +func (mr *MockBTCLightClientKeeperMockRecorder) GetInOrderAncestorsUntil(ctx, descendant, ancestor interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInOrderAncestorsUntil", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetInOrderAncestorsUntil), ctx, descendant, ancestor) +} + +// GetMainChainUpTo mocks base method. +func (m *MockBTCLightClientKeeper) GetMainChainUpTo(ctx types3.Context, depth uint64) []*types0.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMainChainUpTo", ctx, depth) + ret0, _ := ret[0].([]*types0.BTCHeaderInfo) + return ret0 +} + +// GetMainChainUpTo indicates an expected call of GetMainChainUpTo. +func (mr *MockBTCLightClientKeeperMockRecorder) GetMainChainUpTo(ctx, depth interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMainChainUpTo", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetMainChainUpTo), ctx, depth) +} + +// GetTipInfo mocks base method. +func (m *MockBTCLightClientKeeper) GetTipInfo(ctx types3.Context) *types0.BTCHeaderInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTipInfo", ctx) + ret0, _ := ret[0].(*types0.BTCHeaderInfo) + return ret0 +} + +// GetTipInfo indicates an expected call of GetTipInfo. +func (mr *MockBTCLightClientKeeperMockRecorder) GetTipInfo(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTipInfo", reflect.TypeOf((*MockBTCLightClientKeeper)(nil).GetTipInfo), ctx) +} + // MockBtcCheckpointKeeper is a mock of BtcCheckpointKeeper interface. type MockBtcCheckpointKeeper struct { ctrl *gomock.Controller @@ -503,7 +615,7 @@ func (m *MockBtcCheckpointKeeper) EXPECT() *MockBtcCheckpointKeeperMockRecorder } // GetBestSubmission mocks base method. -func (m *MockBtcCheckpointKeeper) GetBestSubmission(ctx types2.Context, e uint64) (types.BtcStatus, *types.SubmissionKey, error) { +func (m *MockBtcCheckpointKeeper) GetBestSubmission(ctx types3.Context, e uint64) (types.BtcStatus, *types.SubmissionKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBestSubmission", ctx, e) ret0, _ := ret[0].(types.BtcStatus) @@ -518,8 +630,50 @@ func (mr *MockBtcCheckpointKeeperMockRecorder) GetBestSubmission(ctx, e interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBestSubmission", reflect.TypeOf((*MockBtcCheckpointKeeper)(nil).GetBestSubmission), ctx, e) } +// GetEpochBestSubmissionBtcInfo mocks base method. +func (m *MockBtcCheckpointKeeper) GetEpochBestSubmissionBtcInfo(ctx types3.Context, ed *types.EpochData) *types.SubmissionBtcInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochBestSubmissionBtcInfo", ctx, ed) + ret0, _ := ret[0].(*types.SubmissionBtcInfo) + return ret0 +} + +// GetEpochBestSubmissionBtcInfo indicates an expected call of GetEpochBestSubmissionBtcInfo. +func (mr *MockBtcCheckpointKeeperMockRecorder) GetEpochBestSubmissionBtcInfo(ctx, ed interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochBestSubmissionBtcInfo", reflect.TypeOf((*MockBtcCheckpointKeeper)(nil).GetEpochBestSubmissionBtcInfo), ctx, ed) +} + +// GetEpochData mocks base method. +func (m *MockBtcCheckpointKeeper) GetEpochData(ctx types3.Context, e uint64) *types.EpochData { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochData", ctx, e) + ret0, _ := ret[0].(*types.EpochData) + return ret0 +} + +// GetEpochData indicates an expected call of GetEpochData. +func (mr *MockBtcCheckpointKeeperMockRecorder) GetEpochData(ctx, e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochData", reflect.TypeOf((*MockBtcCheckpointKeeper)(nil).GetEpochData), ctx, e) +} + +// GetParams mocks base method. +func (m *MockBtcCheckpointKeeper) GetParams(ctx types3.Context) types.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetParams", ctx) + ret0, _ := ret[0].(types.Params) + return ret0 +} + +// GetParams indicates an expected call of GetParams. +func (mr *MockBtcCheckpointKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockBtcCheckpointKeeper)(nil).GetParams), ctx) +} + // GetSubmissionData mocks base method. -func (m *MockBtcCheckpointKeeper) GetSubmissionData(ctx types2.Context, sk types.SubmissionKey) *types.SubmissionData { +func (m *MockBtcCheckpointKeeper) GetSubmissionData(ctx types3.Context, sk types.SubmissionKey) *types.SubmissionData { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSubmissionData", ctx, sk) ret0, _ := ret[0].(*types.SubmissionData) @@ -556,10 +710,10 @@ func (m *MockCheckpointingKeeper) EXPECT() *MockCheckpointingKeeperMockRecorder } // GetBLSPubKeySet mocks base method. -func (m *MockCheckpointingKeeper) GetBLSPubKeySet(ctx types2.Context, epochNumber uint64) ([]*types0.ValidatorWithBlsKey, error) { +func (m *MockCheckpointingKeeper) GetBLSPubKeySet(ctx types3.Context, epochNumber uint64) ([]*types1.ValidatorWithBlsKey, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetBLSPubKeySet", ctx, epochNumber) - ret0, _ := ret[0].([]*types0.ValidatorWithBlsKey) + ret0, _ := ret[0].([]*types1.ValidatorWithBlsKey) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -571,10 +725,10 @@ func (mr *MockCheckpointingKeeperMockRecorder) GetBLSPubKeySet(ctx, epochNumber } // GetRawCheckpoint mocks base method. -func (m *MockCheckpointingKeeper) GetRawCheckpoint(ctx types2.Context, epochNumber uint64) (*types0.RawCheckpointWithMeta, error) { +func (m *MockCheckpointingKeeper) GetRawCheckpoint(ctx types3.Context, epochNumber uint64) (*types1.RawCheckpointWithMeta, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRawCheckpoint", ctx, epochNumber) - ret0, _ := ret[0].(*types0.RawCheckpointWithMeta) + ret0, _ := ret[0].(*types1.RawCheckpointWithMeta) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -609,10 +763,10 @@ func (m *MockEpochingKeeper) EXPECT() *MockEpochingKeeperMockRecorder { } // GetEpoch mocks base method. -func (m *MockEpochingKeeper) GetEpoch(ctx types2.Context) *types1.Epoch { +func (m *MockEpochingKeeper) GetEpoch(ctx types3.Context) *types2.Epoch { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEpoch", ctx) - ret0, _ := ret[0].(*types1.Epoch) + ret0, _ := ret[0].(*types2.Epoch) return ret0 } @@ -623,10 +777,10 @@ func (mr *MockEpochingKeeperMockRecorder) GetEpoch(ctx interface{}) *gomock.Call } // GetHistoricalEpoch mocks base method. -func (m *MockEpochingKeeper) GetHistoricalEpoch(ctx types2.Context, epochNumber uint64) (*types1.Epoch, error) { +func (m *MockEpochingKeeper) GetHistoricalEpoch(ctx types3.Context, epochNumber uint64) (*types2.Epoch, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHistoricalEpoch", ctx, epochNumber) - ret0, _ := ret[0].(*types1.Epoch) + ret0, _ := ret[0].(*types2.Epoch) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -638,7 +792,7 @@ func (mr *MockEpochingKeeperMockRecorder) GetHistoricalEpoch(ctx, epochNumber in } // ProveAppHashInEpoch mocks base method. -func (m *MockEpochingKeeper) ProveAppHashInEpoch(ctx types2.Context, height, epochNumber uint64) (*crypto.Proof, error) { +func (m *MockEpochingKeeper) ProveAppHashInEpoch(ctx types3.Context, height, epochNumber uint64) (*crypto.Proof, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ProveAppHashInEpoch", ctx, height, epochNumber) ret0, _ := ret[0].(*crypto.Proof) diff --git a/x/zoneconcierge/types/packet.pb.go b/x/zoneconcierge/types/packet.pb.go index 1f2bb92a2..56a5e3f68 100644 --- a/x/zoneconcierge/types/packet.pb.go +++ b/x/zoneconcierge/types/packet.pb.go @@ -5,6 +5,10 @@ package types import ( fmt "fmt" + types3 "github.com/babylonchain/babylon/x/btccheckpoint/types" + types "github.com/babylonchain/babylon/x/btclightclient/types" + types2 "github.com/babylonchain/babylon/x/checkpointing/types" + types1 "github.com/babylonchain/babylon/x/epoching/types" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -28,8 +32,7 @@ type ZoneconciergePacketData struct { // packet is the actual message carried in the IBC packet // // Types that are valid to be assigned to Packet: - // - // *ZoneconciergePacketData_Heartbeart + // *ZoneconciergePacketData_BtcTimestamp Packet isZoneconciergePacketData_Packet `protobuf_oneof:"packet"` } @@ -72,11 +75,11 @@ type isZoneconciergePacketData_Packet interface { Size() int } -type ZoneconciergePacketData_Heartbeart struct { - Heartbeart *Heartbeat `protobuf:"bytes,1,opt,name=heartbeart,proto3,oneof" json:"heartbeart,omitempty"` +type ZoneconciergePacketData_BtcTimestamp struct { + BtcTimestamp *BTCTimestamp `protobuf:"bytes,1,opt,name=btc_timestamp,json=btcTimestamp,proto3,oneof" json:"btc_timestamp,omitempty"` } -func (*ZoneconciergePacketData_Heartbeart) isZoneconciergePacketData_Packet() {} +func (*ZoneconciergePacketData_BtcTimestamp) isZoneconciergePacketData_Packet() {} func (m *ZoneconciergePacketData) GetPacket() isZoneconciergePacketData_Packet { if m != nil { @@ -85,9 +88,9 @@ func (m *ZoneconciergePacketData) GetPacket() isZoneconciergePacketData_Packet { return nil } -func (m *ZoneconciergePacketData) GetHeartbeart() *Heartbeat { - if x, ok := m.GetPacket().(*ZoneconciergePacketData_Heartbeart); ok { - return x.Heartbeart +func (m *ZoneconciergePacketData) GetBtcTimestamp() *BTCTimestamp { + if x, ok := m.GetPacket().(*ZoneconciergePacketData_BtcTimestamp); ok { + return x.BtcTimestamp } return nil } @@ -95,28 +98,39 @@ func (m *ZoneconciergePacketData) GetHeartbeart() *Heartbeat { // XXX_OneofWrappers is for the internal use of the proto package. func (*ZoneconciergePacketData) XXX_OneofWrappers() []interface{} { return []interface{}{ - (*ZoneconciergePacketData_Heartbeart)(nil), + (*ZoneconciergePacketData_BtcTimestamp)(nil), } } -// Heartbeat is a heartbeat message that can be carried in IBC packets of -// ZoneConcierge -type Heartbeat struct { - Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` +// BTCTimestamp is a BTC timestamp +type BTCTimestamp struct { + // header is the last CZ header in the finalized Babylon epoch + Header *IndexedHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` + // btc_headers is BTC headers from the BTC header tip upon the last finalised epoch to the current tip + BtcHeaders []*types.BTCHeaderInfo `protobuf:"bytes,2,rep,name=btc_headers,json=btcHeaders,proto3" json:"btc_headers,omitempty"` + // epoch_info is the metadata of the sealed epoch + EpochInfo *types1.Epoch `protobuf:"bytes,3,opt,name=epoch_info,json=epochInfo,proto3" json:"epoch_info,omitempty"` + // raw_checkpoint is the raw checkpoint that seals this epoch + RawCheckpoint *types2.RawCheckpoint `protobuf:"bytes,4,opt,name=raw_checkpoint,json=rawCheckpoint,proto3" json:"raw_checkpoint,omitempty"` + // btc_submission_key is position of two BTC txs that include the raw checkpoint of this epoch + BtcSubmissionKey *types3.SubmissionKey `protobuf:"bytes,5,opt,name=btc_submission_key,json=btcSubmissionKey,proto3" json:"btc_submission_key,omitempty"` + // + // Proofs that the header is finalized + Proof *ProofFinalizedChainInfo `protobuf:"bytes,6,opt,name=proof,proto3" json:"proof,omitempty"` } -func (m *Heartbeat) Reset() { *m = Heartbeat{} } -func (m *Heartbeat) String() string { return proto.CompactTextString(m) } -func (*Heartbeat) ProtoMessage() {} -func (*Heartbeat) Descriptor() ([]byte, []int) { +func (m *BTCTimestamp) Reset() { *m = BTCTimestamp{} } +func (m *BTCTimestamp) String() string { return proto.CompactTextString(m) } +func (*BTCTimestamp) ProtoMessage() {} +func (*BTCTimestamp) Descriptor() ([]byte, []int) { return fileDescriptor_be12e124c5c4fdb9, []int{1} } -func (m *Heartbeat) XXX_Unmarshal(b []byte) error { +func (m *BTCTimestamp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *Heartbeat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *BTCTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_Heartbeat.Marshal(b, m, deterministic) + return xxx_messageInfo_BTCTimestamp.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -126,28 +140,63 @@ func (m *Heartbeat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (m *Heartbeat) XXX_Merge(src proto.Message) { - xxx_messageInfo_Heartbeat.Merge(m, src) +func (m *BTCTimestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCTimestamp.Merge(m, src) } -func (m *Heartbeat) XXX_Size() int { +func (m *BTCTimestamp) XXX_Size() int { return m.Size() } -func (m *Heartbeat) XXX_DiscardUnknown() { - xxx_messageInfo_Heartbeat.DiscardUnknown(m) +func (m *BTCTimestamp) XXX_DiscardUnknown() { + xxx_messageInfo_BTCTimestamp.DiscardUnknown(m) +} + +var xxx_messageInfo_BTCTimestamp proto.InternalMessageInfo + +func (m *BTCTimestamp) GetHeader() *IndexedHeader { + if m != nil { + return m.Header + } + return nil +} + +func (m *BTCTimestamp) GetBtcHeaders() []*types.BTCHeaderInfo { + if m != nil { + return m.BtcHeaders + } + return nil +} + +func (m *BTCTimestamp) GetEpochInfo() *types1.Epoch { + if m != nil { + return m.EpochInfo + } + return nil +} + +func (m *BTCTimestamp) GetRawCheckpoint() *types2.RawCheckpoint { + if m != nil { + return m.RawCheckpoint + } + return nil } -var xxx_messageInfo_Heartbeat proto.InternalMessageInfo +func (m *BTCTimestamp) GetBtcSubmissionKey() *types3.SubmissionKey { + if m != nil { + return m.BtcSubmissionKey + } + return nil +} -func (m *Heartbeat) GetMsg() string { +func (m *BTCTimestamp) GetProof() *ProofFinalizedChainInfo { if m != nil { - return m.Msg + return m.Proof } - return "" + return nil } func init() { proto.RegisterType((*ZoneconciergePacketData)(nil), "babylon.zoneconcierge.v1.ZoneconciergePacketData") - proto.RegisterType((*Heartbeat)(nil), "babylon.zoneconcierge.v1.Heartbeat") + proto.RegisterType((*BTCTimestamp)(nil), "babylon.zoneconcierge.v1.BTCTimestamp") } func init() { @@ -155,21 +204,37 @@ func init() { } var fileDescriptor_be12e124c5c4fdb9 = []byte{ - // 209 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0x4a, 0x4c, 0xaa, - 0xcc, 0xc9, 0xcf, 0xd3, 0xaf, 0xca, 0xcf, 0x4b, 0x4d, 0xce, 0xcf, 0x4b, 0xce, 0x4c, 0x2d, 0x4a, - 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x4c, 0xce, 0x4e, 0x2d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, - 0xc9, 0x17, 0x92, 0x80, 0x2a, 0xd3, 0x43, 0x51, 0xa6, 0x57, 0x66, 0xa8, 0x94, 0xc5, 0x25, 0x1e, - 0x85, 0x2c, 0x16, 0x00, 0xd6, 0xe6, 0x92, 0x58, 0x92, 0x28, 0xe4, 0xca, 0xc5, 0x95, 0x91, 0x9a, - 0x58, 0x54, 0x92, 0x04, 0x22, 0x24, 0x18, 0x15, 0x18, 0x35, 0xb8, 0x8d, 0x94, 0xf5, 0x70, 0x99, - 0xa4, 0xe7, 0x01, 0x55, 0x5b, 0xe2, 0xc1, 0x10, 0x84, 0xa4, 0xd1, 0x89, 0x83, 0x8b, 0x0d, 0xe2, - 0x16, 0x25, 0x59, 0x2e, 0x4e, 0xb8, 0x22, 0x21, 0x01, 0x2e, 0xe6, 0xdc, 0xe2, 0x74, 0xb0, 0xb1, - 0x9c, 0x41, 0x20, 0xa6, 0x93, 0xff, 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, - 0x99, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0xed, 0x4f, 0xce, - 0x48, 0xcc, 0xcc, 0x83, 0x71, 0xf4, 0x2b, 0xd0, 0xfc, 0x5f, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, - 0x06, 0xf6, 0xbc, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xe0, 0xc6, 0x63, 0x3e, 0x25, 0x01, 0x00, - 0x00, + // 471 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0x1b, 0xca, 0x2a, 0x70, 0x37, 0x84, 0x7c, 0x21, 0xda, 0x21, 0x9a, 0x2a, 0x01, 0x45, + 0x9a, 0x1c, 0x65, 0x88, 0x03, 0x27, 0xa4, 0x96, 0x3f, 0xab, 0x10, 0x30, 0x85, 0x71, 0xd9, 0xa5, + 0xb2, 0xdd, 0xb7, 0x8d, 0xd5, 0xd6, 0x8e, 0x12, 0xaf, 0x5b, 0xf7, 0x29, 0xf8, 0x52, 0x48, 0x1c, + 0x77, 0xe4, 0x88, 0xda, 0x2f, 0x82, 0xec, 0xfc, 0x69, 0x52, 0x94, 0x4b, 0xe4, 0xf7, 0xc9, 0xcf, + 0x8f, 0xfd, 0x3e, 0x7e, 0xd1, 0x73, 0x46, 0xd9, 0x7a, 0xa1, 0xa4, 0x7f, 0xa7, 0x24, 0x70, 0x25, + 0xb9, 0x80, 0x64, 0x06, 0xfe, 0x2a, 0xf0, 0x63, 0xca, 0xe7, 0xa0, 0x49, 0x9c, 0x28, 0xad, 0xb0, + 0x9b, 0x63, 0xa4, 0x86, 0x91, 0x55, 0x70, 0x7c, 0x5a, 0x18, 0x30, 0xcd, 0x79, 0x04, 0x7c, 0x1e, + 0x2b, 0x21, 0xb5, 0x31, 0xa8, 0x09, 0x99, 0xcf, 0xf1, 0xab, 0x82, 0xde, 0xfd, 0x11, 0x72, 0x66, + 0xe8, 0xff, 0x50, 0x52, 0x31, 0x5e, 0x88, 0x59, 0x64, 0xbe, 0x50, 0x3a, 0x57, 0x94, 0x9c, 0xef, + 0x15, 0x3c, 0xc4, 0x8a, 0x47, 0xb9, 0x6b, 0xb1, 0xce, 0x99, 0xd3, 0xc6, 0x6e, 0xeb, 0x7d, 0x59, + 0xba, 0x97, 0xa0, 0x67, 0x57, 0x55, 0xf9, 0xc2, 0x26, 0xf2, 0x9e, 0x6a, 0x8a, 0xbf, 0xa0, 0x23, + 0xa6, 0xf9, 0x58, 0x8b, 0x25, 0xa4, 0x9a, 0x2e, 0x63, 0xd7, 0x39, 0x71, 0xfa, 0xdd, 0xb3, 0x17, + 0xa4, 0x29, 0x27, 0x32, 0xb8, 0x1c, 0x5e, 0x16, 0xf4, 0x79, 0x2b, 0x3c, 0x64, 0x9a, 0x97, 0xf5, + 0xe0, 0x11, 0xea, 0x64, 0x71, 0xf7, 0x7e, 0xb5, 0xd1, 0x61, 0x15, 0xc5, 0xef, 0x50, 0x27, 0x02, + 0x3a, 0x81, 0x24, 0x3f, 0xe2, 0x65, 0xf3, 0x11, 0x23, 0x39, 0x81, 0x5b, 0x98, 0x9c, 0x5b, 0x3c, + 0xcc, 0xb7, 0xe1, 0x11, 0xea, 0x9a, 0xab, 0x66, 0x55, 0xea, 0x3e, 0x38, 0x69, 0xf7, 0xbb, 0x67, + 0xfd, 0xd2, 0x65, 0x2f, 0xcb, 0xec, 0xa6, 0x99, 0xc5, 0x48, 0x4e, 0x55, 0x88, 0x98, 0xe6, 0x59, + 0x99, 0xe2, 0xb7, 0x08, 0xd9, 0x40, 0xc7, 0x42, 0x4e, 0x95, 0xdb, 0xb6, 0xf7, 0x29, 0xdf, 0x89, + 0x94, 0x59, 0xaf, 0x02, 0xf2, 0xc1, 0xac, 0xc3, 0xc7, 0x56, 0x32, 0x36, 0xf8, 0x2b, 0x7a, 0x92, + 0xd0, 0x9b, 0xf1, 0xee, 0x95, 0xdd, 0x87, 0x7b, 0xed, 0xd4, 0x26, 0xc2, 0x78, 0x84, 0xf4, 0x66, + 0x58, 0x6a, 0xe1, 0x51, 0x52, 0x2d, 0xf1, 0x0f, 0x84, 0x4d, 0x57, 0xe9, 0x35, 0x5b, 0x8a, 0x34, + 0x15, 0x4a, 0x8e, 0xe7, 0xb0, 0x76, 0x0f, 0xf6, 0x3c, 0xeb, 0x23, 0xb8, 0x0a, 0xc8, 0xf7, 0x92, + 0xff, 0x0c, 0xeb, 0xf0, 0x29, 0xd3, 0xbc, 0xa6, 0xe0, 0x4f, 0xe8, 0x20, 0x4e, 0x94, 0x9a, 0xba, + 0x1d, 0xeb, 0x14, 0x34, 0x87, 0x7d, 0x61, 0xb0, 0x8f, 0x42, 0xd2, 0x85, 0xb8, 0x83, 0xc9, 0x30, + 0xa2, 0x42, 0xda, 0xbc, 0xb2, 0xfd, 0x83, 0x6f, 0xbf, 0x37, 0x9e, 0x73, 0xbf, 0xf1, 0x9c, 0xbf, + 0x1b, 0xcf, 0xf9, 0xb9, 0xf5, 0x5a, 0xf7, 0x5b, 0xaf, 0xf5, 0x67, 0xeb, 0xb5, 0xae, 0xde, 0xcc, + 0x84, 0x8e, 0xae, 0x19, 0xe1, 0x6a, 0xe9, 0xe7, 0xee, 0xdc, 0xec, 0x2e, 0x0a, 0xff, 0x76, 0x6f, + 0x3a, 0xf5, 0x3a, 0x86, 0x94, 0x75, 0xec, 0x4c, 0xbe, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x31, + 0xea, 0x29, 0xbc, 0xb1, 0x03, 0x00, 0x00, } func (m *ZoneconciergePacketData) Marshal() (dAtA []byte, err error) { @@ -204,16 +269,16 @@ func (m *ZoneconciergePacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *ZoneconciergePacketData_Heartbeart) MarshalTo(dAtA []byte) (int, error) { +func (m *ZoneconciergePacketData_BtcTimestamp) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ZoneconciergePacketData_Heartbeart) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ZoneconciergePacketData_BtcTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.Heartbeart != nil { + if m.BtcTimestamp != nil { { - size, err := m.Heartbeart.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.BtcTimestamp.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -225,7 +290,7 @@ func (m *ZoneconciergePacketData_Heartbeart) MarshalToSizedBuffer(dAtA []byte) ( } return len(dAtA) - i, nil } -func (m *Heartbeat) Marshal() (dAtA []byte, err error) { +func (m *BTCTimestamp) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -235,20 +300,87 @@ func (m *Heartbeat) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *Heartbeat) MarshalTo(dAtA []byte) (int, error) { +func (m *BTCTimestamp) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *Heartbeat) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *BTCTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Msg) > 0 { - i -= len(m.Msg) - copy(dAtA[i:], m.Msg) - i = encodeVarintPacket(dAtA, i, uint64(len(m.Msg))) + if m.Proof != nil { + { + size, err := m.Proof.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.BtcSubmissionKey != nil { + { + size, err := m.BtcSubmissionKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.RawCheckpoint != nil { + { + size, err := m.RawCheckpoint.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.EpochInfo != nil { + { + size, err := m.EpochInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.BtcHeaders) > 0 { + for iNdEx := len(m.BtcHeaders) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BtcHeaders[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Header != nil { + { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } i-- dAtA[i] = 0xa } @@ -278,26 +410,48 @@ func (m *ZoneconciergePacketData) Size() (n int) { return n } -func (m *ZoneconciergePacketData_Heartbeart) Size() (n int) { +func (m *ZoneconciergePacketData_BtcTimestamp) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.Heartbeart != nil { - l = m.Heartbeart.Size() + if m.BtcTimestamp != nil { + l = m.BtcTimestamp.Size() n += 1 + l + sovPacket(uint64(l)) } return n } -func (m *Heartbeat) Size() (n int) { +func (m *BTCTimestamp) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Msg) - if l > 0 { + if m.Header != nil { + l = m.Header.Size() + n += 1 + l + sovPacket(uint64(l)) + } + if len(m.BtcHeaders) > 0 { + for _, e := range m.BtcHeaders { + l = e.Size() + n += 1 + l + sovPacket(uint64(l)) + } + } + if m.EpochInfo != nil { + l = m.EpochInfo.Size() + n += 1 + l + sovPacket(uint64(l)) + } + if m.RawCheckpoint != nil { + l = m.RawCheckpoint.Size() + n += 1 + l + sovPacket(uint64(l)) + } + if m.BtcSubmissionKey != nil { + l = m.BtcSubmissionKey.Size() + n += 1 + l + sovPacket(uint64(l)) + } + if m.Proof != nil { + l = m.Proof.Size() n += 1 + l + sovPacket(uint64(l)) } return n @@ -340,7 +494,7 @@ func (m *ZoneconciergePacketData) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Heartbeart", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BtcTimestamp", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -367,11 +521,11 @@ func (m *ZoneconciergePacketData) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &Heartbeat{} + v := &BTCTimestamp{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Packet = &ZoneconciergePacketData_Heartbeart{v} + m.Packet = &ZoneconciergePacketData_BtcTimestamp{v} iNdEx = postIndex default: iNdEx = preIndex @@ -394,7 +548,7 @@ func (m *ZoneconciergePacketData) Unmarshal(dAtA []byte) error { } return nil } -func (m *Heartbeat) Unmarshal(dAtA []byte) error { +func (m *BTCTimestamp) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -417,17 +571,17 @@ func (m *Heartbeat) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Heartbeat: wiretype end group for non-group") + return fmt.Errorf("proto: BTCTimestamp: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Heartbeat: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: BTCTimestamp: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowPacket @@ -437,23 +591,205 @@ func (m *Heartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthPacket } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthPacket } if postIndex > l { return io.ErrUnexpectedEOF } - m.Msg = string(dAtA[iNdEx:postIndex]) + if m.Header == nil { + m.Header = &IndexedHeader{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcHeaders", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BtcHeaders = append(m.BtcHeaders, &types.BTCHeaderInfo{}) + if err := m.BtcHeaders[len(m.BtcHeaders)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EpochInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EpochInfo == nil { + m.EpochInfo = &types1.Epoch{} + } + if err := m.EpochInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RawCheckpoint", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RawCheckpoint == nil { + m.RawCheckpoint = &types2.RawCheckpoint{} + } + if err := m.RawCheckpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BtcSubmissionKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BtcSubmissionKey == nil { + m.BtcSubmissionKey = &types3.SubmissionKey{} + } + if err := m.BtcSubmissionKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Proof == nil { + m.Proof = &ProofFinalizedChainInfo{} + } + if err := m.Proof.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex diff --git a/x/zoneconcierge/types/zoneconcierge.go b/x/zoneconcierge/types/zoneconcierge.go index be4071344..230715dd0 100644 --- a/x/zoneconcierge/types/zoneconcierge.go +++ b/x/zoneconcierge/types/zoneconcierge.go @@ -91,3 +91,11 @@ func (ci *ChainInfo) ValidateBasic() error { return nil } + +func NewBTCTimestampPacketData(btcTimestamp *BTCTimestamp) *ZoneconciergePacketData { + return &ZoneconciergePacketData{ + Packet: &ZoneconciergePacketData_BtcTimestamp{ + BtcTimestamp: btcTimestamp, + }, + } +} From 59802ad94c08ca0a67354b92d8e12dbafdd5047e Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Thu, 25 May 2023 10:20:32 +1000 Subject: [PATCH 8/9] zoneconcierge: parameterise timeout for IBC packets (#386) --- app/app.go | 1 + client/docs/swagger-ui/swagger.yaml | 246 +++++++++ proto/babylon/zoneconcierge/v1/genesis.proto | 4 + proto/babylon/zoneconcierge/v1/params.proto | 16 + proto/babylon/zoneconcierge/v1/query.proto | 15 + proto/babylon/zoneconcierge/v1/tx.proto | 31 +- testutil/keeper/zoneconcierge.go | 3 + x/epoching/types/genesis.go | 2 +- x/zoneconcierge/genesis.go | 6 + x/zoneconcierge/genesis_test.go | 2 + x/zoneconcierge/keeper/grpc_query.go | 15 +- x/zoneconcierge/keeper/ibc_packet.go | 3 +- x/zoneconcierge/keeper/keeper.go | 5 + x/zoneconcierge/keeper/msg_server.go | 22 + x/zoneconcierge/keeper/params.go | 32 ++ x/zoneconcierge/keeper/params_test.go | 20 + x/zoneconcierge/types/genesis.go | 12 + x/zoneconcierge/types/genesis.pb.go | 76 ++- x/zoneconcierge/types/genesis_test.go | 1 + x/zoneconcierge/types/keys.go | 2 +- x/zoneconcierge/types/msg.go | 30 ++ x/zoneconcierge/types/packet.pb.go | 11 +- x/zoneconcierge/types/params.go | 34 ++ x/zoneconcierge/types/params.pb.go | 333 ++++++++++++ x/zoneconcierge/types/params_test.go | 21 + x/zoneconcierge/types/query.pb.go | 510 +++++++++++++++--- x/zoneconcierge/types/query.pb.gw.go | 65 +++ x/zoneconcierge/types/tx.pb.go | 535 ++++++++++++++++++- 28 files changed, 1938 insertions(+), 115 deletions(-) create mode 100644 proto/babylon/zoneconcierge/v1/params.proto create mode 100644 x/zoneconcierge/keeper/params.go create mode 100644 x/zoneconcierge/keeper/params_test.go create mode 100644 x/zoneconcierge/types/msg.go create mode 100644 x/zoneconcierge/types/params.go create mode 100644 x/zoneconcierge/types/params.pb.go create mode 100644 x/zoneconcierge/types/params_test.go diff --git a/app/app.go b/app/app.go index 9ce46ebb4..c878a001f 100644 --- a/app/app.go +++ b/app/app.go @@ -568,6 +568,7 @@ func NewBabylonApp( tmClient, storeQuerier, scopedZoneConciergeKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) // replace IBC keeper's client keeper with our ExtendedKeeper diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 25146beee..a9d62f364 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -10319,6 +10319,224 @@ paths: format: uint64 tags: - Query + /babylon/zoneconcierge/v1/params: + get: + summary: Params queries the parameters of the module. + operationId: ZoneConciergeParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + ibc_packet_timeout_seconds: + type: integer + format: int64 + title: >- + ibc_packet_timeout_seconds is the time period after which + an unrelayed + + IBC packet becomes timeout, measured in seconds + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query definitions: babylon.btccheckpoint.v1.BTCCheckpointInfo: type: object @@ -16596,6 +16814,18 @@ definitions: the header on Babylon ledger title: IndexedHeader is the metadata of a CZ header + babylon.zoneconcierge.v1.Params: + type: object + properties: + ibc_packet_timeout_seconds: + type: integer + format: int64 + title: >- + ibc_packet_timeout_seconds is the time period after which an + unrelayed + + IBC packet becomes timeout, measured in seconds + description: Params defines the parameters for the module. babylon.zoneconcierge.v1.ProofEpochSealed: type: object properties: @@ -19636,6 +19866,22 @@ definitions: description: |- QueryListHeadersResponse is response type for the Query/ListHeaders RPC method. + babylon.zoneconcierge.v1.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + ibc_packet_timeout_seconds: + type: integer + format: int64 + title: >- + ibc_packet_timeout_seconds is the time period after which an + unrelayed + + IBC packet becomes timeout, measured in seconds + description: QueryParamsResponse is the response type for the Query/Params RPC method. tendermint.crypto.Proof: type: object properties: diff --git a/proto/babylon/zoneconcierge/v1/genesis.proto b/proto/babylon/zoneconcierge/v1/genesis.proto index 65ed8cb19..468fa02eb 100644 --- a/proto/babylon/zoneconcierge/v1/genesis.proto +++ b/proto/babylon/zoneconcierge/v1/genesis.proto @@ -1,9 +1,13 @@ syntax = "proto3"; package babylon.zoneconcierge.v1; +import "gogoproto/gogo.proto"; +import "babylon/zoneconcierge/v1/params.proto"; + option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; // GenesisState defines the zoneconcierge module's genesis state. message GenesisState { string port_id = 1; + Params params = 2 [ (gogoproto.nullable) = false ]; } diff --git a/proto/babylon/zoneconcierge/v1/params.proto b/proto/babylon/zoneconcierge/v1/params.proto new file mode 100644 index 000000000..bb3e23738 --- /dev/null +++ b/proto/babylon/zoneconcierge/v1/params.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package babylon.zoneconcierge.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; + +// Params defines the parameters for the module. +message Params { + option (gogoproto.equal) = true; + + // ibc_packet_timeout_seconds is the time period after which an unrelayed + // IBC packet becomes timeout, measured in seconds + uint32 ibc_packet_timeout_seconds = 1 + [ (gogoproto.moretags) = "yaml:\"ibc_packet_timeout_seconds\"" ]; +} diff --git a/proto/babylon/zoneconcierge/v1/query.proto b/proto/babylon/zoneconcierge/v1/query.proto index e3675682d..aa65a805f 100644 --- a/proto/babylon/zoneconcierge/v1/query.proto +++ b/proto/babylon/zoneconcierge/v1/query.proto @@ -1,17 +1,23 @@ syntax = "proto3"; package babylon.zoneconcierge.v1; +import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; import "babylon/btccheckpoint/v1/btccheckpoint.proto"; import "babylon/checkpointing/v1/checkpoint.proto"; import "babylon/epoching/v1/epoching.proto"; import "babylon/zoneconcierge/v1/zoneconcierge.proto"; +import "babylon/zoneconcierge/v1/params.proto"; option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; // Query defines the gRPC querier service. service Query { + // Params queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/babylon/zoneconcierge/v1/params"; + } // Header queries the CZ header and fork headers at a given height. rpc Header(QueryHeaderRequest) returns (QueryHeaderResponse) { option (google.api.http).get = @@ -62,6 +68,15 @@ service Query { } } +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + babylon.zoneconcierge.v1.Params params = 1 [ (gogoproto.nullable) = false ]; +} + // QueryHeaderRequest is request type for the Query/Header RPC method. message QueryHeaderRequest { string chain_id = 1; diff --git a/proto/babylon/zoneconcierge/v1/tx.proto b/proto/babylon/zoneconcierge/v1/tx.proto index f3820e6fa..4fc9a78b9 100644 --- a/proto/babylon/zoneconcierge/v1/tx.proto +++ b/proto/babylon/zoneconcierge/v1/tx.proto @@ -1,7 +1,36 @@ syntax = "proto3"; package babylon.zoneconcierge.v1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "babylon/zoneconcierge/v1/params.proto"; + option go_package = "github.com/babylonchain/babylon/x/zoneconcierge/types"; // Msg defines the Msg service. -service Msg {} +service Msg { + // UpdateParams updates the btccheckpoint module parameters. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgUpdateParams defines a message for updating zoneconcierge module parameters. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the governance account. + // just FYI: cosmos.AddressString marks that this field should use type alias + // for AddressString instead of string, but the functionality is not yet implemented + // in cosmos-proto + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // params defines the epoching paramaeters parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; + } + + // MsgUpdateParamsResponse is the response to the MsgUpdateParams message. + message MsgUpdateParamsResponse {} + \ No newline at end of file diff --git a/testutil/keeper/zoneconcierge.go b/testutil/keeper/zoneconcierge.go index e300d8025..5d11eff1d 100644 --- a/testutil/keeper/zoneconcierge.go +++ b/testutil/keeper/zoneconcierge.go @@ -15,8 +15,10 @@ import ( "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/stretchr/testify/require" @@ -95,6 +97,7 @@ func ZoneConciergeKeeper(t testing.TB, btclcKeeper types.BTCLightClientKeeper, c tmClient, zoneconciergeStoreQuerier{}, capabilityKeeper.ScopeToModule("ZoneconciergeScopedKeeper"), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, logger) diff --git a/x/epoching/types/genesis.go b/x/epoching/types/genesis.go index 641451929..9fec52433 100644 --- a/x/epoching/types/genesis.go +++ b/x/epoching/types/genesis.go @@ -10,7 +10,7 @@ func DefaultGenesis() *GenesisState { } } -// NewGenesis creates a new GenesisState instanc e +// NewGenesis creates a new GenesisState instance func NewGenesis(params Params) *GenesisState { return &GenesisState{ Params: params, diff --git a/x/zoneconcierge/genesis.go b/x/zoneconcierge/genesis.go index fb3758b66..eb5ec4424 100644 --- a/x/zoneconcierge/genesis.go +++ b/x/zoneconcierge/genesis.go @@ -8,6 +8,11 @@ import ( // InitGenesis initializes the module's state from a provided genesis state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + // set params for this module + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(err) + } + k.SetPort(ctx, genState.PortId) // Only try to bind to port if it is not already bound, since we may already own // port capability from capability InitGenesis @@ -24,6 +29,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) // ExportGenesis returns the module's exported genesis func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis := types.DefaultGenesis() + genesis.Params = k.GetParams(ctx) genesis.PortId = k.GetPort(ctx) return genesis } diff --git a/x/zoneconcierge/genesis_test.go b/x/zoneconcierge/genesis_test.go index c0fb0bf36..04e049233 100644 --- a/x/zoneconcierge/genesis_test.go +++ b/x/zoneconcierge/genesis_test.go @@ -13,6 +13,7 @@ import ( func TestGenesis(t *testing.T) { genesisState := types.GenesisState{ PortId: types.PortID, + Params: types.Params{IbcPacketTimeoutSeconds: 100}, } k, ctx := keepertest.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) @@ -24,4 +25,5 @@ func TestGenesis(t *testing.T) { nullify.Fill(got) require.Equal(t, genesisState.PortId, got.PortId) + require.Equal(t, genesisState.Params, got.Params) } diff --git a/x/zoneconcierge/keeper/grpc_query.go b/x/zoneconcierge/keeper/grpc_query.go index 191f59f32..657915801 100644 --- a/x/zoneconcierge/keeper/grpc_query.go +++ b/x/zoneconcierge/keeper/grpc_query.go @@ -3,20 +3,27 @@ package keeper import ( "context" + bbntypes "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/zoneconcierge/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - - bbntypes "github.com/babylonchain/babylon/types" - - "github.com/babylonchain/babylon/x/zoneconcierge/types" ) var _ types.QueryServer = Keeper{} const maxQueryChainsInfoLimit = 100 +func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(c) + + return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil +} + func (k Keeper) ChainList(c context.Context, req *types.QueryChainListRequest) (*types.QueryChainListResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") diff --git a/x/zoneconcierge/keeper/ibc_packet.go b/x/zoneconcierge/keeper/ibc_packet.go index a75fc2415..51b84cfec 100644 --- a/x/zoneconcierge/keeper/ibc_packet.go +++ b/x/zoneconcierge/keeper/ibc_packet.go @@ -32,7 +32,8 @@ func (k Keeper) SendIBCPacket(ctx sdk.Context, channel channeltypes.IdentifiedCh } // timeout - timeoutTime := uint64(ctx.BlockHeader().Time.Add(time.Hour * 24).UnixNano()) // TODO: parameterise + timeoutPeriod := time.Duration(k.GetParams(ctx).IbcPacketTimeoutSeconds) * time.Second + timeoutTime := uint64(ctx.BlockHeader().Time.Add(timeoutPeriod).UnixNano()) zeroheight := clienttypes.ZeroHeight() seq, err := k.ics4Wrapper.SendPacket( diff --git a/x/zoneconcierge/keeper/keeper.go b/x/zoneconcierge/keeper/keeper.go index b7ce6ada1..8231d6a10 100644 --- a/x/zoneconcierge/keeper/keeper.go +++ b/x/zoneconcierge/keeper/keeper.go @@ -29,6 +29,9 @@ type ( tmClient types.TMClient storeQuerier sdk.Queryable scopedKeeper types.ScopedKeeper + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string } ) @@ -48,6 +51,7 @@ func NewKeeper( tmClient types.TMClient, storeQuerier sdk.Queryable, scopedKeeper types.ScopedKeeper, + authority string, ) *Keeper { return &Keeper{ cdc: cdc, @@ -65,6 +69,7 @@ func NewKeeper( tmClient: tmClient, storeQuerier: storeQuerier, scopedKeeper: scopedKeeper, + authority: authority, } } diff --git a/x/zoneconcierge/keeper/msg_server.go b/x/zoneconcierge/keeper/msg_server.go index a4d989c6a..42d7a88fa 100644 --- a/x/zoneconcierge/keeper/msg_server.go +++ b/x/zoneconcierge/keeper/msg_server.go @@ -1,7 +1,12 @@ package keeper import ( + "context" + + errorsmod "cosmossdk.io/errors" "github.com/babylonchain/babylon/x/zoneconcierge/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) type msgServer struct { @@ -15,3 +20,20 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { } var _ types.MsgServer = msgServer{} + +// UpdateParams updates the params. +// TODO investigate when it is the best time to update the params. We can update them +// when the epoch changes, but we can also update them during the epoch and extend +// the epoch duration. +func (ms msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if ms.authority != req.Authority { + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, req.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := ms.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/zoneconcierge/keeper/params.go b/x/zoneconcierge/keeper/params.go new file mode 100644 index 000000000..2cfae5c0f --- /dev/null +++ b/x/zoneconcierge/keeper/params.go @@ -0,0 +1,32 @@ +package keeper + +import ( + "github.com/babylonchain/babylon/x/zoneconcierge/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GetParams get all parameters as types.Params +// SetParams sets the x/zoneconcierge module parameters. +func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { + if err := p.Validate(); err != nil { + return err + } + + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&p) + store.Set(types.ParamsKey, bz) + + return nil +} + +// GetParams returns the current x/zoneconcierge module parameters. +func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return p + } + + k.cdc.MustUnmarshal(bz, &p) + return p +} diff --git a/x/zoneconcierge/keeper/params_test.go b/x/zoneconcierge/keeper/params_test.go new file mode 100644 index 000000000..70c1f96a6 --- /dev/null +++ b/x/zoneconcierge/keeper/params_test.go @@ -0,0 +1,20 @@ +package keeper_test + +import ( + "testing" + + testkeeper "github.com/babylonchain/babylon/testutil/keeper" + "github.com/babylonchain/babylon/x/zoneconcierge/types" + "github.com/stretchr/testify/require" +) + +func TestGetParams(t *testing.T) { + k, ctx := testkeeper.ZoneConciergeKeeper(t, nil, nil, nil, nil, nil) + params := types.DefaultParams() + + if err := k.SetParams(ctx, params); err != nil { + panic(err) + } + + require.EqualValues(t, params, k.GetParams(ctx)) +} diff --git a/x/zoneconcierge/types/genesis.go b/x/zoneconcierge/types/genesis.go index 5bd0f2ea7..37b63a085 100644 --- a/x/zoneconcierge/types/genesis.go +++ b/x/zoneconcierge/types/genesis.go @@ -11,6 +11,15 @@ const DefaultIndex uint64 = 1 func DefaultGenesis() *GenesisState { return &GenesisState{ PortId: PortID, + Params: DefaultParams(), + } +} + +// NewGenesis creates a new GenesisState instance +func NewGenesis(params Params) *GenesisState { + return &GenesisState{ + PortId: PortID, + Params: params, } } @@ -20,5 +29,8 @@ func (gs GenesisState) Validate() error { if err := host.PortIdentifierValidator(gs.PortId); err != nil { return err } + if err := gs.Params.Validate(); err != nil { + return err + } return nil } diff --git a/x/zoneconcierge/types/genesis.pb.go b/x/zoneconcierge/types/genesis.pb.go index 67c72b428..1eed06657 100644 --- a/x/zoneconcierge/types/genesis.pb.go +++ b/x/zoneconcierge/types/genesis.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -25,6 +26,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the zoneconcierge module's genesis state. type GenesisState struct { PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -67,6 +69,13 @@ func (m *GenesisState) GetPortId() string { return "" } +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + func init() { proto.RegisterType((*GenesisState)(nil), "babylon.zoneconcierge.v1.GenesisState") } @@ -76,18 +85,22 @@ func init() { } var fileDescriptor_56f290ad7c2c7dc7 = []byte{ - // 173 bytes of a gzipped FileDescriptorProto + // 228 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4b, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0xaf, 0xca, 0xcf, 0x4b, 0x4d, 0xce, 0xcf, 0x4b, 0xce, 0x4c, 0x2d, 0x4a, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, - 0x2f, 0xc9, 0x17, 0x92, 0x80, 0xaa, 0xd3, 0x43, 0x51, 0xa7, 0x57, 0x66, 0xa8, 0xa4, 0xce, 0xc5, - 0xe3, 0x0e, 0x51, 0x1a, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x24, 0xce, 0xc5, 0x5e, 0x90, 0x5f, 0x54, - 0x12, 0x9f, 0x99, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0xc4, 0x06, 0xe2, 0x7a, 0xa6, 0x38, - 0xf9, 0x9f, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, - 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x69, 0x7a, 0x66, 0x49, - 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0xd4, 0x9e, 0xe4, 0x8c, 0xc4, 0xcc, 0x3c, 0x18, - 0x47, 0xbf, 0x02, 0xcd, 0x79, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0xa7, 0x19, 0x03, - 0x02, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x84, 0xa6, 0xb1, 0xc4, 0x00, 0x00, 0x00, + 0x2f, 0xc9, 0x17, 0x92, 0x80, 0xaa, 0xd3, 0x43, 0x51, 0xa7, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, + 0x9f, 0x9e, 0x0f, 0x56, 0xa4, 0x0f, 0x62, 0x41, 0xd4, 0x4b, 0xa9, 0xe2, 0x34, 0xb7, 0x20, 0xb1, + 0x28, 0x31, 0x17, 0x6a, 0xac, 0x52, 0x3a, 0x17, 0x8f, 0x3b, 0xc4, 0x9e, 0xe0, 0x92, 0xc4, 0x92, + 0x54, 0x21, 0x71, 0x2e, 0xf6, 0x82, 0xfc, 0xa2, 0x92, 0xf8, 0xcc, 0x14, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0xce, 0x20, 0x36, 0x10, 0xd7, 0x33, 0x45, 0xc8, 0x8e, 0x8b, 0x0d, 0xa2, 0x51, 0x82, 0x49, + 0x81, 0x51, 0x83, 0xdb, 0x48, 0x41, 0x0f, 0x97, 0x83, 0xf4, 0x02, 0xc0, 0xea, 0x9c, 0x58, 0x4e, + 0xdc, 0x93, 0x67, 0x08, 0x82, 0xea, 0x72, 0xf2, 0x3f, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, + 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, + 0x39, 0x86, 0x28, 0xd3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, + 0x99, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x05, 0x9a, 0x1f, 0x4a, 0x2a, 0x0b, 0x52, + 0x8b, 0x93, 0xd8, 0xc0, 0x1e, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xb2, 0x6d, 0xbb, + 0x41, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -110,6 +123,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 if len(m.PortId) > 0 { i -= len(m.PortId) copy(dAtA[i:], m.PortId) @@ -141,6 +164,8 @@ func (m *GenesisState) Size() (n int) { if l > 0 { n += 1 + l + sovGenesis(uint64(l)) } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -211,6 +236,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { } m.PortId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/zoneconcierge/types/genesis_test.go b/x/zoneconcierge/types/genesis_test.go index fc2ead9e0..0902eb6d7 100644 --- a/x/zoneconcierge/types/genesis_test.go +++ b/x/zoneconcierge/types/genesis_test.go @@ -22,6 +22,7 @@ func TestGenesisState_Validate(t *testing.T) { desc: "valid genesis state", genState: &types.GenesisState{ PortId: types.PortID, + Params: types.Params{IbcPacketTimeoutSeconds: 100}, }, valid: true, }, diff --git a/x/zoneconcierge/types/keys.go b/x/zoneconcierge/types/keys.go index 4968c8956..b8b2bc1da 100644 --- a/x/zoneconcierge/types/keys.go +++ b/x/zoneconcierge/types/keys.go @@ -35,7 +35,7 @@ var ( EpochChainInfoKey = []byte{0x15} // EpochChainInfoKey defines the key to store each epoch's latests chain info for each CZ in store FinalizedEpochKey = []byte{0x16} // FinalizedEpochKey defines the key to store the last finalised epoch FinalizingBTCTipKey = []byte{0x17} // FinalizingBTCTipKey defines the key to store the BTC tip when the last epoch is finalised - IBCChannelsKey = []byte{0x18} // IBCChannelsKey defines the key to store the initialisation status of IBC channels + ParamsKey = []byte{0x18} // key prefix for the parameters ) func KeyPrefix(p string) []byte { diff --git a/x/zoneconcierge/types/msg.go b/x/zoneconcierge/types/msg.go new file mode 100644 index 000000000..fadaf538a --- /dev/null +++ b/x/zoneconcierge/types/msg.go @@ -0,0 +1,30 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure that these message types implement the sdk.Msg interface +var ( + _ sdk.Msg = &MsgUpdateParams{} +) + +// GetSigners returns the expected signers for a MsgUpdateParams message. +func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check on the provided data. +func (m *MsgUpdateParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + return errorsmod.Wrap(err, "invalid authority address") + } + + if err := m.Params.Validate(); err != nil { + return err + } + + return nil +} diff --git a/x/zoneconcierge/types/packet.pb.go b/x/zoneconcierge/types/packet.pb.go index 56a5e3f68..d2f44c32e 100644 --- a/x/zoneconcierge/types/packet.pb.go +++ b/x/zoneconcierge/types/packet.pb.go @@ -102,11 +102,18 @@ func (*ZoneconciergePacketData) XXX_OneofWrappers() []interface{} { } } -// BTCTimestamp is a BTC timestamp +// BTCTimestamp is a BTC timestamp that carries information of a BTC-finalised epoch +// It includes a number of BTC headers, a raw checkpoint, an epoch metadata, and +// a CZ header if there exists CZ headers checkpointed to this epoch. +// Upon a newly finalised epoch in Babylon, Babylon will send a BTC timestamp to each +// Cosmos zone that has phase-2 integration with Babylon via IBC. type BTCTimestamp struct { // header is the last CZ header in the finalized Babylon epoch Header *IndexedHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - // btc_headers is BTC headers from the BTC header tip upon the last finalised epoch to the current tip + // btc_headers is BTC headers between + // - the block AFTER the common ancestor of BTC tip at epoch `lastFinalizedEpoch-1` and BTC tip at epoch `lastFinalizedEpoch` + // - BTC tip at epoch `lastFinalizedEpoch` + // where `lastFinalizedEpoch` is the last finalised epoch in Babylon BtcHeaders []*types.BTCHeaderInfo `protobuf:"bytes,2,rep,name=btc_headers,json=btcHeaders,proto3" json:"btc_headers,omitempty"` // epoch_info is the metadata of the sealed epoch EpochInfo *types1.Epoch `protobuf:"bytes,3,opt,name=epoch_info,json=epochInfo,proto3" json:"epoch_info,omitempty"` diff --git a/x/zoneconcierge/types/params.go b/x/zoneconcierge/types/params.go new file mode 100644 index 000000000..298ade2a7 --- /dev/null +++ b/x/zoneconcierge/types/params.go @@ -0,0 +1,34 @@ +package types + +import ( + "fmt" +) + +const ( + DefaultIbcPacketTimeoutSeconds uint32 = 60 * 60 * 24 // 24 hours + MaxIbcPacketTimeoutSeconds uint32 = 60 * 60 * 24 * 365 // 1 year +) + +// NewParams creates a new Params instance +func NewParams(ibcPacketTimeoutSeconds uint32) Params { + return Params{ + IbcPacketTimeoutSeconds: ibcPacketTimeoutSeconds, + } +} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return NewParams(DefaultIbcPacketTimeoutSeconds) +} + +// Validate validates the set of params +func (p Params) Validate() error { + if p.IbcPacketTimeoutSeconds == 0 { + return fmt.Errorf("IbcPacketTimeoutSeconds must be positive") + } + if p.IbcPacketTimeoutSeconds > MaxIbcPacketTimeoutSeconds { + return fmt.Errorf("IbcPacketTimeoutSeconds must be no larger than %d", MaxIbcPacketTimeoutSeconds) + } + + return nil +} diff --git a/x/zoneconcierge/types/params.pb.go b/x/zoneconcierge/types/params.pb.go new file mode 100644 index 000000000..ac4b27c33 --- /dev/null +++ b/x/zoneconcierge/types/params.pb.go @@ -0,0 +1,333 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/zoneconcierge/v1/params.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params defines the parameters for the module. +type Params struct { + // ibc_packet_timeout_seconds is the time period after which an unrelayed + // IBC packet becomes timeout, measured in seconds + IbcPacketTimeoutSeconds uint32 `protobuf:"varint,1,opt,name=ibc_packet_timeout_seconds,json=ibcPacketTimeoutSeconds,proto3" json:"ibc_packet_timeout_seconds,omitempty" yaml:"ibc_packet_timeout_seconds"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_c0696c936eb15fe4, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetIbcPacketTimeoutSeconds() uint32 { + if m != nil { + return m.IbcPacketTimeoutSeconds + } + return 0 +} + +func init() { + proto.RegisterType((*Params)(nil), "babylon.zoneconcierge.v1.Params") +} + +func init() { + proto.RegisterFile("babylon/zoneconcierge/v1/params.proto", fileDescriptor_c0696c936eb15fe4) +} + +var fileDescriptor_c0696c936eb15fe4 = []byte{ + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0xaf, 0xca, 0xcf, 0x4b, 0x4d, 0xce, 0xcf, 0x4b, 0xce, 0x4c, 0x2d, 0x4a, + 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x92, 0x80, 0x2a, 0xd3, 0x43, 0x51, 0xa6, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, + 0x9e, 0x0f, 0x56, 0xa4, 0x0f, 0x62, 0x41, 0xd4, 0x2b, 0x15, 0x71, 0xb1, 0x05, 0x80, 0xf5, 0x0b, + 0x25, 0x71, 0x49, 0x65, 0x26, 0x25, 0xc7, 0x17, 0x24, 0x26, 0x67, 0xa7, 0x96, 0xc4, 0x97, 0x64, + 0xe6, 0xa6, 0xe6, 0x97, 0x96, 0xc4, 0x17, 0x83, 0x0c, 0x49, 0x29, 0x96, 0x60, 0x54, 0x60, 0xd4, + 0xe0, 0x75, 0x52, 0xfd, 0x74, 0x4f, 0x5e, 0xb1, 0x32, 0x31, 0x37, 0xc7, 0x4a, 0x09, 0xb7, 0x5a, + 0xa5, 0x20, 0xf1, 0xcc, 0xa4, 0xe4, 0x00, 0xb0, 0x5c, 0x08, 0x44, 0x2a, 0x18, 0x22, 0x63, 0xc5, + 0xf2, 0x62, 0x81, 0x3c, 0xa3, 0x93, 0xff, 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, 0x99, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0x3d, 0x92, + 0x9c, 0x91, 0x98, 0x99, 0x07, 0xe3, 0xe8, 0x57, 0xa0, 0x79, 0xbf, 0xa4, 0xb2, 0x20, 0xb5, 0x38, + 0x89, 0x0d, 0xec, 0x17, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xf3, 0xe4, 0xef, 0x24, + 0x01, 0x00, 0x00, +} + +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.IbcPacketTimeoutSeconds != that1.IbcPacketTimeoutSeconds { + return false + } + return true +} +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IbcPacketTimeoutSeconds != 0 { + i = encodeVarintParams(dAtA, i, uint64(m.IbcPacketTimeoutSeconds)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IbcPacketTimeoutSeconds != 0 { + n += 1 + sovParams(uint64(m.IbcPacketTimeoutSeconds)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IbcPacketTimeoutSeconds", wireType) + } + m.IbcPacketTimeoutSeconds = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IbcPacketTimeoutSeconds |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowParams + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/zoneconcierge/types/params_test.go b/x/zoneconcierge/types/params_test.go new file mode 100644 index 000000000..c4be306d0 --- /dev/null +++ b/x/zoneconcierge/types/params_test.go @@ -0,0 +1,21 @@ +package types_test + +import ( + "testing" + + "github.com/babylonchain/babylon/x/zoneconcierge/types" + "github.com/stretchr/testify/require" +) + +func TestParamsEqual(t *testing.T) { + p1 := types.DefaultParams() + p2 := types.DefaultParams() + + ok := p1.Equal(p2) + require.True(t, ok) + + p2.IbcPacketTimeoutSeconds = 100 + + ok = p1.Equal(p2) + require.False(t, ok) +} diff --git a/x/zoneconcierge/types/query.pb.go b/x/zoneconcierge/types/query.pb.go index fa90d6a3d..64d219deb 100644 --- a/x/zoneconcierge/types/query.pb.go +++ b/x/zoneconcierge/types/query.pb.go @@ -10,6 +10,7 @@ import ( types1 "github.com/babylonchain/babylon/x/checkpointing/types" types "github.com/babylonchain/babylon/x/epoching/types" query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" @@ -32,6 +33,89 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd665af90102da38, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params holds all the parameters of this module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd665af90102da38, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + // QueryHeaderRequest is request type for the Query/Header RPC method. type QueryHeaderRequest struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` @@ -42,7 +126,7 @@ func (m *QueryHeaderRequest) Reset() { *m = QueryHeaderRequest{} } func (m *QueryHeaderRequest) String() string { return proto.CompactTextString(m) } func (*QueryHeaderRequest) ProtoMessage() {} func (*QueryHeaderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{0} + return fileDescriptor_cd665af90102da38, []int{2} } func (m *QueryHeaderRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -95,7 +179,7 @@ func (m *QueryHeaderResponse) Reset() { *m = QueryHeaderResponse{} } func (m *QueryHeaderResponse) String() string { return proto.CompactTextString(m) } func (*QueryHeaderResponse) ProtoMessage() {} func (*QueryHeaderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{1} + return fileDescriptor_cd665af90102da38, []int{3} } func (m *QueryHeaderResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -148,7 +232,7 @@ func (m *QueryChainListRequest) Reset() { *m = QueryChainListRequest{} } func (m *QueryChainListRequest) String() string { return proto.CompactTextString(m) } func (*QueryChainListRequest) ProtoMessage() {} func (*QueryChainListRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{2} + return fileDescriptor_cd665af90102da38, []int{4} } func (m *QueryChainListRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -196,7 +280,7 @@ func (m *QueryChainListResponse) Reset() { *m = QueryChainListResponse{} func (m *QueryChainListResponse) String() string { return proto.CompactTextString(m) } func (*QueryChainListResponse) ProtoMessage() {} func (*QueryChainListResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{3} + return fileDescriptor_cd665af90102da38, []int{5} } func (m *QueryChainListResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -248,7 +332,7 @@ func (m *QueryChainsInfoRequest) Reset() { *m = QueryChainsInfoRequest{} func (m *QueryChainsInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryChainsInfoRequest) ProtoMessage() {} func (*QueryChainsInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{4} + return fileDescriptor_cd665af90102da38, []int{6} } func (m *QueryChainsInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -293,7 +377,7 @@ func (m *QueryChainsInfoResponse) Reset() { *m = QueryChainsInfoResponse func (m *QueryChainsInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryChainsInfoResponse) ProtoMessage() {} func (*QueryChainsInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{5} + return fileDescriptor_cd665af90102da38, []int{7} } func (m *QueryChainsInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -340,7 +424,7 @@ func (m *QueryEpochChainsInfoRequest) Reset() { *m = QueryEpochChainsInf func (m *QueryEpochChainsInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryEpochChainsInfoRequest) ProtoMessage() {} func (*QueryEpochChainsInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{6} + return fileDescriptor_cd665af90102da38, []int{8} } func (m *QueryEpochChainsInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -394,7 +478,7 @@ func (m *QueryEpochChainsInfoResponse) Reset() { *m = QueryEpochChainsIn func (m *QueryEpochChainsInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryEpochChainsInfoResponse) ProtoMessage() {} func (*QueryEpochChainsInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{7} + return fileDescriptor_cd665af90102da38, []int{9} } func (m *QueryEpochChainsInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -441,7 +525,7 @@ func (m *QueryListHeadersRequest) Reset() { *m = QueryListHeadersRequest func (m *QueryListHeadersRequest) String() string { return proto.CompactTextString(m) } func (*QueryListHeadersRequest) ProtoMessage() {} func (*QueryListHeadersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{8} + return fileDescriptor_cd665af90102da38, []int{10} } func (m *QueryListHeadersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -497,7 +581,7 @@ func (m *QueryListHeadersResponse) Reset() { *m = QueryListHeadersRespon func (m *QueryListHeadersResponse) String() string { return proto.CompactTextString(m) } func (*QueryListHeadersResponse) ProtoMessage() {} func (*QueryListHeadersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{9} + return fileDescriptor_cd665af90102da38, []int{11} } func (m *QueryListHeadersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -551,7 +635,7 @@ func (m *QueryListEpochHeadersRequest) Reset() { *m = QueryListEpochHead func (m *QueryListEpochHeadersRequest) String() string { return proto.CompactTextString(m) } func (*QueryListEpochHeadersRequest) ProtoMessage() {} func (*QueryListEpochHeadersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{10} + return fileDescriptor_cd665af90102da38, []int{12} } func (m *QueryListEpochHeadersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -605,7 +689,7 @@ func (m *QueryListEpochHeadersResponse) Reset() { *m = QueryListEpochHea func (m *QueryListEpochHeadersResponse) String() string { return proto.CompactTextString(m) } func (*QueryListEpochHeadersResponse) ProtoMessage() {} func (*QueryListEpochHeadersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{11} + return fileDescriptor_cd665af90102da38, []int{13} } func (m *QueryListEpochHeadersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -654,7 +738,7 @@ func (m *QueryFinalizedChainsInfoRequest) Reset() { *m = QueryFinalizedC func (m *QueryFinalizedChainsInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryFinalizedChainsInfoRequest) ProtoMessage() {} func (*QueryFinalizedChainsInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{12} + return fileDescriptor_cd665af90102da38, []int{14} } func (m *QueryFinalizedChainsInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -707,7 +791,7 @@ func (m *QueryFinalizedChainsInfoResponse) Reset() { *m = QueryFinalized func (m *QueryFinalizedChainsInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryFinalizedChainsInfoResponse) ProtoMessage() {} func (*QueryFinalizedChainsInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{13} + return fileDescriptor_cd665af90102da38, []int{15} } func (m *QueryFinalizedChainsInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -764,7 +848,7 @@ func (m *QueryFinalizedChainInfoUntilHeightRequest) String() string { } func (*QueryFinalizedChainInfoUntilHeightRequest) ProtoMessage() {} func (*QueryFinalizedChainInfoUntilHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{14} + return fileDescriptor_cd665af90102da38, []int{16} } func (m *QueryFinalizedChainInfoUntilHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -838,7 +922,7 @@ func (m *QueryFinalizedChainInfoUntilHeightResponse) String() string { } func (*QueryFinalizedChainInfoUntilHeightResponse) ProtoMessage() {} func (*QueryFinalizedChainInfoUntilHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cd665af90102da38, []int{15} + return fileDescriptor_cd665af90102da38, []int{17} } func (m *QueryFinalizedChainInfoUntilHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -903,6 +987,8 @@ func (m *QueryFinalizedChainInfoUntilHeightResponse) GetProof() *ProofFinalizedC } func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "babylon.zoneconcierge.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "babylon.zoneconcierge.v1.QueryParamsResponse") proto.RegisterType((*QueryHeaderRequest)(nil), "babylon.zoneconcierge.v1.QueryHeaderRequest") proto.RegisterType((*QueryHeaderResponse)(nil), "babylon.zoneconcierge.v1.QueryHeaderResponse") proto.RegisterType((*QueryChainListRequest)(nil), "babylon.zoneconcierge.v1.QueryChainListRequest") @@ -926,76 +1012,81 @@ func init() { } var fileDescriptor_cd665af90102da38 = []byte{ - // 1101 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x4d, 0x6f, 0x1b, 0x45, - 0x18, 0xce, 0x3a, 0x1f, 0x4d, 0x5e, 0xf3, 0x51, 0x4d, 0xda, 0x62, 0xb6, 0xad, 0x6b, 0x2d, 0x1f, - 0x4d, 0x4b, 0xb2, 0x8b, 0x5d, 0xd2, 0xaa, 0x5c, 0xaa, 0x36, 0x25, 0x69, 0x54, 0x54, 0xda, 0x85, - 0x80, 0xc4, 0x65, 0xd9, 0x5d, 0x8f, 0xed, 0x55, 0xe2, 0x1d, 0xd7, 0xb3, 0x76, 0xeb, 0x86, 0x70, - 0x40, 0xfc, 0x00, 0x24, 0x2e, 0x88, 0x13, 0x27, 0x0e, 0x1c, 0x7a, 0xe3, 0x27, 0x20, 0x71, 0xe0, - 0x50, 0x89, 0x0b, 0x47, 0x94, 0xf0, 0x0b, 0xb8, 0x23, 0xa1, 0x9d, 0x99, 0xb5, 0xf7, 0xd3, 0x5e, - 0x87, 0xdc, 0x32, 0xe3, 0xf7, 0x7d, 0x9e, 0xe7, 0xfd, 0x9a, 0x77, 0x03, 0x6f, 0x5a, 0xa6, 0x35, - 0xd8, 0x23, 0xae, 0xf6, 0x8c, 0xb8, 0xd8, 0x26, 0xae, 0xed, 0xe0, 0x6e, 0x13, 0x6b, 0xfd, 0xaa, - 0xf6, 0xb8, 0x87, 0xbb, 0x03, 0xb5, 0xd3, 0x25, 0x1e, 0x41, 0x25, 0x61, 0xa5, 0x46, 0xac, 0xd4, - 0x7e, 0x55, 0xbe, 0xd0, 0x24, 0xa4, 0xb9, 0x87, 0x35, 0xb3, 0xe3, 0x68, 0xa6, 0xeb, 0x12, 0xcf, - 0xf4, 0x1c, 0xe2, 0x52, 0xee, 0x27, 0x5f, 0xb5, 0x09, 0x6d, 0x13, 0xaa, 0x59, 0x26, 0xc5, 0x1c, - 0x50, 0xeb, 0x57, 0x2d, 0xec, 0x99, 0x55, 0xad, 0x63, 0x36, 0x1d, 0x97, 0x19, 0x0b, 0xdb, 0xd5, - 0x40, 0x89, 0xe5, 0xd9, 0x76, 0x0b, 0xdb, 0xbb, 0x1d, 0xe2, 0xb8, 0x9e, 0xaf, 0x24, 0x72, 0x21, - 0xac, 0xaf, 0x04, 0xd6, 0xa3, 0x5f, 0x1c, 0xb7, 0xe9, 0x5b, 0x27, 0x4c, 0x95, 0xc0, 0x14, 0x77, - 0x88, 0xdd, 0x12, 0x56, 0xc1, 0xdf, 0x71, 0xf2, 0x44, 0x1a, 0xa2, 0x11, 0x33, 0x6b, 0x65, 0x0b, - 0xd0, 0x23, 0x3f, 0x98, 0x7b, 0xd8, 0xac, 0xe3, 0xae, 0x8e, 0x1f, 0xf7, 0x30, 0xf5, 0xd0, 0xeb, - 0xb0, 0x68, 0xb7, 0x4c, 0xc7, 0x35, 0x9c, 0x7a, 0x49, 0xaa, 0x48, 0x2b, 0x4b, 0xfa, 0x29, 0x76, - 0xde, 0xae, 0xa3, 0x73, 0xb0, 0xd0, 0xc2, 0x4e, 0xb3, 0xe5, 0x95, 0x0a, 0x15, 0x69, 0x65, 0x4e, - 0x17, 0x27, 0xe5, 0x07, 0x09, 0x96, 0x23, 0x48, 0xb4, 0x43, 0x5c, 0x8a, 0xd1, 0x2d, 0xdf, 0xde, - 0xbf, 0x61, 0x40, 0xc5, 0xda, 0x65, 0x35, 0xab, 0x00, 0xea, 0xb6, 0x5b, 0xc7, 0x4f, 0x71, 0x5d, - 0x00, 0x08, 0x37, 0x74, 0x07, 0x5e, 0x6a, 0x90, 0xee, 0xae, 0xc1, 0x8f, 0x94, 0xd1, 0x16, 0x6b, - 0x97, 0xb2, 0x61, 0x36, 0x49, 0x77, 0x97, 0xea, 0x45, 0xdf, 0x89, 0x43, 0x51, 0xc5, 0x80, 0xb3, - 0x4c, 0xdb, 0x86, 0x1f, 0xc4, 0x87, 0x0e, 0xf5, 0x82, 0x40, 0x37, 0x01, 0x46, 0xd5, 0x13, 0x0a, - 0xdf, 0x56, 0x79, 0xa9, 0x55, 0xbf, 0xd4, 0x2a, 0xef, 0x1d, 0x51, 0x6a, 0xf5, 0xa1, 0xd9, 0xc4, - 0xc2, 0x57, 0x0f, 0x79, 0x2a, 0x5f, 0xc1, 0xb9, 0x38, 0x81, 0x88, 0xff, 0x3c, 0x2c, 0x05, 0xa9, - 0xa4, 0x25, 0xa9, 0x32, 0xbb, 0xb2, 0xa4, 0x2f, 0x8a, 0x5c, 0x52, 0xb4, 0x15, 0xa1, 0x2f, 0x88, - 0x04, 0x4d, 0xa2, 0xe7, 0xc8, 0x11, 0xfe, 0xf5, 0x30, 0x3f, 0xdd, 0x76, 0x1b, 0x24, 0x88, 0x70, - 0x1c, 0xbf, 0x62, 0xc0, 0x6b, 0x09, 0x37, 0xa1, 0xfb, 0x2e, 0x14, 0x99, 0x19, 0x35, 0x1c, 0xb7, - 0x41, 0x98, 0x67, 0xb1, 0xf6, 0x46, 0x76, 0xd6, 0x19, 0x04, 0x43, 0x00, 0x7b, 0x88, 0xa6, 0x7c, - 0x06, 0xe7, 0x19, 0xc1, 0x07, 0x7e, 0x8f, 0xa6, 0x8a, 0x63, 0xdd, 0x6b, 0xb8, 0xbd, 0x36, 0xcb, - 0xfe, 0x9c, 0xbe, 0xc8, 0x2e, 0x1e, 0xf4, 0xda, 0x51, 0xe5, 0x85, 0x98, 0xf2, 0x3a, 0x5c, 0x48, - 0x07, 0x3e, 0x51, 0xf9, 0x5f, 0x8a, 0xfc, 0xf8, 0x15, 0x15, 0xbd, 0x94, 0x63, 0x44, 0x36, 0x53, - 0xaa, 0x7a, 0x9c, 0xa6, 0xfa, 0x49, 0x82, 0x52, 0x92, 0x5e, 0x04, 0x78, 0x1b, 0x4e, 0x05, 0x13, - 0xc1, 0x83, 0xcb, 0x3d, 0x58, 0x81, 0xdf, 0xc9, 0x75, 0xdf, 0xa7, 0xa2, 0x18, 0xbe, 0x4e, 0x56, - 0x90, 0x58, 0xae, 0xc6, 0x96, 0x39, 0x9c, 0xc8, 0x42, 0x24, 0x91, 0x8a, 0x05, 0x17, 0x33, 0x70, - 0x4f, 0x2c, 0x09, 0xca, 0x27, 0x70, 0x89, 0x71, 0x6c, 0x3a, 0xae, 0xb9, 0xe7, 0x3c, 0xc3, 0xf5, - 0xe9, 0x46, 0x08, 0x9d, 0x81, 0xf9, 0x4e, 0x97, 0xf4, 0x31, 0xd3, 0xbe, 0xa8, 0xf3, 0x83, 0xf2, - 0x8d, 0x04, 0x95, 0x6c, 0x58, 0xa1, 0xfe, 0x0b, 0x38, 0xdb, 0x08, 0x7e, 0x36, 0x92, 0xdd, 0xba, - 0x3a, 0xe6, 0x89, 0x8b, 0xa0, 0x32, 0xd0, 0xe5, 0x46, 0x92, 0x49, 0xf1, 0xe0, 0x4a, 0x8a, 0x0a, - 0xff, 0xa7, 0x1d, 0xd7, 0x73, 0xf6, 0xee, 0xb1, 0xa7, 0xfb, 0xf8, 0x8f, 0xfe, 0x28, 0xf8, 0xd9, - 0x70, 0xf0, 0xcf, 0x67, 0xe1, 0x6a, 0x1e, 0x5a, 0x91, 0x86, 0x1d, 0x38, 0x13, 0x4b, 0x43, 0x90, - 0x05, 0x29, 0xef, 0xcc, 0xa2, 0x46, 0x82, 0x09, 0xdd, 0x04, 0xe0, 0x4d, 0xc7, 0xc0, 0x78, 0x77, - 0xcb, 0x43, 0xb0, 0xe1, 0xd2, 0xec, 0x57, 0x55, 0xd6, 0x5a, 0x3a, 0x6f, 0x51, 0xe6, 0xfa, 0x00, - 0x5e, 0xe9, 0x9a, 0x4f, 0x8c, 0xd1, 0xfa, 0x65, 0xf1, 0x85, 0xbb, 0x2b, 0xb2, 0xaa, 0x7d, 0x0c, - 0xdd, 0x7c, 0xb2, 0x31, 0xbc, 0xd3, 0x5f, 0xee, 0x86, 0x8f, 0x68, 0x07, 0x90, 0xe5, 0xd9, 0x06, - 0xed, 0x59, 0x6d, 0x87, 0x52, 0x87, 0xb8, 0xc6, 0x2e, 0x1e, 0x94, 0xe6, 0x62, 0x98, 0xd1, 0x6f, - 0x83, 0x7e, 0x55, 0xfd, 0x78, 0x68, 0x7f, 0x1f, 0x0f, 0xf4, 0xd3, 0x96, 0x67, 0x47, 0x6e, 0xd0, - 0x16, 0xcb, 0x3e, 0x69, 0x94, 0xe6, 0x19, 0x52, 0x35, 0x3b, 0x53, 0x0f, 0x7d, 0xb3, 0x94, 0xa6, - 0xe1, 0xfe, 0xb5, 0x7f, 0x8a, 0x30, 0xcf, 0x0a, 0x86, 0x9e, 0x4b, 0xb0, 0xc0, 0x27, 0x04, 0x8d, - 0x69, 0xbf, 0xe4, 0x17, 0x83, 0xbc, 0x96, 0xd3, 0x9a, 0xd7, 0x5c, 0xd9, 0xfa, 0xfa, 0x8f, 0xbf, - 0xbf, 0x2b, 0xdc, 0x46, 0xb7, 0xb4, 0xcc, 0xaf, 0x95, 0x51, 0x27, 0x68, 0xfb, 0x41, 0x5f, 0x1e, - 0x68, 0x7c, 0x6c, 0xb5, 0x7d, 0xde, 0x80, 0x07, 0xe8, 0x7b, 0x09, 0x96, 0x86, 0x4b, 0x17, 0x69, - 0x13, 0x54, 0xc4, 0xf7, 0xbf, 0xfc, 0x6e, 0x7e, 0x07, 0xa1, 0x7c, 0x85, 0x29, 0x57, 0x50, 0x65, - 0x82, 0x72, 0x8a, 0x7e, 0x94, 0x00, 0x46, 0xb3, 0x88, 0x72, 0x51, 0x85, 0xdf, 0x1d, 0xb9, 0x3a, - 0x85, 0x87, 0x50, 0xb7, 0xc6, 0xd4, 0x5d, 0x46, 0x6f, 0x4d, 0x52, 0xc7, 0x12, 0x8b, 0x7e, 0x91, - 0xe0, 0xd5, 0xd8, 0x06, 0x45, 0xeb, 0x13, 0x58, 0xd3, 0x57, 0xb9, 0x7c, 0x7d, 0x5a, 0x37, 0xa1, - 0xf8, 0x1a, 0x53, 0xbc, 0x86, 0xde, 0xc9, 0x56, 0xcc, 0xc7, 0x38, 0xac, 0xfb, 0x67, 0x09, 0x8a, - 0xa1, 0xa5, 0x88, 0x26, 0x65, 0x2a, 0xb9, 0xbf, 0xe5, 0xda, 0x34, 0x2e, 0x42, 0xeb, 0x7b, 0x4c, - 0xab, 0x8a, 0x56, 0xb3, 0xb5, 0x8a, 0xb5, 0x12, 0x6a, 0x59, 0xf4, 0xbb, 0x04, 0xa7, 0xe3, 0x1b, - 0x0c, 0x5d, 0xcf, 0x41, 0x9f, 0xb2, 0x4a, 0xe5, 0x1b, 0x53, 0xfb, 0xe5, 0x9f, 0xb8, 0xa4, 0x76, - 0x9e, 0x7a, 0xaa, 0xed, 0x0f, 0xd7, 0xf7, 0x01, 0xfa, 0x55, 0x82, 0xe5, 0x94, 0xad, 0x86, 0x6e, - 0x4e, 0x50, 0x96, 0xbd, 0x60, 0xe5, 0xf7, 0x8f, 0xe3, 0x2a, 0xe2, 0xba, 0xc1, 0xe2, 0xaa, 0x22, - 0x2d, 0x3b, 0xae, 0xd4, 0x25, 0x8b, 0xfe, 0x95, 0xe0, 0xe2, 0xd8, 0x05, 0x85, 0x36, 0xa6, 0x92, - 0x95, 0xbe, 0x55, 0xe5, 0xbb, 0xff, 0x0f, 0x44, 0x44, 0xf9, 0x88, 0x45, 0x79, 0x1f, 0x6d, 0xe7, - 0x8e, 0x32, 0xe5, 0xe5, 0xf4, 0x11, 0x87, 0x2f, 0xe7, 0x9d, 0x8f, 0x7e, 0x3b, 0x2c, 0x4b, 0x2f, - 0x0e, 0xcb, 0xd2, 0x5f, 0x87, 0x65, 0xe9, 0xdb, 0xa3, 0xf2, 0xcc, 0x8b, 0xa3, 0xf2, 0xcc, 0x9f, - 0x47, 0xe5, 0x99, 0xcf, 0xd7, 0x9b, 0x8e, 0xd7, 0xea, 0x59, 0xaa, 0x4d, 0xda, 0x01, 0x1d, 0x83, - 0x19, 0x72, 0x3f, 0x8d, 0xb1, 0x7b, 0x83, 0x0e, 0xa6, 0xd6, 0x02, 0xfb, 0x8f, 0xf2, 0xda, 0x7f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x80, 0x0f, 0x7d, 0x88, 0x0f, 0x00, 0x00, + // 1176 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x6f, 0xdc, 0x44, + 0x14, 0x8e, 0xf3, 0xab, 0xc9, 0x5b, 0x0a, 0xd5, 0x24, 0x2d, 0x8b, 0xdb, 0x6e, 0x22, 0x43, 0x69, + 0x5a, 0x12, 0x9b, 0x4d, 0x49, 0xab, 0x72, 0xa0, 0x6a, 0x52, 0x92, 0x46, 0x45, 0xa5, 0x35, 0x04, + 0x24, 0x2e, 0xc6, 0xf6, 0xce, 0x7a, 0xad, 0x64, 0x3d, 0x5b, 0xdb, 0xbb, 0xed, 0x36, 0x84, 0x03, + 0xea, 0x1d, 0x24, 0x2e, 0x88, 0x13, 0x27, 0x0e, 0x1c, 0x7a, 0xe3, 0x4f, 0x40, 0xea, 0x81, 0x43, + 0x25, 0x2e, 0x9c, 0x10, 0x4a, 0xf8, 0x37, 0x90, 0x90, 0x67, 0xc6, 0xbb, 0xfe, 0xb9, 0xeb, 0x0d, + 0xb9, 0xed, 0x8c, 0xdf, 0xfb, 0xbe, 0xef, 0xbd, 0x79, 0x33, 0xef, 0x2d, 0xbc, 0x65, 0xe8, 0x46, + 0x77, 0x8f, 0x38, 0xca, 0x53, 0xe2, 0x60, 0x93, 0x38, 0xa6, 0x8d, 0x5d, 0x0b, 0x2b, 0x9d, 0xaa, + 0xf2, 0xa8, 0x8d, 0xdd, 0xae, 0xdc, 0x72, 0x89, 0x4f, 0x50, 0x99, 0x5b, 0xc9, 0x31, 0x2b, 0xb9, + 0x53, 0x15, 0xe7, 0x2d, 0x62, 0x11, 0x6a, 0xa4, 0x04, 0xbf, 0x98, 0xbd, 0x78, 0xc1, 0x22, 0xc4, + 0xda, 0xc3, 0x8a, 0xde, 0xb2, 0x15, 0xdd, 0x71, 0x88, 0xaf, 0xfb, 0x36, 0x71, 0x3c, 0xfe, 0xf5, + 0xaa, 0x49, 0xbc, 0x26, 0xf1, 0x14, 0x43, 0xf7, 0x30, 0xa3, 0x51, 0x3a, 0x55, 0x03, 0xfb, 0x7a, + 0x55, 0x69, 0xe9, 0x96, 0xed, 0x50, 0x63, 0x6e, 0xbb, 0x1c, 0xea, 0x33, 0x7c, 0xd3, 0x6c, 0x60, + 0x73, 0xb7, 0x45, 0x6c, 0xc7, 0x0f, 0xf4, 0xc5, 0x36, 0xb8, 0xf5, 0x95, 0xd0, 0xba, 0xff, 0xc5, + 0x76, 0xac, 0xc0, 0x3a, 0x65, 0x2a, 0x85, 0xa6, 0xb8, 0x45, 0xcc, 0x06, 0xb7, 0x0a, 0x7f, 0x27, + 0xc9, 0x53, 0xc9, 0x89, 0xe7, 0x81, 0x59, 0x5f, 0xca, 0xb5, 0x6e, 0xe9, 0xae, 0xde, 0xe4, 0xd1, + 0x4b, 0xf3, 0x80, 0x1e, 0x06, 0x31, 0x3f, 0xa0, 0x9b, 0x2a, 0x7e, 0xd4, 0xc6, 0x9e, 0x2f, 0xed, + 0xc0, 0x5c, 0x6c, 0xd7, 0x6b, 0x11, 0xc7, 0xc3, 0xe8, 0x03, 0x98, 0x66, 0xce, 0x65, 0x61, 0x51, + 0x58, 0x2a, 0xad, 0x2e, 0xca, 0x79, 0x27, 0x21, 0x33, 0xcf, 0xf5, 0xc9, 0x17, 0x7f, 0x2d, 0x8c, + 0xa9, 0xdc, 0x4b, 0xda, 0xe2, 0x64, 0x77, 0xb1, 0x5e, 0xc3, 0x2e, 0x27, 0x43, 0x6f, 0xc0, 0x8c, + 0xd9, 0xd0, 0x6d, 0x47, 0xb3, 0x6b, 0x14, 0x77, 0x56, 0x3d, 0x45, 0xd7, 0xdb, 0x35, 0x74, 0x0e, + 0xa6, 0x1b, 0xd8, 0xb6, 0x1a, 0x7e, 0x79, 0x7c, 0x51, 0x58, 0x9a, 0x54, 0xf9, 0x4a, 0xfa, 0x51, + 0xe0, 0x02, 0x43, 0x24, 0x2e, 0xf0, 0x56, 0x60, 0x1f, 0xec, 0x70, 0x81, 0x97, 0xf3, 0x05, 0x6e, + 0x3b, 0x35, 0xfc, 0x04, 0xd7, 0x38, 0x00, 0x77, 0x43, 0xeb, 0xf0, 0x4a, 0x9d, 0xb8, 0xbb, 0x1a, + 0x5b, 0x7a, 0x94, 0xb6, 0xb4, 0xba, 0x90, 0x0f, 0xb3, 0x49, 0xdc, 0x5d, 0x4f, 0x2d, 0x05, 0x4e, + 0x0c, 0xca, 0x93, 0x34, 0x38, 0x4b, 0xb5, 0x6d, 0x04, 0x41, 0x7c, 0x64, 0x7b, 0x7e, 0x18, 0xe8, + 0x26, 0x40, 0xbf, 0xa2, 0xb8, 0xc2, 0xb7, 0x65, 0x56, 0x7e, 0x72, 0x50, 0x7e, 0x32, 0xab, 0x72, + 0x5e, 0x7e, 0xf2, 0x03, 0xdd, 0xc2, 0xdc, 0x57, 0x8d, 0x78, 0x4a, 0x5f, 0xc3, 0xb9, 0x24, 0x01, + 0x8f, 0xff, 0x3c, 0xcc, 0x86, 0xa9, 0x0c, 0xce, 0x68, 0x62, 0x69, 0x56, 0x9d, 0xe1, 0xb9, 0xf4, + 0xd0, 0x56, 0x8c, 0x7e, 0x9c, 0x27, 0x68, 0x18, 0x3d, 0x43, 0x8e, 0xf1, 0xaf, 0x45, 0xf9, 0xbd, + 0x6d, 0xa7, 0x4e, 0xc2, 0x08, 0x07, 0xf1, 0x4b, 0x1a, 0xbc, 0x9e, 0x72, 0xe3, 0xba, 0xef, 0x40, + 0x89, 0x9a, 0x79, 0x9a, 0xed, 0xd4, 0x09, 0xf5, 0x2c, 0xad, 0xbe, 0x99, 0x9f, 0x75, 0x0a, 0x41, + 0x11, 0xc0, 0xec, 0xa1, 0x49, 0x9f, 0xc3, 0x79, 0x4a, 0xf0, 0x61, 0x70, 0x6f, 0x32, 0xc5, 0xd1, + 0x1b, 0xa5, 0x39, 0xed, 0x26, 0xcd, 0xfe, 0xa4, 0x3a, 0x43, 0x37, 0xee, 0xb7, 0x9b, 0x71, 0xe5, + 0xe3, 0x09, 0xe5, 0x35, 0xb8, 0x90, 0x0d, 0x7c, 0xa2, 0xf2, 0xbf, 0xe2, 0xf9, 0x09, 0x4e, 0x94, + 0xd7, 0x52, 0x81, 0x2b, 0xb2, 0x99, 0x71, 0xaa, 0xc7, 0x29, 0xaa, 0x9f, 0x05, 0x28, 0xa7, 0xe9, + 0x79, 0x80, 0xb7, 0xe1, 0x54, 0x78, 0x23, 0x58, 0x70, 0x85, 0x2f, 0x56, 0xe8, 0x77, 0x72, 0xd5, + 0xf7, 0x19, 0x3f, 0x8c, 0x40, 0x27, 0x3d, 0x90, 0x44, 0xae, 0x06, 0x1e, 0x73, 0x34, 0x91, 0xe3, + 0xb1, 0x44, 0x4a, 0x06, 0x5c, 0xcc, 0xc1, 0x3d, 0xb1, 0x24, 0x48, 0x9f, 0xc2, 0x02, 0xe5, 0xd8, + 0xb4, 0x1d, 0x7d, 0xcf, 0x7e, 0x8a, 0x6b, 0xa3, 0x5d, 0x21, 0x34, 0x0f, 0x53, 0x2d, 0x97, 0x74, + 0x30, 0xd5, 0x3e, 0xa3, 0xb2, 0x85, 0xf4, 0x4c, 0x80, 0xc5, 0x7c, 0x58, 0xae, 0xfe, 0x4b, 0x38, + 0x5b, 0x0f, 0x3f, 0x6b, 0xe9, 0x6a, 0x5d, 0x1e, 0xf0, 0xc4, 0xc5, 0x50, 0x29, 0xe8, 0x5c, 0x3d, + 0xcd, 0x24, 0xf9, 0x70, 0x25, 0x43, 0x45, 0xf0, 0x69, 0xc7, 0xf1, 0xed, 0xbd, 0xbb, 0xf4, 0xe9, + 0x3e, 0xfe, 0xa3, 0xdf, 0x0f, 0x7e, 0x22, 0x1a, 0xfc, 0xf3, 0x09, 0xb8, 0x5a, 0x84, 0x96, 0xa7, + 0x61, 0x07, 0xe6, 0x13, 0x69, 0x08, 0xb3, 0x20, 0x14, 0xbd, 0xb3, 0xa8, 0x9e, 0x62, 0x42, 0x37, + 0x01, 0x58, 0xd1, 0x51, 0x30, 0x56, 0xdd, 0x62, 0x0f, 0xac, 0xd7, 0xc8, 0x3b, 0x55, 0x99, 0x96, + 0x96, 0xca, 0x4a, 0x94, 0xba, 0xde, 0x87, 0x57, 0x5d, 0xfd, 0xb1, 0xd6, 0x1f, 0x09, 0x68, 0x7c, + 0xd1, 0xea, 0x8a, 0x8d, 0x0f, 0x01, 0x86, 0xaa, 0x3f, 0xde, 0xe8, 0xed, 0xa9, 0xa7, 0xdd, 0xe8, + 0x12, 0xed, 0x00, 0x32, 0x7c, 0x53, 0xf3, 0xda, 0x46, 0xd3, 0xf6, 0x3c, 0x9b, 0x38, 0xda, 0x2e, + 0xee, 0x96, 0x27, 0x13, 0x98, 0xf1, 0x79, 0xa5, 0x53, 0x95, 0x3f, 0xe9, 0xd9, 0xdf, 0xc3, 0x5d, + 0xf5, 0x8c, 0xe1, 0x9b, 0xb1, 0x1d, 0xb4, 0x45, 0xb3, 0x4f, 0xea, 0xe5, 0x29, 0x8a, 0x54, 0x1d, + 0xd0, 0xfa, 0x03, 0xb3, 0x8c, 0xa2, 0x61, 0xfe, 0xab, 0xcf, 0x4e, 0xc3, 0x14, 0x3d, 0x30, 0xf4, + 0xad, 0x00, 0xd3, 0x6c, 0x4e, 0x40, 0x03, 0xca, 0x2f, 0x3d, 0x9e, 0x88, 0x2b, 0x05, 0xad, 0xd9, + 0x99, 0x4b, 0x4b, 0xdf, 0xfc, 0xf1, 0xcf, 0xf7, 0xe3, 0x12, 0x5a, 0x54, 0x86, 0xcc, 0x44, 0xe8, + 0xb9, 0x00, 0xd3, 0xec, 0xce, 0x0e, 0x55, 0x14, 0x9b, 0x61, 0x86, 0x2a, 0x8a, 0xcf, 0x29, 0xd2, + 0x16, 0x55, 0x74, 0x1b, 0xdd, 0xca, 0x57, 0xd4, 0xaf, 0x4d, 0x65, 0x3f, 0xbc, 0x29, 0x07, 0x0a, + 0x7b, 0x48, 0x94, 0x7d, 0x76, 0x25, 0x0e, 0xd0, 0x0f, 0x02, 0xcc, 0xf6, 0xc6, 0x00, 0xa4, 0x0c, + 0x51, 0x91, 0x9c, 0x48, 0xc4, 0x77, 0x8b, 0x3b, 0x14, 0xcf, 0x25, 0x7b, 0x5c, 0xd0, 0x4f, 0x02, + 0x40, 0xff, 0x75, 0x40, 0x85, 0xa8, 0xa2, 0x2f, 0xa1, 0x58, 0x1d, 0xc1, 0x83, 0xab, 0x5b, 0xa1, + 0xea, 0x2e, 0xa3, 0x4b, 0xc3, 0xd4, 0xd1, 0xc4, 0xa2, 0x5f, 0x05, 0x78, 0x2d, 0xd1, 0xd3, 0xd1, + 0xda, 0x10, 0xd6, 0xec, 0xe1, 0x42, 0xbc, 0x3e, 0xaa, 0x1b, 0x57, 0x7c, 0x8d, 0x2a, 0x5e, 0x41, + 0xef, 0xe4, 0x2b, 0x66, 0x0f, 0x4b, 0x54, 0xf7, 0x2f, 0x02, 0x94, 0x22, 0x6d, 0x1a, 0x0d, 0xcb, + 0x54, 0x7a, 0xa2, 0x10, 0x57, 0x47, 0x71, 0xe1, 0x5a, 0xdf, 0xa3, 0x5a, 0x65, 0xb4, 0x9c, 0xaf, + 0x95, 0x37, 0xba, 0x48, 0xc9, 0xa2, 0xdf, 0x05, 0x38, 0x93, 0xec, 0xa9, 0xe8, 0x7a, 0x01, 0xfa, + 0x8c, 0xe6, 0x2e, 0xde, 0x18, 0xd9, 0xaf, 0xf8, 0x8d, 0x4b, 0x6b, 0x67, 0xa9, 0xf7, 0x94, 0xfd, + 0xde, 0x40, 0x71, 0x80, 0x7e, 0x13, 0x60, 0x2e, 0xa3, 0xcf, 0xa2, 0x9b, 0x43, 0x94, 0xe5, 0xb7, + 0x7c, 0xf1, 0xfd, 0xe3, 0xb8, 0xf2, 0xb8, 0x6e, 0xd0, 0xb8, 0xaa, 0x48, 0xc9, 0x8f, 0x2b, 0xb3, + 0xed, 0xa3, 0x7f, 0x05, 0xb8, 0x38, 0xb0, 0x65, 0xa2, 0x8d, 0x91, 0x64, 0x65, 0xf7, 0x79, 0xf1, + 0xce, 0xff, 0x03, 0xe1, 0x51, 0x3e, 0xa4, 0x51, 0xde, 0x43, 0xdb, 0x85, 0xa3, 0xcc, 0x78, 0x39, + 0x03, 0xc4, 0xde, 0xcb, 0xb9, 0xfe, 0xf1, 0x8b, 0xc3, 0x8a, 0xf0, 0xf2, 0xb0, 0x22, 0xfc, 0x7d, + 0x58, 0x11, 0xbe, 0x3b, 0xaa, 0x8c, 0xbd, 0x3c, 0xaa, 0x8c, 0xfd, 0x79, 0x54, 0x19, 0xfb, 0x62, + 0xcd, 0xb2, 0xfd, 0x46, 0xdb, 0x90, 0x4d, 0xd2, 0x0c, 0xe9, 0x28, 0x4c, 0x8f, 0xfb, 0x49, 0x82, + 0xdd, 0xef, 0xb6, 0xb0, 0x67, 0x4c, 0xd3, 0x3f, 0xd4, 0xd7, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, + 0x03, 0xf9, 0xf2, 0x6b, 0xc4, 0x10, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1010,6 +1101,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { + // Params queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) // Header queries the CZ header and fork headers at a given height. Header(ctx context.Context, in *QueryHeaderRequest, opts ...grpc.CallOption) (*QueryHeaderResponse, error) // ChainList queries the list of chains that checkpoint to Babylon @@ -1040,6 +1133,15 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.zoneconcierge.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) Header(ctx context.Context, in *QueryHeaderRequest, opts ...grpc.CallOption) (*QueryHeaderResponse, error) { out := new(QueryHeaderResponse) err := c.cc.Invoke(ctx, "/babylon.zoneconcierge.v1.Query/Header", in, out, opts...) @@ -1114,6 +1216,8 @@ func (c *queryClient) FinalizedChainInfoUntilHeight(ctx context.Context, in *Que // QueryServer is the server API for Query service. type QueryServer interface { + // Params queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) // Header queries the CZ header and fork headers at a given height. Header(context.Context, *QueryHeaderRequest) (*QueryHeaderResponse, error) // ChainList queries the list of chains that checkpoint to Babylon @@ -1140,6 +1244,9 @@ type QueryServer interface { type UnimplementedQueryServer struct { } +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} func (*UnimplementedQueryServer) Header(ctx context.Context, req *QueryHeaderRequest) (*QueryHeaderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Header not implemented") } @@ -1169,6 +1276,24 @@ func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) } +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.zoneconcierge.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_Header_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryHeaderRequest) if err := dec(in); err != nil { @@ -1317,6 +1442,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "babylon.zoneconcierge.v1.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, { MethodName: "Header", Handler: _Query_Header_Handler, @@ -1354,6 +1483,62 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Metadata: "babylon/zoneconcierge/v1/query.proto", } +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *QueryHeaderRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2039,6 +2224,26 @@ func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + func (m *QueryHeaderRequest) Size() (n int) { if m == nil { return 0 @@ -2321,6 +2526,139 @@ func sovQuery(x uint64) (n int) { func sozQuery(x uint64) (n int) { return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryHeaderRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/zoneconcierge/types/query.pb.gw.go b/x/zoneconcierge/types/query.pb.gw.go index 607477240..fc22099f6 100644 --- a/x/zoneconcierge/types/query.pb.gw.go +++ b/x/zoneconcierge/types/query.pb.gw.go @@ -33,6 +33,24 @@ var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_Header_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryHeaderRequest var metadata runtime.ServerMetadata @@ -501,6 +519,29 @@ func local_request_Query_FinalizedChainInfoUntilHeight_0(ctx context.Context, ma // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Header_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -726,6 +767,26 @@ func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc // "QueryClient" to call the correct interceptors. func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Header_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -890,6 +951,8 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "zoneconcierge", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Header_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"babylon", "zoneconcierge", "v1", "chain_info", "chain_id", "header", "height"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_ChainList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"babylon", "zoneconcierge", "v1", "chains"}, "", runtime.AssumeColonVerbOpt(false))) @@ -908,6 +971,8 @@ var ( ) var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + forward_Query_Header_0 = runtime.ForwardResponseMessage forward_Query_ChainList_0 = runtime.ForwardResponseMessage diff --git a/x/zoneconcierge/types/tx.pb.go b/x/zoneconcierge/types/tx.pb.go index 2c52ff6b3..c42d065d3 100644 --- a/x/zoneconcierge/types/tx.pb.go +++ b/x/zoneconcierge/types/tx.pb.go @@ -6,10 +6,17 @@ package types import ( context "context" fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -23,19 +30,133 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// MsgUpdateParams defines a message for updating zoneconcierge module parameters. +type MsgUpdateParams struct { + // authority is the address of the governance account. + // just FYI: cosmos.AddressString marks that this field should use type alias + // for AddressString instead of string, but the functionality is not yet implemented + // in cosmos-proto + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the epoching paramaeters parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_35e2112d987e4e18, []int{0} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse is the response to the MsgUpdateParams message. +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_35e2112d987e4e18, []int{1} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUpdateParams)(nil), "babylon.zoneconcierge.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "babylon.zoneconcierge.v1.MsgUpdateParamsResponse") +} + func init() { proto.RegisterFile("babylon/zoneconcierge/v1/tx.proto", fileDescriptor_35e2112d987e4e18) } var fileDescriptor_35e2112d987e4e18 = []byte{ - // 140 bytes of a gzipped FileDescriptorProto + // 324 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0xaf, 0xca, 0xcf, 0x4b, 0x4d, 0xce, 0xcf, 0x4b, 0xce, 0x4c, 0x2d, 0x4a, 0x4f, 0xd5, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, - 0x2a, 0xd1, 0x43, 0x51, 0xa2, 0x57, 0x66, 0x68, 0xc4, 0xca, 0xc5, 0xec, 0x5b, 0x9c, 0xee, 0xe4, - 0x7f, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, - 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xa6, 0xe9, 0x99, 0x25, 0x19, - 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x50, 0x53, 0x92, 0x33, 0x12, 0x33, 0xf3, 0x60, 0x1c, - 0xfd, 0x0a, 0x34, 0x7b, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x16, 0x1b, 0x03, 0x02, - 0x00, 0x00, 0xff, 0xff, 0xd3, 0x96, 0xa5, 0xaf, 0x9d, 0x00, 0x00, 0x00, + 0x2a, 0xd1, 0x43, 0x51, 0xa2, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa4, + 0x0f, 0x62, 0x41, 0xd4, 0x4b, 0x49, 0x26, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xc7, 0x43, 0x24, 0x20, + 0x1c, 0xa8, 0x94, 0x38, 0x84, 0xa7, 0x9f, 0x5b, 0x9c, 0x0e, 0xb2, 0x22, 0xb7, 0x38, 0x1d, 0x2a, + 0xa1, 0x8a, 0xd3, 0x19, 0x05, 0x89, 0x45, 0x89, 0xb9, 0x50, 0xfd, 0x4a, 0x33, 0x19, 0xb9, 0xf8, + 0x7d, 0x8b, 0xd3, 0x43, 0x0b, 0x52, 0x12, 0x4b, 0x52, 0x03, 0xc0, 0x32, 0x42, 0x66, 0x5c, 0x9c, + 0x89, 0xa5, 0x25, 0x19, 0xf9, 0x45, 0x99, 0x25, 0x95, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x4e, + 0x12, 0x97, 0xb6, 0xe8, 0x8a, 0x40, 0x2d, 0x76, 0x4c, 0x49, 0x29, 0x4a, 0x2d, 0x2e, 0x0e, 0x2e, + 0x29, 0xca, 0xcc, 0x4b, 0x0f, 0x42, 0x28, 0x15, 0xb2, 0xe3, 0x62, 0x83, 0x98, 0x2d, 0xc1, 0xa4, + 0xc0, 0xa8, 0xc1, 0x6d, 0xa4, 0xa0, 0x87, 0xcb, 0x9f, 0x7a, 0x10, 0x9b, 0x9c, 0x58, 0x4e, 0xdc, + 0x93, 0x67, 0x08, 0x82, 0xea, 0xb2, 0xe2, 0x6b, 0x7a, 0xbe, 0x41, 0x0b, 0x61, 0x9e, 0x92, 0x24, + 0x97, 0x38, 0x9a, 0xd3, 0x82, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x8d, 0x8a, 0xb9, 0x98, + 0x7d, 0x8b, 0xd3, 0x85, 0x72, 0xb8, 0x78, 0x50, 0x5c, 0xae, 0x89, 0xdb, 0x46, 0x34, 0x93, 0xa4, + 0x0c, 0x89, 0x56, 0x0a, 0xb3, 0xd4, 0xc9, 0xff, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, + 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, + 0x18, 0xa2, 0x4c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xa1, 0xc6, + 0x26, 0x67, 0x24, 0x66, 0xe6, 0xc1, 0x38, 0xfa, 0x15, 0x68, 0xd1, 0x50, 0x52, 0x59, 0x90, 0x5a, + 0x9c, 0xc4, 0x06, 0x8e, 0x03, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5a, 0xdf, 0x0c, 0x09, + 0x33, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -50,6 +171,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { + // UpdateParams updates the btccheckpoint module parameters. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) } type msgClient struct { @@ -60,22 +183,414 @@ func NewMsgClient(cc grpc1.ClientConn) MsgClient { return &msgClient{cc} } +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/babylon.zoneconcierge.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { + // UpdateParams updates the btccheckpoint module parameters. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. type UnimplementedMsgServer struct { } +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) } +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/babylon.zoneconcierge.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "babylon.zoneconcierge.v1.Msg", HandlerType: (*MsgServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{}, - Metadata: "babylon/zoneconcierge/v1/tx.proto", + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "babylon/zoneconcierge/v1/tx.proto", +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) From af59704a79b43bd35279a2008ccef95c1bc4b369 Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Thu, 25 May 2023 10:41:23 +1000 Subject: [PATCH 9/9] zoneconcierge: adding header timestamp to IndexedHeader (#387) --- client/docs/swagger-ui/swagger.yaml | 299 ++++++++++++++++++ .../zoneconcierge/v1/zoneconcierge.proto | 12 +- .../extended-client-keeper/hooks.go | 3 + .../extended-client-keeper/keeper.go | 1 + x/zoneconcierge/keeper/hooks.go | 1 + x/zoneconcierge/types/zoneconcierge.pb.go | 192 +++++++---- 6 files changed, 444 insertions(+), 64 deletions(-) diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index a9d62f364..14f146ca2 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -5737,6 +5737,16 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -5858,6 +5868,17 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block @@ -6535,6 +6556,17 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block @@ -6659,6 +6691,18 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ + ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon @@ -7053,6 +7097,17 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block @@ -7177,6 +7232,18 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ + ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon @@ -7579,6 +7646,17 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -7702,6 +7780,18 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ + ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon @@ -8588,6 +8678,18 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ + ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon @@ -8714,6 +8816,18 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ + ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the @@ -9589,6 +9703,16 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -10010,6 +10134,16 @@ paths: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -15530,6 +15664,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes @@ -15650,6 +15791,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -15820,6 +15971,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -15941,6 +16102,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -16575,6 +16746,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes @@ -16717,6 +16895,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes this @@ -17205,6 +17390,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -17326,6 +17521,17 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -17506,6 +17712,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -17627,6 +17843,17 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -17808,6 +18035,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -17929,6 +18166,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -18577,6 +18824,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -18700,6 +18957,17 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is + BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block @@ -19340,6 +19608,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes @@ -19460,6 +19735,16 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: >- + time is the timestamp of this header on CZ ledger + + it is needed for CZ to unbond all mature + validators/delegations + + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that @@ -19612,6 +19897,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes @@ -19738,6 +20030,13 @@ definitions: (hash, height) jointly provides the position of the header on CZ ledger + time: + type: string + format: date-time + title: |- + time is the timestamp of this header on CZ ledger + it is needed for CZ to unbond all mature validators/delegations + before this timestamp when this header is BTC-finalised babylon_header: title: >- babylon_header is the header of the babylon block that includes diff --git a/proto/babylon/zoneconcierge/v1/zoneconcierge.proto b/proto/babylon/zoneconcierge/v1/zoneconcierge.proto index c19a34bba..53fa995a8 100644 --- a/proto/babylon/zoneconcierge/v1/zoneconcierge.proto +++ b/proto/babylon/zoneconcierge/v1/zoneconcierge.proto @@ -1,6 +1,8 @@ syntax = "proto3"; package babylon.zoneconcierge.v1; +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; import "tendermint/types/types.proto"; import "tendermint/crypto/proof.proto"; import "babylon/btccheckpoint/v1/btccheckpoint.proto"; @@ -19,15 +21,19 @@ message IndexedHeader { // height is the height of this header on CZ ledger // (hash, height) jointly provides the position of the header on CZ ledger uint64 height = 3; + // time is the timestamp of this header on CZ ledger + // it is needed for CZ to unbond all mature validators/delegations + // before this timestamp when this header is BTC-finalised + google.protobuf.Timestamp time = 4 [ (gogoproto.stdtime) = true ]; // babylon_header is the header of the babylon block that includes this CZ // header - tendermint.types.Header babylon_header = 4; + tendermint.types.Header babylon_header = 5; // epoch is the epoch number of this header on Babylon ledger - uint64 babylon_epoch = 5; + uint64 babylon_epoch = 6; // babylon_tx_hash is the hash of the tx that includes this header // (babylon_block_height, babylon_tx_hash) jointly provides the position of // the header on Babylon ledger - bytes babylon_tx_hash = 6; + bytes babylon_tx_hash = 7; } // Forks is a list of non-canonical `IndexedHeader`s at the same height. diff --git a/x/zoneconcierge/extended-client-keeper/hooks.go b/x/zoneconcierge/extended-client-keeper/hooks.go index 9c0367277..516e7ecac 100644 --- a/x/zoneconcierge/extended-client-keeper/hooks.go +++ b/x/zoneconcierge/extended-client-keeper/hooks.go @@ -1,6 +1,8 @@ package extended_client_keeper import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -13,6 +15,7 @@ type HeaderInfo struct { Hash []byte ChaindId string Height uint64 + Time time.Time } // MultiClientHooks is a concrete implementation of ClientHooks diff --git a/x/zoneconcierge/extended-client-keeper/keeper.go b/x/zoneconcierge/extended-client-keeper/keeper.go index 4c59119e9..68474423c 100644 --- a/x/zoneconcierge/extended-client-keeper/keeper.go +++ b/x/zoneconcierge/extended-client-keeper/keeper.go @@ -34,6 +34,7 @@ func GetHeaderInfo(ctx sdk.Context, m exported.ClientMessage) *HeaderInfo { Hash: msg.Header.LastCommitHash, ChaindId: msg.Header.ChainID, Height: uint64(msg.Header.Height), + Time: msg.Header.Time, } default: return nil diff --git a/x/zoneconcierge/keeper/hooks.go b/x/zoneconcierge/keeper/hooks.go index ff6335eae..82d573955 100644 --- a/x/zoneconcierge/keeper/hooks.go +++ b/x/zoneconcierge/keeper/hooks.go @@ -29,6 +29,7 @@ func (h Hooks) AfterHeaderWithValidCommit(ctx sdk.Context, txHash []byte, header ChainId: header.ChaindId, Hash: header.Hash, Height: header.Height, + Time: &header.Time, BabylonHeader: &babylonHeader, BabylonEpoch: h.k.GetEpoch(ctx).EpochNumber, BabylonTxHash: txHash, diff --git a/x/zoneconcierge/types/zoneconcierge.pb.go b/x/zoneconcierge/types/zoneconcierge.pb.go index f9e732c74..c5466c08b 100644 --- a/x/zoneconcierge/types/zoneconcierge.pb.go +++ b/x/zoneconcierge/types/zoneconcierge.pb.go @@ -10,16 +10,21 @@ import ( types1 "github.com/babylonchain/babylon/x/epoching/types" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" types "github.com/cometbft/cometbft/proto/tendermint/types" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/protobuf/types/known/timestamppb" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -36,15 +41,19 @@ type IndexedHeader struct { // height is the height of this header on CZ ledger // (hash, height) jointly provides the position of the header on CZ ledger Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + // time is the timestamp of this header on CZ ledger + // it is needed for CZ to unbond all mature validators/delegations + // before this timestamp when this header is BTC-finalised + Time *time.Time `protobuf:"bytes,4,opt,name=time,proto3,stdtime" json:"time,omitempty"` // babylon_header is the header of the babylon block that includes this CZ // header - BabylonHeader *types.Header `protobuf:"bytes,4,opt,name=babylon_header,json=babylonHeader,proto3" json:"babylon_header,omitempty"` + BabylonHeader *types.Header `protobuf:"bytes,5,opt,name=babylon_header,json=babylonHeader,proto3" json:"babylon_header,omitempty"` // epoch is the epoch number of this header on Babylon ledger - BabylonEpoch uint64 `protobuf:"varint,5,opt,name=babylon_epoch,json=babylonEpoch,proto3" json:"babylon_epoch,omitempty"` + BabylonEpoch uint64 `protobuf:"varint,6,opt,name=babylon_epoch,json=babylonEpoch,proto3" json:"babylon_epoch,omitempty"` // babylon_tx_hash is the hash of the tx that includes this header // (babylon_block_height, babylon_tx_hash) jointly provides the position of // the header on Babylon ledger - BabylonTxHash []byte `protobuf:"bytes,6,opt,name=babylon_tx_hash,json=babylonTxHash,proto3" json:"babylon_tx_hash,omitempty"` + BabylonTxHash []byte `protobuf:"bytes,7,opt,name=babylon_tx_hash,json=babylonTxHash,proto3" json:"babylon_tx_hash,omitempty"` } func (m *IndexedHeader) Reset() { *m = IndexedHeader{} } @@ -101,6 +110,13 @@ func (m *IndexedHeader) GetHeight() uint64 { return 0 } +func (m *IndexedHeader) GetTime() *time.Time { + if m != nil { + return m.Time + } + return nil +} + func (m *IndexedHeader) GetBabylonHeader() *types.Header { if m != nil { return m.BabylonHeader @@ -519,60 +535,64 @@ func init() { } var fileDescriptor_ab886e1868e5c5cd = []byte{ - // 848 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdd, 0x6e, 0x1b, 0x45, - 0x14, 0xce, 0xc6, 0xf9, 0x21, 0xe3, 0x38, 0x84, 0x29, 0xd0, 0x4d, 0x00, 0x63, 0xb9, 0x52, 0x71, - 0x11, 0xac, 0xe5, 0x20, 0x2e, 0xe0, 0x06, 0x91, 0xd0, 0xd2, 0xb4, 0x88, 0xa2, 0x89, 0x5b, 0x10, - 0x12, 0x5a, 0xcd, 0xee, 0x8e, 0xbd, 0x23, 0xaf, 0x67, 0x56, 0x3b, 0x13, 0xd7, 0xee, 0x53, 0xf0, - 0x2a, 0xbc, 0x05, 0x97, 0xbd, 0xe4, 0x12, 0x25, 0xf0, 0x06, 0xdc, 0x70, 0x87, 0xf6, 0xcc, 0xec, - 0x7a, 0x5d, 0x6b, 0x69, 0x6e, 0x2c, 0xcf, 0xcc, 0x77, 0xbe, 0xf3, 0xcd, 0x77, 0xce, 0x99, 0x45, - 0x9f, 0x04, 0x34, 0x58, 0x24, 0x52, 0xf4, 0x5f, 0x48, 0xc1, 0x42, 0x29, 0x42, 0xce, 0xb2, 0x31, - 0xeb, 0xcf, 0x06, 0xab, 0x1b, 0x5e, 0x9a, 0x49, 0x2d, 0xb1, 0x6b, 0xd1, 0xde, 0xea, 0xe1, 0x6c, - 0x70, 0xfc, 0xbe, 0x66, 0x22, 0x62, 0xd9, 0x94, 0x0b, 0xdd, 0xd7, 0x8b, 0x94, 0x29, 0xf3, 0x6b, - 0xe2, 0x8e, 0x3f, 0xa8, 0x9c, 0x86, 0xd9, 0x22, 0xd5, 0xb2, 0x9f, 0x66, 0x52, 0x8e, 0xec, 0x71, - 0x29, 0x22, 0xd0, 0x61, 0x18, 0xb3, 0x70, 0x92, 0xca, 0x1c, 0x39, 0x1b, 0xac, 0x6e, 0x58, 0xf4, - 0xdd, 0x02, 0xbd, 0x3c, 0xe1, 0x62, 0x0c, 0xe8, 0x44, 0xf9, 0x13, 0xb6, 0xb0, 0xb8, 0x7b, 0xb5, - 0xb8, 0x35, 0xca, 0x6e, 0x01, 0x65, 0xa9, 0x0c, 0x63, 0x8b, 0x2a, 0xfe, 0x1b, 0x4c, 0xf7, 0x2f, - 0x07, 0xb5, 0xce, 0x45, 0xc4, 0xe6, 0x2c, 0x7a, 0xc8, 0x68, 0xc4, 0x32, 0x7c, 0x84, 0xde, 0x08, - 0x63, 0xca, 0x85, 0xcf, 0x23, 0xd7, 0xe9, 0x38, 0xbd, 0x3d, 0xb2, 0x0b, 0xeb, 0xf3, 0x08, 0x63, - 0xb4, 0x15, 0x53, 0x15, 0xbb, 0x9b, 0x1d, 0xa7, 0xb7, 0x4f, 0xe0, 0x3f, 0x7e, 0x17, 0xed, 0xc4, - 0x8c, 0x8f, 0x63, 0xed, 0x36, 0x3a, 0x4e, 0x6f, 0x8b, 0xd8, 0x15, 0xfe, 0x0a, 0x1d, 0xd8, 0xf4, - 0x7e, 0x0c, 0xc4, 0xee, 0x56, 0xc7, 0xe9, 0x35, 0x4f, 0x5c, 0x6f, 0xe9, 0x9a, 0x67, 0xdc, 0x34, - 0x89, 0x49, 0xcb, 0xe2, 0xad, 0x8e, 0x3b, 0xa8, 0xd8, 0xf0, 0x41, 0xb3, 0xbb, 0x0d, 0xfc, 0xfb, - 0x76, 0xf3, 0x7e, 0xbe, 0x87, 0xef, 0xa2, 0x37, 0x0b, 0x90, 0x9e, 0xfb, 0x20, 0x6e, 0x07, 0xc4, - 0x15, 0xb1, 0xc3, 0xf9, 0x43, 0xaa, 0xe2, 0xee, 0x23, 0xb4, 0xfd, 0x40, 0x66, 0x13, 0x85, 0xbf, - 0x46, 0xbb, 0x46, 0x8e, 0x72, 0x1b, 0x9d, 0x46, 0xaf, 0x79, 0xf2, 0x91, 0x57, 0x57, 0x7d, 0x6f, - 0xc5, 0x17, 0x52, 0xc4, 0x75, 0xff, 0x71, 0xd0, 0xde, 0x19, 0x38, 0x22, 0x46, 0xf2, 0xff, 0xec, - 0xfa, 0x0e, 0xb5, 0x12, 0xaa, 0x99, 0xd2, 0x85, 0x03, 0x9b, 0xe0, 0xc0, 0x8d, 0x33, 0xee, 0x9b, - 0x68, 0xeb, 0xc7, 0x29, 0xb2, 0x6b, 0x7f, 0x94, 0xdf, 0x04, 0xec, 0x6e, 0x9e, 0x7c, 0x58, 0x4f, - 0x06, 0x17, 0x26, 0x4d, 0x13, 0x64, 0x6e, 0xff, 0x25, 0x3a, 0xd2, 0x7c, 0xca, 0x94, 0xa6, 0xd3, - 0x94, 0x45, 0x56, 0x96, 0xf2, 0x43, 0x79, 0x29, 0x34, 0xd4, 0x67, 0x8b, 0xdc, 0xae, 0x00, 0x4c, - 0x66, 0x75, 0x96, 0x1f, 0x77, 0x7f, 0x6b, 0x20, 0xfc, 0x80, 0x0b, 0x9a, 0xf0, 0x17, 0x2c, 0xba, - 0xd1, 0xfd, 0x9f, 0xa2, 0xb7, 0x47, 0x45, 0x80, 0x6f, 0x41, 0x62, 0x24, 0xad, 0x0d, 0x77, 0xea, - 0x95, 0x97, 0xec, 0x04, 0x8f, 0xd6, 0x33, 0x7e, 0x81, 0x10, 0x34, 0x84, 0x21, 0x33, 0x36, 0x1c, - 0x97, 0x64, 0x65, 0x7f, 0xcf, 0x06, 0x1e, 0xf4, 0x08, 0xd9, 0x83, 0x2d, 0x08, 0xfd, 0x1e, 0x1d, - 0x64, 0xf4, 0xb9, 0xbf, 0x9c, 0x14, 0xdb, 0x94, 0xcb, 0x92, 0xac, 0x4c, 0x55, 0xce, 0x41, 0xe8, - 0xf3, 0xb3, 0x72, 0x8f, 0xb4, 0xb2, 0xea, 0x12, 0x3f, 0x45, 0x38, 0xd0, 0xa1, 0xaf, 0x2e, 0x83, - 0x29, 0x57, 0x8a, 0x4b, 0x91, 0x0f, 0x2a, 0x34, 0x6a, 0x95, 0x73, 0x75, 0xdc, 0x67, 0x03, 0xef, - 0xa2, 0xc4, 0x3f, 0x66, 0x0b, 0x72, 0x18, 0xe8, 0x70, 0x65, 0x07, 0x7f, 0x8b, 0xb6, 0xe1, 0x21, - 0x81, 0x5e, 0x6e, 0x9e, 0x0c, 0xea, 0x9d, 0xfa, 0x21, 0x87, 0xad, 0x57, 0x85, 0x98, 0xf8, 0xee, - 0xbf, 0x0e, 0x3a, 0x04, 0x08, 0x38, 0x71, 0xc1, 0x68, 0xc2, 0x22, 0x4c, 0x50, 0x6b, 0x46, 0x13, - 0x1e, 0x51, 0x2d, 0x33, 0x5f, 0x31, 0xed, 0x3a, 0x30, 0x08, 0x9f, 0xd6, 0x7b, 0xf0, 0xac, 0x80, - 0xff, 0xc8, 0x75, 0x7c, 0x9a, 0xa8, 0x5c, 0xf5, 0x7e, 0xc9, 0x71, 0xc1, 0x34, 0xbe, 0x8f, 0x0e, - 0x21, 0xa3, 0x5f, 0xa9, 0x8c, 0x29, 0xf3, 0x7b, 0xd5, 0x79, 0x37, 0xaf, 0xa4, 0x51, 0xfd, 0x24, - 0x55, 0xe4, 0x20, 0x2d, 0xc5, 0x41, 0x7d, 0x1e, 0xa1, 0x5b, 0x55, 0x9a, 0x19, 0x4d, 0x40, 0x60, - 0xe3, 0xf5, 0x4c, 0x87, 0x4b, 0xa6, 0x67, 0x34, 0xb9, 0x60, 0xba, 0xfb, 0xf7, 0x26, 0xba, 0x5d, - 0x63, 0x0f, 0xfe, 0x06, 0xbd, 0x65, 0xf2, 0xe8, 0xb9, 0xcf, 0x85, 0x1f, 0x24, 0x32, 0x9c, 0xd8, - 0x56, 0x38, 0x5a, 0x7f, 0x9f, 0x86, 0x73, 0xe0, 0xb1, 0x6a, 0x87, 0xf3, 0x73, 0x71, 0x9a, 0x07, - 0xe0, 0xc7, 0xe8, 0x1d, 0xc3, 0x62, 0xe6, 0x28, 0x67, 0x5a, 0xbe, 0x54, 0xaf, 0xbc, 0x74, 0x55, - 0xbd, 0x04, 0x43, 0x98, 0x99, 0xae, 0x73, 0xfb, 0x92, 0xfd, 0x84, 0x70, 0xf5, 0xea, 0x0a, 0x6a, - 0x65, 0x1b, 0xe0, 0xe3, 0xd7, 0x34, 0x40, 0xa5, 0xba, 0x55, 0x23, 0x6c, 0xbd, 0x7f, 0x29, 0x64, - 0x5a, 0xe6, 0xbc, 0xd5, 0xb4, 0x66, 0x91, 0xbb, 0x0b, 0x75, 0xbf, 0x57, 0xdf, 0xa7, 0xc3, 0x8c, - 0x0a, 0x45, 0x43, 0xcd, 0xa5, 0xe9, 0xaa, 0x5b, 0x15, 0xee, 0x82, 0xe5, 0xf4, 0xc9, 0xef, 0x57, - 0x6d, 0xe7, 0xe5, 0x55, 0xdb, 0xf9, 0xf3, 0xaa, 0xed, 0xfc, 0x7a, 0xdd, 0xde, 0x78, 0x79, 0xdd, - 0xde, 0xf8, 0xe3, 0xba, 0xbd, 0xf1, 0xf3, 0xe7, 0x63, 0xae, 0xe3, 0xcb, 0xc0, 0x0b, 0xe5, 0xb4, - 0x6f, 0x73, 0xc0, 0x2b, 0x50, 0x2c, 0xfa, 0xf3, 0x57, 0xbe, 0xcf, 0x60, 0x77, 0xb0, 0x03, 0x5f, - 0xa6, 0xcf, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xe5, 0x71, 0xc2, 0xc5, 0x07, 0x00, 0x00, + // 901 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xcf, 0x6e, 0x1b, 0x45, + 0x1c, 0xce, 0xc6, 0x4e, 0x42, 0xc6, 0x71, 0x08, 0xd3, 0x42, 0x37, 0x01, 0x1c, 0xcb, 0x95, 0x8a, + 0x8b, 0x60, 0x2d, 0x07, 0x38, 0xc0, 0x05, 0xe1, 0xd0, 0xd2, 0xb4, 0x88, 0xa2, 0x89, 0x5b, 0x10, + 0x12, 0x5a, 0xcd, 0xee, 0x8e, 0xbd, 0xa3, 0xac, 0x67, 0xac, 0x9d, 0x89, 0x6b, 0xf7, 0x29, 0x7a, + 0xe6, 0x2d, 0x78, 0x0b, 0x8e, 0x3d, 0x72, 0x03, 0x25, 0xe2, 0x0d, 0xb8, 0x70, 0x43, 0xfb, 0x9b, + 0x19, 0x7b, 0x4d, 0x70, 0x9b, 0x4b, 0xb4, 0x33, 0xf3, 0xfd, 0xbe, 0xf9, 0xe6, 0xfb, 0xfd, 0x89, + 0xd1, 0x47, 0x11, 0x8d, 0x66, 0x99, 0x14, 0x9d, 0xe7, 0x52, 0xb0, 0x58, 0x8a, 0x98, 0xb3, 0x7c, + 0xc8, 0x3a, 0x93, 0xee, 0xf2, 0x46, 0x30, 0xce, 0xa5, 0x96, 0xd8, 0xb7, 0xe8, 0x60, 0xf9, 0x70, + 0xd2, 0x3d, 0xb8, 0x39, 0x94, 0x43, 0x09, 0xa0, 0x4e, 0xf1, 0x65, 0xf0, 0x07, 0x87, 0x43, 0x29, + 0x87, 0x19, 0xeb, 0xc0, 0x2a, 0x3a, 0x1f, 0x74, 0x34, 0x1f, 0x31, 0xa5, 0xe9, 0x68, 0x6c, 0x01, + 0xef, 0x69, 0x26, 0x12, 0x96, 0x8f, 0xb8, 0xd0, 0x1d, 0x3d, 0x1b, 0x33, 0x65, 0xfe, 0xda, 0xd3, + 0xf7, 0x4b, 0xa7, 0x71, 0x3e, 0x1b, 0x6b, 0x59, 0x30, 0xc9, 0x81, 0x3d, 0x9e, 0x6b, 0x8f, 0x74, + 0x1c, 0xa7, 0x2c, 0x3e, 0x1b, 0xcb, 0x02, 0x39, 0xe9, 0x2e, 0x6f, 0x58, 0xf4, 0x1d, 0x87, 0x5e, + 0x9c, 0x70, 0x31, 0x04, 0x74, 0xa6, 0xc2, 0x33, 0x36, 0xb3, 0xb8, 0xbb, 0x2b, 0x71, 0x57, 0x28, + 0x5b, 0x0e, 0xca, 0xc6, 0x32, 0x4e, 0x2d, 0xca, 0x7d, 0x1b, 0x4c, 0xeb, 0x97, 0x75, 0x54, 0x3f, + 0x11, 0x09, 0x9b, 0xb2, 0xe4, 0x01, 0xa3, 0x09, 0xcb, 0xf1, 0x3e, 0x7a, 0x23, 0x4e, 0x29, 0x17, + 0x21, 0x4f, 0x7c, 0xaf, 0xe9, 0xb5, 0xb7, 0xc9, 0x16, 0xac, 0x4f, 0x12, 0x8c, 0x51, 0x35, 0xa5, + 0x2a, 0xf5, 0xd7, 0x9b, 0x5e, 0x7b, 0x87, 0xc0, 0x37, 0x7e, 0x07, 0x6d, 0xa6, 0x8c, 0x0f, 0x53, + 0xed, 0x57, 0x9a, 0x5e, 0xbb, 0x4a, 0xec, 0x0a, 0x7f, 0x8a, 0xaa, 0x85, 0x9b, 0x7e, 0xb5, 0xe9, + 0xb5, 0x6b, 0x47, 0x07, 0x81, 0xb1, 0x3a, 0x70, 0x56, 0x07, 0x7d, 0x67, 0x75, 0xaf, 0xfa, 0xe2, + 0x8f, 0x43, 0x8f, 0x00, 0x1a, 0x7f, 0x89, 0x76, 0xad, 0xe8, 0x30, 0x05, 0x39, 0xfe, 0x06, 0xc4, + 0xfb, 0xc1, 0xc2, 0xeb, 0xc0, 0xe4, 0xc0, 0xc8, 0x25, 0x75, 0x8b, 0xb7, 0xea, 0x6f, 0x23, 0xb7, + 0x11, 0xc2, 0x4b, 0xfd, 0x4d, 0x50, 0xb5, 0x63, 0x37, 0xef, 0x15, 0x7b, 0xf8, 0x0e, 0x7a, 0xd3, + 0x81, 0xf4, 0x34, 0x84, 0x27, 0x6d, 0xc1, 0x93, 0x5c, 0x6c, 0x7f, 0xfa, 0x80, 0xaa, 0xb4, 0xf5, + 0x10, 0x6d, 0xdc, 0x97, 0xf9, 0x99, 0xc2, 0x5f, 0xa1, 0x2d, 0x23, 0x47, 0xf9, 0x95, 0x66, 0xa5, + 0x5d, 0x3b, 0xfa, 0x20, 0x58, 0x55, 0x6a, 0xc1, 0x92, 0x9b, 0xc4, 0xc5, 0xb5, 0xfe, 0xf6, 0xd0, + 0xf6, 0x31, 0xf8, 0x28, 0x06, 0xf2, 0x55, 0x26, 0x7f, 0x8b, 0xea, 0x19, 0xd5, 0x4c, 0x69, 0xe7, + 0xc0, 0x3a, 0x38, 0x70, 0xed, 0x1b, 0x77, 0x4c, 0xb4, 0xf5, 0xa3, 0x87, 0xec, 0x3a, 0x1c, 0x14, + 0x2f, 0x81, 0x24, 0xd5, 0x8e, 0x0e, 0x57, 0x93, 0xc1, 0x83, 0x49, 0xcd, 0x04, 0x99, 0xd7, 0x7f, + 0x81, 0xf6, 0xe7, 0x8d, 0xc1, 0x12, 0x2b, 0x4b, 0x85, 0xb1, 0x3c, 0x17, 0x1a, 0xf2, 0x5b, 0x25, + 0xb7, 0x4a, 0x00, 0x73, 0xb3, 0x3a, 0x2e, 0x8e, 0x5b, 0xbf, 0x56, 0x10, 0xbe, 0xcf, 0x05, 0xcd, + 0xf8, 0x73, 0x96, 0x5c, 0xeb, 0xfd, 0x4f, 0xd0, 0xcd, 0x81, 0x0b, 0x08, 0x2d, 0x48, 0x0c, 0xa4, + 0xb5, 0xe1, 0xf6, 0x6a, 0xe5, 0x73, 0x76, 0x82, 0x07, 0x57, 0x6f, 0xfc, 0x1c, 0x21, 0x28, 0x08, + 0x43, 0x56, 0xb1, 0x55, 0xe9, 0xc8, 0xe6, 0x5d, 0x31, 0xe9, 0x06, 0x50, 0x23, 0x64, 0x1b, 0xb6, + 0x20, 0xf4, 0x3b, 0xb4, 0x9b, 0xd3, 0x67, 0xe1, 0xa2, 0xbf, 0x6c, 0x51, 0x2f, 0x52, 0xb2, 0xd4, + 0x8b, 0x05, 0x07, 0xa1, 0xcf, 0x8e, 0xe7, 0x7b, 0xa4, 0x9e, 0x97, 0x97, 0xf8, 0x09, 0xc2, 0x91, + 0x8e, 0x43, 0x75, 0x1e, 0x8d, 0xb8, 0x52, 0x5c, 0x8a, 0xa2, 0xbd, 0x6d, 0xa1, 0x2f, 0x38, 0x97, + 0x87, 0xc4, 0xa4, 0x1b, 0x9c, 0xce, 0xf1, 0x8f, 0xd8, 0x8c, 0xec, 0x45, 0x3a, 0x5e, 0xda, 0xc1, + 0xdf, 0xa0, 0x0d, 0x18, 0x3f, 0x50, 0xf2, 0xb5, 0xa3, 0xee, 0x6a, 0xa7, 0xbe, 0x2f, 0x60, 0x57, + 0xb3, 0x42, 0x4c, 0x7c, 0xeb, 0x1f, 0x0f, 0xed, 0x01, 0x04, 0x9c, 0x38, 0x65, 0x34, 0x63, 0x09, + 0x26, 0xa8, 0x3e, 0xa1, 0x19, 0x4f, 0xa8, 0x96, 0x79, 0xa8, 0x98, 0xf6, 0x3d, 0x68, 0x84, 0x8f, + 0x57, 0x7b, 0xf0, 0xd4, 0xc1, 0x7f, 0xe0, 0x3a, 0xed, 0x65, 0xaa, 0x50, 0xbd, 0x33, 0xe7, 0x38, + 0x65, 0x1a, 0xdf, 0x43, 0x7b, 0x70, 0x63, 0x58, 0xca, 0x8c, 0x49, 0xf3, 0xbb, 0xe5, 0x7e, 0x37, + 0xb3, 0xd5, 0xa8, 0x7e, 0x3c, 0x56, 0x64, 0x77, 0x3c, 0x17, 0x07, 0xf9, 0x79, 0x88, 0x6e, 0x94, + 0x69, 0x26, 0x34, 0x03, 0x81, 0x95, 0xd7, 0x33, 0xed, 0x2d, 0x98, 0x9e, 0xd2, 0xec, 0x94, 0xe9, + 0xd6, 0x5f, 0xeb, 0xe8, 0xd6, 0x0a, 0x7b, 0xf0, 0xd7, 0xe8, 0x2d, 0x73, 0x8f, 0x9e, 0x86, 0x5c, + 0x84, 0x51, 0x26, 0xe3, 0x33, 0x5b, 0x0a, 0xfb, 0x57, 0xe7, 0x53, 0x7f, 0x0a, 0x3c, 0x56, 0x6d, + 0x7f, 0x7a, 0x22, 0x7a, 0x45, 0x00, 0x7e, 0x84, 0xde, 0x36, 0x2c, 0xa6, 0x8f, 0x0a, 0x26, 0x33, + 0xa9, 0xfe, 0x67, 0xd2, 0x95, 0xf5, 0x12, 0x0c, 0x61, 0xa6, 0xbb, 0x4e, 0xec, 0x24, 0xfb, 0x11, + 0xe1, 0xf2, 0xd3, 0x15, 0xe4, 0xca, 0x16, 0xc0, 0x87, 0xaf, 0x29, 0x80, 0x52, 0x76, 0xcb, 0x46, + 0xd8, 0x7c, 0xff, 0xec, 0x64, 0x5a, 0xe6, 0xa2, 0xd4, 0xb4, 0x66, 0x89, 0xbf, 0x05, 0x79, 0xbf, + 0xbb, 0xba, 0x4e, 0xfb, 0x39, 0x15, 0x8a, 0xc6, 0x9a, 0x4b, 0x53, 0x55, 0x37, 0x4a, 0xdc, 0x8e, + 0xa5, 0xf7, 0xf8, 0xb7, 0x8b, 0x86, 0xf7, 0xf2, 0xa2, 0xe1, 0xfd, 0x79, 0xd1, 0xf0, 0x5e, 0x5c, + 0x36, 0xd6, 0x5e, 0x5e, 0x36, 0xd6, 0x7e, 0xbf, 0x6c, 0xac, 0xfd, 0xf4, 0xd9, 0x90, 0xeb, 0xf4, + 0x3c, 0x0a, 0x62, 0x39, 0xea, 0xd8, 0x3b, 0x60, 0x0a, 0xb8, 0x45, 0x67, 0xfa, 0x9f, 0x1f, 0x03, + 0x60, 0x77, 0xb4, 0x09, 0xff, 0x59, 0x3e, 0xf9, 0x37, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x27, 0xa0, + 0x60, 0x32, 0x08, 0x00, 0x00, } func (m *IndexedHeader) Marshal() (dAtA []byte, err error) { @@ -600,12 +620,12 @@ func (m *IndexedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.BabylonTxHash) i = encodeVarintZoneconcierge(dAtA, i, uint64(len(m.BabylonTxHash))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if m.BabylonEpoch != 0 { i = encodeVarintZoneconcierge(dAtA, i, uint64(m.BabylonEpoch)) i-- - dAtA[i] = 0x28 + dAtA[i] = 0x30 } if m.BabylonHeader != nil { { @@ -617,6 +637,16 @@ func (m *IndexedHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintZoneconcierge(dAtA, i, uint64(size)) } i-- + dAtA[i] = 0x2a + } + if m.Time != nil { + n2, err2 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.Time):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintZoneconcierge(dAtA, i, uint64(n2)) + i-- dAtA[i] = 0x22 } if m.Height != 0 { @@ -989,6 +1019,10 @@ func (m *IndexedHeader) Size() (n int) { if m.Height != 0 { n += 1 + sovZoneconcierge(uint64(m.Height)) } + if m.Time != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.Time) + n += 1 + l + sovZoneconcierge(uint64(l)) + } if m.BabylonHeader != nil { l = m.BabylonHeader.Size() n += 1 + l + sovZoneconcierge(uint64(l)) @@ -1246,6 +1280,42 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { } } case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowZoneconcierge + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthZoneconcierge + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthZoneconcierge + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Time == nil { + m.Time = new(time.Time) + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(m.Time, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BabylonHeader", wireType) } @@ -1281,7 +1351,7 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BabylonEpoch", wireType) } @@ -1300,7 +1370,7 @@ func (m *IndexedHeader) Unmarshal(dAtA []byte) error { break } } - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BabylonTxHash", wireType) }